This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Mathematical functions

Using standard mathematical functions in Punctual expressions.

    Punctual include a whole lot of mathematical functions that can be used in different way in our expressions.

    Range between two values

    between

    • between [min1,max1...] expr

    between allows you to specify one or more ranges and any other expression. It returns 1 if the expression is between the specified ranges and 0 if not.

    We’ve already seen some uses of between in other sections, for example, the drawing of a radius 1 circumference:

    between [fr-px, fr+px] 1 >> add;
    

    Between example 1

    Note that the expression fr==1 >> add; won’t yield any results. There isn’t any fragment that has a radius of exactly one. Due to the division of the screen in a finite number of fragments, and thus not being a continuous space, some fragments will have a radius very near to 1 (such as 1.0001 or 0.99998), but not exactly 1.

    These two expressions are equivalent:

    between [0.5,0.7] fy >> add;
    hline 0.6 0.1 >> add;
    

    Between example 2

    A 45º arc:

    fit 1 $ (between [0.4,0.42] fr) * (between [0, pi/4] ft) >> add;
    

    Between example 3

    A lot of concentric circles:

    r << fr%0.1;
    fit 1 $ between [r-2*px, r+2*px] 0.1 >> add;
    

    Between example 4

    In the next example, we use between to create a mask where only fragments whose green component is in a certain range are selected.

    A second mask is used to discard fragments with a lot of blue component.

    Then, these two masks filter the original image, so only the selected fragments are shown at full bright, and the others are dimmed.

    i << img "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/Half-timbered_mansion%2C_Zirkel%2C_East_view.jpg/1024px-Half-timbered_mansion%2C_Zirkel%2C_East_view.jpg";
    o << 0.3*osc 0.015;
    g<<between (o+[0.5,0.9]) $ rgbg i;
    m << rgbb i < 0.6;
    i*g*m >> add;
    i*0.2 >> add;
    

    Between example 5

    betweenp

    • betweenp [min1,max1...] expr: is the same than between, but if multiple limits and expressions are provided, they are combined in a pair-wise way, while with between they are combined in a combinatorial way.

    See the difference between these two expressions:

    between [0.2,0.3,-0.2,-0.3] [fy,fx] >> add;
    

    Between example 6

    betweenp [0.2,0.3,-0.2,-0.3] [fy,fx] >> add;
    

    Between example 7

    The first one creates four stripes, as it combines [0.2,0.3] with fy and fx, and [-0.2,-0.3] with fx and fy, while the second one creates only two stripes, combining [0.2, 0.3] with fy, and [-0.2, -0.3] with fx.

    Sign of an expression

    abs

    • abs graph returns the absolute value of the provided graph, that is the same graph but ignoring the sign. abs (-1) is 1 and abs 1 is 1.

    There are a lot of examples of the use of abs throughout this guide, as, alongside unipolar, is a fast way to covert a -1 to 1 range to a 0 to 1 one.

    abs fx >> add;
    

    Sign of an expression example 1

    abs [1-fx, spin (saw 0.03) $ fx+osc 0.04, abs $ osc 0.13] >> add;
    

    Sign of an expression example 2

    It is also useful to create symmetry:

    setfx (abs fx) $ spin [0.2] $ tile [1,12] $ hline 0 0.01 >> add;
    

    Sign of an expression example 3

    fit 1 $ setfxy (abs fxy) $ spin [0.2] $ tile [1,12] $ hline 0 0.01 >> add;
    

    Sign of an expression example 4

    sign

    • sign graph returns only the sign of the graph: 1 if it’s positive, -1 if it’s negative, 0 if it’s 0.

    In this example, we use sign as a threshold. For each color component, only fragments that have a certain amount of the component color are lit (but when they are, they are completely lit).

    i2 is used as mask, so the brightest fragments are kept black.

    Note that the same result could be achieved using a >0 comparison instead of sign.

    i << img "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/Half-timbered_mansion%2C_Zirkel%2C_East_view.jpg/1024px-Half-timbered_mansion%2C_Zirkel%2C_East_view.jpg";
    i2 << rgbv i < 0.7;
    r << sign $ rgbr i - (0.3~~0.7 $ osc 0.13);
    g << sign $ rgbg i - (0.3~~0.7 $ osc 0.15);
    b << sign $ rgbb i - (0.3~~0.7 $ osc 0.09);
    i2*[r, g, b] >> add;
    

    Sign of an expression example 5

    Here, we explore the same idea, but use the HSV color space, oversaturate some fragments, and apply a gradual palette change to the entire image.

    i << img "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/Half-timbered_mansion%2C_Zirkel%2C_East_view.jpg/1024px-Half-timbered_mansion%2C_Zirkel%2C_East_view.jpg";
    i2 << rgbv i < 0.6;
    s << sign $ rgbs i - (0.3~~0.7 $ osc 0.013);
    h << ((rgbh i)+(unipolar $ saw 0.01));
    v << rgbv i;
    hsvrgb $ i2*[h, s, v] >> add;
    

    Sign of an expression example 6

    Here, sign is applied to a bunch of oscillators. The resulting o variable represents a series of -1, 0, or 1, depending on the sign of each oscillator at a given time.

    When o is used inside move, we get a lot of moving lines, but that abruptly change their position whenever their associated oscillator changes its sign.

    l << spin [fr*8] $ spin [saw 0.16] $ tile [1,8] $ hline 0 0.01;
    o << sign $ osc [0.13,0.15..0.20];
    fit 1 $ mono $ move [o*osc 0.03] l >> add;
    0.9 * fb fxy >> add;
    

    Sign of an expression example 7

    Rounding numbers

    fract

    • fract returns the fractional part of numbers.

    Note that here “the fractional part” is defined as the difference between the number and the whole number immediately below it, so fract 2.3 is 0.3, and fract (-1.2) is 0.8 (-1.2 - (-2) = -1.2+2 = 0.8):

    l1 << vline (fract 2.3) 0.01;
    l2 << vline (fract (-1.2)) 0.01;
    [0,1,0]*l1 >> add;
    [1,0,0]*l2 >> add;
    

    Rounding numbers example 1

    Also note that fract x is equivalent to x%1 for any x.

    In this example, the image is divided in 100 vertical slices, and each slice is distorted. Within each slice, each fragment is vertically displaced based on its position within the slice. The displacement goes from no displacement on the left side to maximum displacement on the right side, resulting in a distortion effect across the image:

    i << img "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Peaceful_waterfall_%28Unsplash%29.jpg/1024px-Peaceful_waterfall_%28Unsplash%29.jpg";
    move [0,((-0.5) ~~ (-0.01) $ sin 0.03)*fract (fx*100)] $ i >> add;
    

    Rounding numbers example 2

    round, ceil, floor, trunc

    • round, ceil, floor and trunc are intimately related. All four convert a number to a near integer.

    round rounds the number to the nearest integer. round 3.2 is 3, round 3.7 is 4, round (-3.2) is -3 and round (-3.7) is -4. For a fractional part of exactly 0.5, the number is rounded to the next higher integer.

    ceil rounds the number to the next higher integer. ceil 3.2 is 4, ceil 3.7 is 4, ceil (-3.2) is -3 and ceil (-3.7) is -3.

    floor rounds the number to the next lower integer. floor 3.2 is 3, floor 3.7 is 3, floor (-3.2) is -4 and floor (-3.7) is -4.

    trunc truncates the number, in explain, it gets rid of the fractional part. trunc 3.2 is 3, trunc 3.7 is 3, trunc (-3.2) is -3 and trunc (-3.7) is -3.

    Amongst the useful applications of these functions, there is to apply a pixelation effect:

    p << spin 0.25 $ unipolar fx;
    setfxy [round (fxy*10)/10] p >> add;
    

    Rounding numbers example 3

    In this example, the original pattern p is pixelated, creating a grid of 10x10 squares, each one in a uniform color. The use of the other functions lead to slightly different results.

    Next example is a reimplementation of Hydra’s function posterize, applied to an image:

    i << img "https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Sonoma_chipmunk_at_Samuel_P._Taylor_State_Park.jpg/1280px-Sonoma_chipmunk_at_Samuel_P._Taylor_State_Park.jpg";
    bins << 3;
    gamma << 0.6;
    c2 << (floor $ i ** gamma * bins)/bins;
    c2 ** (1/gamma) >> add;
    

    Rounding numbers example 4

    Note how the same method as in the previous example is used (multiply by a number, rounding, and dividing by the same number), in this case to reduce to number of colors in the image.

    Exponentials, logarithms, and roots

    sqrt

    • sqrt <graph>: Calculates the square root of the provided graph. It yields the positive square root for each value in the graph.

    cbrt

    • cbrt <graph>: Computes the cube root of the provided graph. The cube root is the number that, when multiplied by itself three times, results in the original number. It returns both positive and negative cube roots.

    exp

    • exp <graph>: Computes the exponential function, which raises the mathematical constant e to the power of the values in the provided graph. The exponential function grows rapidly for positive values and approaches zero for negative values.

    log

    • log <graph>: Computes the natural logarithm (base e) of the values in the provided graph. It is the inverse operation of the exponential function. It is only defined for positive numbers and grows faster than the binary and decimal logarithms.

    log2

    • log2 <graph>: Calculates the binary logarithm (base 2) of the values in the provided graph. It represents the power to which the base (2) must be raised to produce the values in the graph. The binary logarithm grows slower than the natural logarithm but faster than the decimal logarithm.

    log10

    • log10 <graph>: Computes the decimal logarithm (base 10) of the values in the provided graph. It represents the power to which the base (10) must be raised to produce the values in the graph. The decimal logarithm grows slower than both the natural and binary logarithms.

    Let’s first see the graphs of these functions:

    fit 1 $ zoom 0.5 $ circle [fx, [sqrt fx, cbrt fx, exp fx]] 0.01 >> add;
    

    Exponentials, logarithms, and roots example 1

    fit 1 $ zoom 0.5 $ circle [fx, [log fx, log2 fx, log10 fx]] 0.01 >> add;
    

    Exponentials, logarithms, and roots example 2

    Exponentials, logarithms, and roots examples

    In the next example, we manipulate the graph of the square root function to create a symmetrical pattern reminiscent of wings.

    First step is to create a four-way symmetry of the original graph. This is done by taking the absolute value of x (so we have results for both positive and negative values), and then multiplying the square root by [-1, 1], so the graph is mirrored horizontally. The result is a four-way symmetry of the original graph:

    s << [-1,1]*(sqrt $ abs fx);
    mono $ zoom 0.5 $ circle [fx, s] 0.004 >> add;
    

    Exponentials, logarithms, and roots example 3

    To complete the example, an oscillator is added to the square root, so the graphs move up and down like wings. A second oscillator is added to curve the lines in response to the incoming audio signal.

    Finally, we give the result some color and add a zooming feedback effect, which increase the wings resemblance:

    o << unipolar $ saw 0.9;
    s <<  [-1,1]*(sqrt $ o*abs fx);
    g << mono $ zoom 0.5 $ circle [fx, s] 0.004;
    g*[fr,0.5, 0.5+abs fy]>> add;
    zoom 0.96 $ fb fxy >> add;
    

    Exponentials, logarithms, and roots example 4

    In this example, we use the cubic root function (cbrt) to manipulate the parameters of a circle pattern, resulting in a visually dynamic and intricate design.

    The core of the pattern is just a circle, but the coordinates that define its center are defined by the x and y variables, that vary with fr, fx and fy. Here is important to remember that circle does not necessary define a proper circle: a fragment (fx, fy) will be considered to be inside the circle if (x, y) is at a distance of the fragment of 0.8 or less.

    Let’s try first a simplified version:

    d << fr;
    x << d;
    y << d;
    move [0,-1] $ circle [x, y] 0.8 >> add;
    

    Exponentials, logarithms, and roots example 5

    This is just a distorted circle. Now, let’s add the different parts step by step:

    d << fr;
    x << d*(saw (0.1*fy));
    y << d;
    move [0,-1] $ circle [x, y] 0.8 >> add;
    

    Exponentials, logarithms, and roots example 6

    This is the main pattern. The circle moves horizontally, and the movement is controlled by fy, slicing the circle in multiple parts.

    d << fr;
    x << d*(saw (0.1*fy));
    y << d+(0.2*fract (fx*100));
    move [0,-1] $ circle [x, y] 0.8 >> add;
    

    Exponentials, logarithms, and roots example 7

    In this step, the circle is sliced vertically. Note, for example, that fract (fx*10) >> add would create a vertical stripe pattern.

    To create the final pattern, we first add cbrt to the d calculation. This has the effect of compressing the pattern vertically. Finally, we add color to the circle using the HSV color space, with hue and saturation modulated by fx and fy, respectively:

    d << cbrt fr;
    x << d*(saw (0.1*fy));
    y << d+(0.2*fract (fx*100));
    c << move [0,-1] $ circle [x, y] 0.8;
    co << hsvrgb [fx+saw 0.03,fy,0.8];
    c*co >> add;
    

    Exponentials, logarithms, and roots example 8

    In this example, we use the three logarithm variants to create variation in the movement of six circles.

    The pattern starts by defining a circle and applying a horizontal movement to it. As dx is a 3-channel signal, three circles are created. Then they are duplicated again, creating the six circles, by using spin with two oscillators. The modulo operator %1 is used to ensure that dx values are within the interval [0,1]. Because we are using three variants of the logarithm, the circles move in a similar but not identical way.

    The circles are colored in the HSV color space, with the hue oscillating and the saturation and value fixed. The result is then converted to RGBA by adding an alpha channel.

    Finally, feedback is applied to the image, and it is moved to the left to create a trail effect that increases the movement sensation.

    c << circle 0 0.08;
    et << etime;
    dx << [log et, log2 et, log10 et]%1;
    cs << mono $ spin (saw [0.2,-0.2]) $ move [dx, 0] c;
    cs*(hsvrgb [saw 0.9, 0.6, 0.6]++1) >> blend;
    move [-0.03,0] $ fb fxy >> add;
    

    Exponentials, logarithms, and roots example 9

    Trigonometric functions

    pi

    • pi: this is just the number pi, 3.14159…

    sin', cos, tan

    • Basic trigonometric functions: sin', cos, tan. These functions receive a graph in radians and return the sine, cosine, and tangent of that number, respectively. Note that the sine function is called sin' to avoid conflicts with the sin function, which is a (deprecated) synonym for osc.
    fit 1 $ zoom 0.5 $ circle [fx, [sin' fx, cos fx, tan fx]] 0.01 >> add;
    

    Trigonometric functions example 1

    asin, acos, atan

    • Inverse trigonometric functions: asin, acos, atan. These functions receive a number between -1 and 1 and return the corresponding angle in radians.
    fit 1 $ zoom 0.5 $ circle [fx, [asin fx, acos fx, atan fx]] 0.01 >> add;
    

    Trigonometric functions example 2

    sinh, cosh, tanh

    • Hyperbolic functions: sinh, cosh, tanh. These functions are the hyperbolic counterparts to the basic trigonometric functions. They are useful to create smooth curves and transitions.
    fit 1 $ zoom 0.5 $ circle [fx, [sinh fx, cosh fx, tanh fx]] 0.01 >> add;
    

    Trigonometric functions example 3

    asinh, acosh, atanh

    • Inverse hyperbolic functions: asinh, acosh, atanh. These functions are the hyperbolic counterparts to the inverse trigonometric functions.
    fit 1 $ zoom 0.5 $ circle [fx, [asinh fx, acosh fx, atanh fx]] 0.01 >> add;
    

    Trigonometric functions example 4

    Trigonometric functions examples

    These functions provide a range of mathematical tools to manipulate angles, curves, and transitions within your graphical patterns.

    In this example, sinusoidal waves are generated from an image.

    To assure a smooth transition, the image is first vertically reflected. The setfx function in the second line is used to mirror the image and put the two copies side by side.

    The tiled variable is created by tiling the reflected image, so the two copies are repeated horizontally. Note that the tile function is used with a 1 parameter, so there is no visible change at this point, but the image will now be repeated when moved.

    The next step is to create a wave that will be used to move the image. The wave is created by combining a sine wave with a cosine wave. This way, two copies of the wave are created, but displaced by 90 degrees. The ~~ operator is used to reduce the displacement that will be applied to the image.

    Finally, the image is moved by the wave, and the result is horizontally moved with a saw oscillator to create the actual waves.

    i << img "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Peaceful_waterfall_%28Unsplash%29.jpg/1024px-Peaceful_waterfall_%28Unsplash%29.jpg";
    reflected << setfx [fx-1, (-1)*fx-1] i;
    tiled << tile 1 reflected;
    wave << (-0.5) ~~ 0 $ [sin' (fx*10), cos (fx*10)];
    move [saw 0.02, wave] tiled >> add;
    

    Trigonometric functions example 5

    The next example utilize the tan function to curve a horizontal line. The tangent argument, t, is defined by the multiplication of two parts. The right part, (1-(abs fx)), goes from 0 on the left and right sides of the screen, to 1 on the center. The left part, (etime%5), goes from 0 to 5 and back to 0 every 5 seconds.

    After applying the tangent of t to the line’s y coordinate, the line starts horizontal and bends from the center upwards, reappearing from the bottom after reaching the top. This line is then colored red and tiled.

    The last bit of the example is the addition of the feedback in polar coordinates. Two copies are created, mirrored vertically, and then the result is duplicated again by rotating it 180 degrees.

    t << (etime%5)*(1-(abs fx));
    tile [8,4] $ [1,0,0]*(hline (tan t) 0.08) >> add;
    zoom 1.4 $ fit 1 $ spin ([0,-1]) $ mono $ fb [fr, [1,-1]*ft] >> add;
    

    Trigonometric functions example 6