Shape

Procedural Shapes

The code for our custom shape examples has so far been rather tedious. We have manually created shapes by typing line after line of vertex function calls, and this strategy will not work for more complex shapes. Also, given the title of the book, this approach might seem a bit anti-climatic. Now that we understand the basics of `beginShape()`, let us have a first look at how to procedurally draw custom shapes using a for loop, and the `sin()` and `cos()` functions.

Sine and Cosine

Over the years, I have seen many students struggle with Sine and Cosine. It is easy to understand why: These words seem rather scary and abstract, especially if you do not consider yourself good at math. However, this is both unfortunate and unnecessary. Unfortunate, because these two functions are a fundamental part of most programmatic designs, and a good understanding of them will allow you to solve many visual problems. Unnecessary, because they are not that hard to learn. Even if you do not understand everything presented in this chapter, you can get started by memorizing two almost identical lines of code.

Sine and Cosine allow us to find any position on the outline of an ellipse. They do this by converting an angle into an `x` position (Cosine) or a `y` position (Sine) for a circle with a 1 pixel radius. These values can then be multiplied by the radius of your actual circle to scale them up. Although it is not strictly necessary to understand how these functions work internally, here is a way to visualize what is going on: Imagine a right sided triangle connecting the center of the circle to the point on the outline. The Sine function is a quick way to get the ratio between the left side (hypotenuse) and the right side (opposite) of that triangle. The Cosine function is likewise the ratio between the hypotenuse and the bottom side (adjacent) of the triangle.

In P5, these functions are called `sin()` and `cos()`. As described above, they accept a single argument – an angle in radians – and return a value between `-1` and `1` representing the `x` or `y` position on a tiny circle. The two lines below demonstrate how to get these values and multiply them by the radius of your actual circle. Memorize these two lines, as they are very important.

``````var x = cos(RADIANS) * RADIUS;

To put this into context, here is an example where we use the same code to draw a small circle 330 degrees along the outline of a bigger circle.

``````translate(width/2, height/2);

noFill();
var radius = width * 0.3;

fill(30);
ellipse(x, y, 20, 20);
``````

If you consider all the basic shapes – as well as many complex shapes – they are characterized by having non-overlapping outlines that move around a center point. Some shapes, like the triangle, have just a few vertices, while others – like the ellipse – have many vertices. The `sin()` and `cos()` functions give a way to procedurally draw these types of shapes.

The For Loop

Although we will dedicate an entire part of this book to repetition, let us briefly go over the basic functionality of a `for` loop. A `for` loop allows us to execute code multiple times in a row by incrementing (or decrementing) a variable – often called `i` – until an expression is no longer true and the loop stops. In the following example, we initialize a variable with the number `0`, iterate as long as our variable is lower than `10`, and increment our variable by one between each iteration. The result is a loop that iterates ten times with our variable incrementing from zero to nine, drawing ten rectangles on the screen.

``````for(var i = 0; i < 10; i++) {
rect(0, 0, 100, 100);
}
``````

Unfortunately, all these rectangles have identical positions and sizes because we are passing the same static numbers to the `rect()` function over and over again. This is where `i` comes into play: Because it changes between each iteration of the loop, it can be used to create variance between each rectangle. The example below uses `i` to position the ten rectangles one pixel apart on the x-axis.

``````for(var i = 0; i < 10; i++) {
rect(i, 0, 100, 100);
}
``````

Although it might not be immediately clear, this is an important technique when drawing procedural designs. Because `i` increments by one between each iteration, it can be used as a scalar to distribute shapes across the canvas. For example, if we want to position the rectangles next to each other, we can multiply `i` with a number greater than the width of the rectangles.

``````for(var i = 0; i < 10; i++) {
rect(i * 105, 0, 100, 100);
}
``````

We can use this same technique to draw custom shapes. Instead of drawing individual shapes in the loop, we use the for loop to procedurally add vertices between the `beginShape()` and `endShape()` function calls. In the example below, we use this technique to draw ten random vertices in the center of the canvas.

``````translate(width/2, height/2);
beginShape();
for(var i = 0; i < 10; i++) {
var x = random(-100, 100);
var y = random(-100, 100);
vertex(x, y);
}
endShape();
``````

The result is certainly a procedural shape, but the use of `random()` does not give us a lot of control over the placement of the vertices: The shape is just a bunch of lines randomly crossing each other. The final step is to put our two techniques together and generate shapes with `sin()` and `cos()` inside of a `for` loop.

Putting it together

Starting from our random shape code above, let us replace the random vertices with vertices placed sequentially along the outline of an ellipse. We do this by using the same two lines that we memorized earlier, but instead of passing the same angle to `sin()` and `cos()`, we calculate a different angle on every iteration by multiplying `i` with the angle we want between the vertices. The result is a shape with ten vertices evenly spread around the center of the canvas.

``````translate(width/2, height/2);
beginShape();
for(var i = 0; i < 10; i++) {
var x = cos(radians(i * 36)) * 100;
var y = sin(radians(i * 36)) * 100;
vertex(x, y);
}
endShape();
``````

By changing the number of iterations and the spacing between the vertices, you can draw all of the basic shapes. The code below adds a few variables on top of the sketch to automatically calculate the spacing based on the number of vertices. Change the `numVertices` variable and another shape will appear.

``````var numVertices = 3; // or 4 or 30
var spacing = 360 / numVertices;
translate(width/2, height/2);
beginShape();
for(var i = 0; i < 10; i++) {
var x = cos(radians(i * spacing)) * 100;
var y = sin(radians(i * spacing)) * 100;
vertex(x, y);
}
endShape();
``````

‘Great, we have reinvented the basic shape functions’ you might say. Actually, this technique allows us to draw much more sophisticated shapes. Let's run through a few examples that all use the same `sin()` and `cos()` formula to draw different types of shapes. We'll start with the squiggly circle below that has a random radius for each vertex, making it look like it was drawn by hand.

``````translate(width/2, height/2);

beginShape();
for(var i = 0; i < 100; i++) {
``````

Change the radius for every vertex

``````  var radius = 100 + random(5);
``````
``````  var x = cos(radians(i * 3.6)) * radius;
vertex(x, y);
}
endShape();
``````

The star below is created by alternating between a low and a high radius for each vertex. It's easy to tweak the style of the star by using different numbers or more vertices, or using `rotate()` to change the orientation of the star.

``````translate(width/2, height/2);

``````

Set the initial radius to 100

``````var radius = 100;
``````
``````
beginShape();
for(var i = 0; i < 10; i++) {

``````

Use the radius in the cos/sin formula

``````  var x = cos(radians(i * 36)) * radius;
``````
``````  vertex(x, y);

``````

Change the radius for the next vertex

``````  if(radius == 100) {
} else {
}
``````
``````}
endShape();
``````

Here is a flower created with `quadraticVertex()` where all vertices and control points are positioned using `sin()` and `cos()`. By using a larger radius for the control points (the inverse of the star example above), the curves go outwards. When using Bézier curves, remember to start the shape with a `vertex()` function call. We do this by checking the value of `i` within the loop.

Automatically calculate the spacing

``````var numVertices = 7;
var spacing = 360 / numVertices;
``````
``````
beginShape();
``````

Loop one extra time to close shape with a curved line.

``````for(var i = 0; i < numVertices+1; i++) {
``````
``````
``````

Find the position for the vertex

``````  var angle = i * spacing;
var x = cos(radians(angle)) * 100;
var y = sin(radians(angle)) * 100;
``````
``````
if(i == 0) {
``````

If this is the first run of the loop, create simple vertex.

``````    vertex(x, y);
``````
``````  }
else {
``````

Otherwise create a quadratic Bézier vertex with a control point halfway in between the points and with a higher radius.

``````    var cAngle = angle - spacing/2;
var cX = cos(radians(cAngle)) * 180;
var cY = sin(radians(cAngle)) * 180;
``````
``````  }
}
endShape();
``````

You will often find yourself needing to use just one of the circular functions. The two shapes below are created just like that: The first one uses `sin()` and the second one uses `cos()` (as demonstrated in the code below).

``````strokeWeight(20);
strokeCap(SQUARE);
translate((width/2) - 200, height/2);
beginShape();
for(var i = 0; i < 200; i++) {
``````

2 pixel spacing on the x-axis.

``````  var x = i * 2;
``````

200 pixel high waveform on the y-axis.

``````  var y = cos(i * radians(2)) * 100;
``````
``````  vertex(x, y);
}
endShape();
``````

Sine and Cosine can be used to create a range of different shapes during the design process. In this design by Josef Müller-Brockmann, a series of exponentially growing arcs are rotated around the bottom left of the canvas.

Sediment Mars is a series of generative poster designs by Sarah Hallacher and Alessandra Villaamil. The `sin()` and `cos()` functions are used to generate an elliptical shape, which is then distorted by adding random values to it.

The project Generative Play is a card game by Adria Navarro that uses procedural drawing to create an infinite amount of generative characters. The character bodies are created using `sin()` and `cos()`.

Try to draw all the basic shapes using the techniques presented in this chapter. Then continue to generate other types of shapes. Can you use `random()` to manipulate the shape outline? Can you use Bézier curves instead of simple vertices?