## Basic transformations

Punctual have two sets of functions for making geometric transformations. The first set are functions that are meant for a specific transformation, while the second set are functions that allow to specify any formula. In all cases, the result is achieved by remapping `fx`

and `fy`

.

In the first set, there are four functions:

`tile`

`tile [x,y] ...`

: repeats the pattern`x`

times across the x axis and`y`

times across the y axis. If only one number is specified, it’s applied to both axis. If a negative number is specified, the corresponding coordinate is flipped.

```
tile [8,4] $ iline [0.3,0.5] [-0.6,-0.8] 0.02 >> add;
```

Note how the visible pattern in the screen is rescaled and repeated: lines get thiner and are limited to the length of the original pattern.

`move`

`move [x,y] ...`

: translates the pattern by the specified amount in each axis. If only one number is specified, it’s applied to both axis.

```
move [0.3, -0.4] $ circle 0 0.1 >> add;
```

`spin`

`spin amount ...`

: rotates the pattern by the specified amount. Here, 1 is half a revolution, and`spin`

amount acts in a clockwise direction (in opposition to degrees or radiants), hence 0.5 is -90º, -0.25 is 45º and so on.

```
spin (-0.25) $ line 0 [1,0] 0.001 >> add;
```

To convert from radiants (`α`

) to the unit used by `spin`

(`s`

), use the formula: `s=-α/π`

. The reverse is `α=-s*π`

.

`zoom`

`zoom [x,y] ...`

: zooms in by`x`

across the x axis and by`y`

across the y axis. If only one number is specified, it applied to both axis. Numbers greater than 1 make the pattern bigger (like looking from a shorter distance), and less than 1 make the pattern smaller (like if we were moving away from the screen). If a negative number is specified, the corresponding coordinate is flipped.

```
zoom 0.2 $ circle 0 0.1 >> add;
```

### Basic transformations examples

Things get interesting when applying some of these ideas to the transformations:

```
spin (0.8*osc 0.013*osc 0.15) $ tile [3 ~~ 8 $ osc 0.13] $ circle [0,0.5] (unipolar $ tri 2) >> add;
0.9 * fb fxy >> add;
```

```
(fit 1 $ mono $ zoom [0.8, 0.8, 1.4] $ move [0.2,0.2,0.1] $ spin (saw [0.03,-0.02,0.04]) $ tile [0,8] $ hline 0 0.03) * [0.8, 0.4, 0] >> add;
```

- Transformations that are different for each fragment by using some of the fragment coordinates.

```
fit 1 $ mono $ spin (saw [-0.06,0.034]) $ spin (6*ft/(2*pi)) $ tile [6+15*(unipolar $ osc 0.07),1] $ vline 0 0.03 >> add;
```

```
l << line 0 [1,0] 0.01 >> add;
spin ft l >> red;
```

This example deserves an explanation. How is it possible that the white line turns into 4 lines when applying `spin ft`

?

When we say that `spin`

(or any other transformation) rotates the specified graph we are making a simplification. In reality, each fragment computes its own result. Here, for each fragment, the inverse rotation is computed using its own `ft`

, and the ones that after being rotated get near enough to the line are painted red.

Note that when using a fixed number (like `spin 0.3`

), both ways of thinking lead to the same result. It’s the same to say a line is rotated by 0.3, than saying that all fragments that when rotated by -0.3 are in the line are white. But the latter one is far more confusing and difficult to say.

So, let’s try to explain the 4 red lines. We need to find for which `ft`

, when applying a rotation of `-ft`

the result is 0. Note that `ft`

is computed in radiants, but `spin`

uses its own unit, explained above.

The answer is that this will happen for any `ft`

that’s equal in radiants than in the spin unit. Note that the obvious answer is `ft=0`

(because 0 is the solution of the `ft=-ft/pi`

equation). But there are other possibilities, because adding any multiple of 2 to the spin is the same angle, and it’s valid as long the result is the range -π to π.

Take for example `ft=-ft/pi+2`

. Group the `ft`

: `ft+ft/pi=2`

; multiply by π and take the common factor: `ft*(pi+y1)=2*pi`

; finally, isolate `ft`

: `ft=2*pi/(pi+1)`

. Then, `ft ~= 1.517`

. You can check that `spin 1.517 $ line 0 [1,0] 0.01 >> add`

is the same as one of the four red lines.

Following the same argument, the other three angles are the solution of `ft=-ft/pi-2`

(-1.517), `ft=-ft/pi+4`

(3.034), and `ft=-ft/pi-4`

(-3.034).

The next examples combine some of the previous ideas:

```
(fit 1 $ mono $ spin [fr*8, (-1)*fr*8] $ hline (tri $ 0.35 / [5,7,9]) 0.1) * [fr/3,0,unipolar $ osc 0.05] >> add;
```

In this first example, we start with three horizontal lines that move vertically at different speeds. By using `spin`

with `fr`

, we bend the lines, and, as we used a two-channel graph inside spin, the result are 6 curves that moves by groups of three in a symmetrical way. Lastly, we apply color: first we crunch all channels together with `mono`

and then multiply the result by the color. Note how the red component depends on the fragment’s radius, and the blue component varies through time.

```
c << move [tri [-0.13, 0.15], tri [-0.15, 0.16]] $ circle 0 1.2 - circle 0 1;
fit 1 $ spin (fr*0.3) $ spin (ft/pi) $ move [osc fxy * 0.05, osc fxy * 0.09] $ c >> add;
```

Here, we start by building a circumference by subtracting two circles. Then we provide two oscillators to `move`

, with two coordinates each one, and so we have four circles moving around the screen. On the second `move`

, we use oscillators which frequency have two numbers (because `fxy`

is `[fx,fy]`

). That means that each fragments is duplicated and moved differently depending on its coordinates, and this creates the blurring effect. Then, `spin`

is applied twice, using `ft`

and `fr`

. The first `spin`

## Remaps

The set of functions `setfx`

, `setfy`

and `setfxy`

allows to transform one or both coordinates using any expression. They are a bit more complex to use than the basic transformations, but very flexible.

`setfx`

`setfx [x...] graph`

`setfx`

remaps the x coordinate of a graph by the given formula.

With simple examples, `setfx`

is equivalent to `move`

. These two lines are equivalent:

```
move [0.2,0] $ vline 0 0.01 >> red;
setfx (fx-0.2) $ vline 0 0.01 >> green;
```

Moving the line by 0.2 to the right is equivalent than taking each fragment and painting it in the color found at 0.2 to the left.

But these remapping functions are a lot more flexible. In the next example, each fragment x coordinate is multiplied by a number between -1 and 1 that depends on its distance to the origin. This operation completely deforms the image creating an interesting pattern:

```
l << tile [8,1] $ vline 0 0.05;
setfx (fx*sin' (fr*10)) l >> add;
```

The formula to calculate x don’t even need to include the original x coordinate. This is the same example than before, switching `fx`

by `fy`

:

```
l << tile [8,1] $ vline 0 0.05;
setfx (fy*sin' (fr*10)) l >> add;
```

If there is more than one element in the list, each one creates different channels, so the resulting graph has as many channels as the product of the number of channels in graph by the number of channels in the list.

In this example, the final graph has a total of 6 channels (3*2) that are mapped to a 4-channel output:

```
c << tile [8,4] $ circle 0 0.8 * [0.8, 0, 0.5];
setfx [fx+fx%0.1, fx*sin' (fy*8)] c >> blend;
```

`setfy`

`setfy [y...] graph`

`setfy`

is exactly the same function than `setfx`

, but it modifies the y coordinate instead of the x one.

Here, `setfy`

is used to evolve a color pattern, creating a symmetric repetition across the vertical axis:

```
c << [unipolar fx, fr*0.4, 1-abs fx, 0.8];
setfy [sin' (fy*10)] c >> blend;
```

```
c << fit 1 $ between [0.5+2*px, 0.5-2*px] fr;
mono $ setfy (fy+[0.1,0.2]*osc (fx*0.2)*(sin' $ fx*20)) c >> add;
```

`setfxy`

`setfxy [x,y...] graph`

`setfxy`

remaps both the x and the y coordinates of a graph. `move`

, `spin`

, `zoom`

and `tile`

can be rewritten using only `setfxy`

, but `setfxy`

is more flexible, allowing the use of any other mathematical formula.

In this example, each fragment is slightly displaced, and the displacement varies through time at a speed that depends on the fragment’s coordinates. This results in the blurring of the circle outline:

```
setfxy (fxy +: (osc fxy * 0.05)) $ circle 0 0.5 >> add;
```

This code is equivalent to `move (osc fxy * (-0.05)) $ circle 0 0.5 >> add;`

.

In the following example, a circle `c`

is defined with coordinates `(x, y)`

and radius `r`

. However, `x`

, `y`

and `r`

are determined using distances from moving points, creating a highly distorted and dynamic shape.

To grasp this concept better, let’s start with a simplified code:

```
x << dist [0.2,0.2];
circle [x,0] 0.2 >> add;
```

Here, for each fragment `(fx, fy)`

, the distance `x`

between the fragment and `(0.2, 0.2)`

is computed. Then, the fragment is painted white if the distance between the fragment and the point `(x, 0)`

is less than 0.2.

In the final example, `x`

is calculated in this manner but with a moving point. `y`

is similar, but it uses the “proximity” instead of distance. Additionally, `r`

is also dynamic and computed similarly to `x`

.

The computed values of `x`

, `y`

, or `r`

for each fragment can be visualized by sending them to the output.

The resulting circle can be visualized with this code:

```
x << dist $ osc [0.3, 0.2];
y << prox $ osc [0.13, 0.1];
r << dist $ osc [0.11, 0.19];
circle [x, y] r >> add;
```

The final shape is built by applying the transformation `setfxy (abs fxy)`

to the previous circle.

To understand the effect of this transformation, let’s study this simplified code:

```
c << circle [0.5,0.2] 0.02;
setfxy (abs fxy) c >> add;
```

Here, the absolute value of `fx`

and `fy`

is computed. If the result is near enough to `(0.5, 0.2)`

, the fragment is white; otherwise, it is black. This results in a circle in the first quadrant (both coordinates positive) being duplicated in each of the other quadrants. Note that if the circle has any negative coordinate, the result is a black screen, as there exists no number that results in a negative value when applying `abs`

.

With this analysis, we can now understand that this `setfx`

transformation is creating a four-fold symmetry from the content in the first quadrant.

The rest of the example involves details. `fit 1`

is applied to the resulting shape in order to make fragments square, and `mono`

is used to put the four copies of the shape in the same channel.

Finally, some color is applied to the shape before sending it to the output, and feedback is used to create the impression that the shape is moving smoothly:

```
x << dist $ osc [0.3, 0.2];
y << prox $ osc [0.13, 0.1];
r << dist $ osc [0.11, 0.19];
c << circle [x, y] r;
shape << mono $ fit 1 $ setfxy (abs fxy) c;
color << [0, fr/2, 0.5, 0.7];
shape * color >> blend;
gate 0.1 $ 0.98 * fb fxy >> add;
```

In the following example, `setfxy`

is employed twice: first to establish a horizontal symmetry and then to transform the pattern into polar coordinates.

The pattern begins by defining `x`

and `y`

as two rotating gradients from black to white.

These gradients `x`

and `y`

are combined to create a more complex color pattern in `co`

. This pattern is then transformed into `sh1`

by applying horizontal symmetry and further converted into polar coordinates, stored in `sh2`

. `sh3`

represents a version of `sh2`

that is moved and rotated.

Finally, the color in `sh3`

is transformed into the HSV colorspace and sent to the output, with the hue interpreted as red, the saturation as green, and the value as blue, resulting in the final color pattern.

```
x << unipolar $ fit 1 $ spin (saw (-0.013)) fx;
y << unipolar $ fit 1 $ spin (saw 0.01) fy;
co << 10*([x,y,x+y]%0.08);
sh1 << setfxy [fx, abs fy] co;
sh2 << setfxy frt sh1;
sh3 << move [0,0.45] $ spin (-0.5) sh2;
hsvrgb sh3 >> add;
```

In the following pattern, `setfxy`

is utilized to convert to polar coordinates, followed by a simple geometric transformation, and then another application of `setfxy`

to revert to Cartesian coordinates.

The initial segment of the pattern builds upon one of the examples seen in 10 ways of drawing a circumference of radius 1. The key variation here is the addition of a value to the radius at each point on the circumference. This value corresponds to the intensity of the frequency that aligns with the angle of the point, as defined by the formula in `a`

.

In `a`

, the possible angle range is mapped to `[0, 0.7]`

, representing audible frequencies except the highest. `abs ft`

is employed to create symmetry between the upper and bottom halves of the circumference. Finally, the `** 3`

operation cubes the result, reducing the amount added to the radius.

The subsequent segment of the pattern takes this result, translates it to polar coordinates, duplicates it using `tile`

, and then reverts the result back into Cartesian coordinates. This technique enhances simple transformations when applied within polar coordinates.

```
a << (linlin [0,pi] [0,0.7] $ abs ft) ** 3;
b << 1+ifft a;
c << between [b-px,b+px] fr;
d << tile [8,4] $ setfxy frt c;
setfxy (rtxy fxy) d >> add;
0.9*fb fxy >> add;
```

The last example expands upon the previous one, retaining the initial setup with minor adjustments to the circumference stroke width. Afterward, the circumference is translated into polar coordinates, and color is applied.

The `tile`

operation undergoes slight modifications, introducing irregularity by incorporating `fx`

into the calculation determining the number of fragment repetitions. Additionally, a new basic transformation, a constant velocity `spin`

, is introduced.

Further irregularity is introduced through a `move`

operation, with one of its values determined by an oscillator driven by the audio frequency. Visualizing `f`

reveals a series of curved lines that move dynamically across the screen in response to the music.

The subsequent steps involve translating the result back from polar coordinates to Cartesian coordinates and applying a substantial amount of feedback.

```
a << (linlin [0,pi] [0,0.7] $ abs ft) ** 3;
b << 1+ifft a;
c << between [b-4*px,b+4*px] fr;
d << (setfxy frt c)*hsvrgb [fr,fx,fy];
e << spin (saw 0.1) $ tile [1+3*fx,4] d;
f << move [saw imid, saw 0.035] e;
setfxy (rtxy fxy) f >> add;
0.99*fb fxy >> add;
```