# Casey Reas' Process Compendium

In this tutorial we're going to explore the use of object-oriented programming by implementing the elements defined by Casey Reas in his Process Compendium. The Process Compendium is based around a series of text descriptions of processes that are defined in terms of elements, which are dynamic shapes comprised of forms with one or more behaviours. Reas' approach to generative art builds on the tradition of conceptual artists like Sol LeWitt, who's Wall Drawings consisted of a series of instructions that skilled technicians are required to follow in order to produce the artwork in a gallery. The Process Compendium contains the following library of forms, behaviours and elements.

## Library

The library defines two types of forms, circles and lines. In this tutorial we will concentrate on Form 1, the circle. Despite working with the Process Compendium for 7 years (2004-2010), Casey Reas never found it necessary to add a third shape, e.g., square or triangle, because the first two forms combined with a range of behaviours are sufficient to produce a wide range of processes.

Form Description
F1 Circle
F2 Line

The library defines seven simple types of behaviours that can be combined in different ways to produce the more complex behaviours described in processes. Again, notice that this small set of simple behaviours is sufficient to define a wide range of processes.

Behaviour Description
B1 Move in a straight line
B2 Constrain to surface
B3 Change direction while touching another Element
B4 Move away from an overlapping Element
B5 Enter from the opposite edge after moving off the surface
B6 Orient toward the direction of an Element that is touching
B7 Deviate from the current direction

In Casey Reas' system, elements are the building blocks of process descriptions, they combine a form with one or more behaviours. These elements are used in processes as autonomous units.

Element Description
E1 F1 + B1 + B2 + B3 + B4
E2 F1 + B1 + B5
E3 F2 + B1 + B3 + B5
E4 F1 + B3 + B5
E5 F2 + B5 + B6 + B7

## Processes

Processes are defined in terms of elements and drawing instructions based on the elements. For example, Process 4 is defined using Element 1, i.e., circles (Form 1) that move in a straight line (Behaviour 1), are constrained to the surface (Behaviour 2), change direction while touching another (Behaviour 3), and move away from over overlapping elements (Behaviour 4).

 `Process 4: A rectangular surface filled with varying sizes of Element 1. Draw a line from the centers of Elements that are touching. Set the value of the shortest possible line to black and the longest to white, with varying grays representing values in between.`

In this tutorial, we will develop the necessary code to implement elements based on Form 1.

# Form 1: Circle

To begin let's look at a simple circle class. A circle has a position (x and y) and a radius. The constructor for the `Circle` class takes initial values for `x`, `y` and `radius` and copies them into the object. The `Circle` class defines a single function `draw()`, which draws the circle at it's current location.

``````class Circle {
float x;
float y;
float radius;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
}

void draw() {
pushStyle(); // Remember current drawing style
noFill(); // Remove fill for circle
stroke(0); // Set the stroke colour to black
strokeWeight(1); // Set the stroke weight to 1 pixel
pushMatrix(); // Remember current drawing transformation
translate(x, y); // Translate to the position of the circle
ellipseMode(RADIUS); // Set the ellipse drawing mode using radius
ellipse(0, 0, radius, radius); // Draw the circle
popMatrix(); // Restore previous drawing transformation
popStyle(); // Restore previous drawing style
}
}``````

The `draw()` function uses `pushStyle()` and `popStyle()` to remember and restore the drawing style for the circle. It also uses `pushMatrix()` and `popMatrix()` to remember and restore the drawing transformation for the circle. This is a useful way to remember and restore the complete drawing state in a sketch so that each object can draw itself without interfering with other objects needing to draw to the same display. We can test the circle class with a very simple sketch, like this:

• Show Sketch
``````/** @peep sketchcode */

Circle circle; // A variable to hold an instance of the Circle class

void setup() {
size(300, 300);
frameRate(10);
smooth();
// Construct an instance of the Circle class at the centre of the sketch
circle = new Circle(width/2, height/2, width/4);
}

void draw() {
background(255);
circle.draw(); // Have the circle draw itself on screen
}

class Circle {
float x;
float y;
float radius;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
}

void draw() {
pushStyle(); // Remember current drawing style
noFill(); // Remove fill for circle
stroke(0); // Set the stroke colour to black
strokeWeight(1); // Set the stroke weight to 1 pixel
pushMatrix(); // Remember current drawing transformation
translate(x, y); // Translate to the position of the circle
ellipseMode(RADIUS); // Set the ellipse drawing mode using radius
ellipse(0, 0, radius, radius); // Draw the circle
popMatrix(); // Restore previous drawing transformation
popStyle(); // Restore previous drawing style
}
}``````

This code constructs a single instance of the `Circle` class and simply gets it to draw itself on screen. Of course, it doesn't look very exciting just yet! As we saw in the previous tutorial, we can produce many instances of a class and store them in an array, just like we can with other data types. Here is a simple example of doing that using the `Circle` class. It produces 100 circles and stores them in an array using a `for` loop. The `draw()` function simply has to use another `for` loop to go through all of the circles in the array and request that each one draw itself.

• Show Code
``````/** @peep sketch **/

int NUM_CIRCLES = 100;
float MIN_RADIUS = 10;
float MAX_RADIUS = 20;

Circle[] circles;

void setup() {
size(300, 300);
frameRate(10);
smooth();
circles = new Circle[NUM_CIRCLES];
for (int i = 0; i < circles.length; i++) {
circles[i] = new Circle(random(width), random(height), random(MIN_RADIUS, MAX_RADIUS));
}
}

void draw() {
background(255);
for (int i = 0; i < circles.length; i++) {
circles[i].draw();
}
}

class Circle {
float x;
float y;
float radius;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
}

void draw() {
pushStyle(); // Remember current drawing style
noFill(); // Remove fill for circle
stroke(0); // Set the stroke colour to black
strokeWeight(1); // Set the stroke weight to 1 pixel
pushMatrix(); // Remember current drawing transformation
translate(x, y); // Translate to the position of the circle
ellipseMode(RADIUS); // Set the ellipse drawing mode using radius
ellipse(0, 0, radius, radius); // Draw the circle
popMatrix(); // Restore previous drawing transformation
popStyle(); // Restore previous drawing style
}
}``````

## Updates

Now that we have a class to represent and draw a static form, we can build on this to support behaviours. The simplest way to do this is to add a function to the Circle class, which updates the current position and/or radius of the circle. We will call the function `update()` and for the moment, we will simply add a small random value to the `x` and `y` values, like this:

``````class Circle {

// Other circle related stuff here...

void update() {
x += random(-1, 1);
y += random(-1, 1);
}
}``````

To make use of this new function, we need to make sure that we call it from the main program. The cleanest way to implement this is to have an `update()` function in the main program as well and call this from the `draw()` function every time the display needs to be updated. Here's the code for the `update()` function:

``````void update() {
for (int i = 0; i < circles.length; i++) {
circles[i].update();
}
}``````

This simply uses another `for` loop to call `update()` on all of the circles. Using this approach cleanly separates the updating of circles from the drawing of them, which makes it easier to organise our code. The `draw()` function simply calls the `update()` function before it begins drawing, to ensure that all of the circles are updated once per frame:

``````void draw() {
update(); // Update the circles first
background(255);
for (int i = 0; i < circles.length; i++) {
circles[i].draw();
}
}``````

Putting this together, we produce a sketch that displays a jostling collection of circles. (Note: The circles will tend to drift off the screen over time, simply restart the sketch to see how it looks at the start.)

• Show Code
``````/** @peep sketch **/

int NUM_CIRCLES = 100;
float MIN_RADIUS = 10;
float MAX_RADIUS = 20;

Circle[] circles;

void setup() {
size(300, 300);
frameRate(10);
smooth();
circles = new Circle[NUM_CIRCLES];
for (int i = 0; i < circles.length; i++) {
circles[i] = new Circle(random(width), random(height), random(MIN_RADIUS, MAX_RADIUS));
}
}

void update() {
for (int i = 0; i < circles.length; i++) {
circles[i].update();
}
}

void draw() {
update();
background(255);
for (int i = 0; i < circles.length; i++) {
circles[i].draw();
}
}

class Circle {
float x;
float y;
float radius;

float heading;
float speed;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
heading = random(TWO_PI);
speed = 1;
}

void update() {
x += random(-1, 1);
y += random(-1, 1);
}

void draw() {
pushStyle();
noFill();
stroke(0);
strokeWeight(1);
ellipseMode(RADIUS);
pushMatrix();
translate(x, y);
ellipse(0, 0, radius, radius);
popMatrix();
popStyle();
}
}``````

Change the code above to produce a sketch where the circles also change their radius. (Hint: you only need to add one line of code to the `Circle` class.)

# Behaviour 1: Move in a straight line

Now that we've got a circle that can be updated we can begin to support behaviours. Behaviour 1 calls for the ability for forms to move in a straight line. To implement this we will add two new variables (a.k.a. fields) to our `Circle` class to determine the direction (heading) and speed that the circle moves in. Note: in an alternative implementation we could have added variables to store the movement in x and y, e.g., `dx` and `dy` but while this is simple to implement at first it is more complex to use in the long run as should become apparent later.

Here is the new `Circle` class it now contains fields that control position (`x`, `y`), size (`radius`) and movement (`heading`, `speed`). This code uses radians to store the heading but, if you prefer, you can use degrees in your own code - just remember to wrap the variable in a call to `radians()` whenever you want to pass it to a function that takes an angle as a argument, e.g., `sin()` and `cos()`. The initial value for `heading` is chosen at random in the constructor for the Circle class and `speed` is set to a default value of 1.

``````class Circle {
float x;
float y;
float radius;
float heading;
float speed;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
heading = random(TWO_PI);
speed = 1;
}

void update() {
x += random(-1, 1);
y += random(-1, 1);
}

void draw() {
pushStyle();
noFill();
stroke(0);
strokeWeight(1);
ellipseMode(RADIUS);
pushMatrix();
translate(x, y);
rotate(heading); // Rotate to the direction of the heading
ellipse(0, 0, radius, radius);
line(0, 0, radius, 0); // Draw a line in the direction of the heading
popMatrix();
popStyle();
}
}``````

The `draw()` function has been altered to indicate the heading of circle with a line, to achieve this the transformation of the shape has been altered to rotate the drawing so that the x-axis points along the direction of the heading, that way a line drawn along the x-axis indicates the direction that the circle is facing. If we use this new `Circle` class in our previous sketch, we can see that the jostling circles all now have a randomly chosen heading indicated by a radial line.

• Show Code
``````/** @peep sketch **/

int NUM_CIRCLES = 100;
float MIN_RADIUS = 10;
float MAX_RADIUS = 20;

Circle[] circles;

void setup() {
size(300, 300);
frameRate(10);
smooth();
circles = new Circle[NUM_CIRCLES];
for (int i = 0; i < circles.length; i++) {
circles[i] = new Circle(random(width), random(height), random(MIN_RADIUS, MAX_RADIUS));
}
}

void update() {
for (int i = 0; i < circles.length; i++) {
circles[i].update();
}
}

void draw() {
update();
background(255);
for (int i = 0; i < circles.length; i++) {
circles[i].draw();
}
}

class Circle {
float x;
float y;
float radius;

float heading;
float speed;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
heading = random(TWO_PI);
speed = 1;
}

void update() {
x += random(-1, 1);
y += random(-1, 1);
}

void draw() {
pushStyle();
noFill();
stroke(0);
strokeWeight(1);
ellipseMode(RADIUS);
pushMatrix();
translate(x, y);
rotate(heading);
ellipse(0, 0, radius, radius);
line(0, 0, radius, 0);
popMatrix();
popStyle();
}
}``````

To make use of the new `heading` and `speed` variables we will start by changing the `update()` function to calculate the change in position each frame. Here's the new implementation:

``````void update() {
float dx = speed * cos(heading);
float dy = speed * sin(heading);
x += dx;
y += dy;
}``````

Changing this one function makes our circles move in a straight line. Note: the circles will move off the display quite quickly now, so you may have to restart the sketch to see anything.

• Show Code
``````/** @peep sketch **/

int NUM_CIRCLES = 100;
float MIN_RADIUS = 10;
float MAX_RADIUS = 20;

Circle[] circles;

void setup() {
size(300, 300);
frameRate(10);
smooth();
circles = new Circle[NUM_CIRCLES];
for (int i = 0; i < circles.length; i++) {
circles[i] = new Circle(random(width), random(height), random(MIN_RADIUS, MAX_RADIUS));
}
}

void update() {
for (int i = 0; i < circles.length; i++) {
circles[i].update();
}
}

void draw() {
update();
background(255);
for (int i = 0; i < circles.length; i++) {
circles[i].draw();
}
}

class Circle {
float x;
float y;
float radius;

float heading;
float speed;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
heading = random(TWO_PI);
speed = 1;
}

void update() {
float dx = speed * cos(heading);
float dy = speed * sin(heading);
x += dx;
y += dy;
}

void draw() {
pushStyle();
noFill();
stroke(0);
strokeWeight(1);
ellipseMode(RADIUS);
pushMatrix();
translate(x, y);
rotate(heading);
ellipse(0, 0, radius, radius);
line(0, 0, radius, 0);
popMatrix();
popStyle();
}
}``````

This version of the `update()` function implements Behaviour 1, i.e., the circles move in a straight line. We could keep all of the code to implement other behaviours in the `update()` function but this might become confusing as we add more behaviours. Instead, we're going to "refactor" our code to produce a new function that implements just Behaviour 1 and call it from `update()`.

``````void update() {
behaviour1();
}

void behaviour1() {
// Constant linear motion
float dx = speed * cos(heading);
float dy = speed * sin(heading);
x += dx;
y += dy;
}``````

These types of changes to code is called "refactoring", it is a technique where the code is changed to be clearer to read, or easier to maintain without changing its functionality, i.e., the updated code does exactly the same thing as the original code. In this case, we have made the `update()` function "self-documenting", i.e., through the use of an appropriate function name we have described what the `update()` function does in the call to `behaviour1()`. We have also made it easier to maintain the `Circle` class by making it clear that any time we want to add a new behaviour, we simply write a new function and call it from `update()`. This makes it easier for other people to understand how the class works and how they might extend it. Here is a sketch that uses this refactored code, as you can see it does exactly the same as before:

• Show Code
``````/** @peep sketch **/
int NUM_CIRCLES = 100;
float MIN_RADIUS = 10;
float MAX_RADIUS = 20;

Circle[] circles;

void setup() {
size(300, 300);
frameRate(10);
smooth();
circles = new Circle[NUM_CIRCLES];
for (int i = 0; i < circles.length; i++) {
circles[i] = new Circle(random(width), random(height), random(MIN_RADIUS, MAX_RADIUS));
}
}

void update() {
for (int i = 0; i < circles.length; i++) {
circles[i].update();
}
}

void draw() {
update();
background(255);
for (int i = 0; i < circles.length; i++) {
circles[i].draw();
}
}

class Circle {
float x;
float y;
float radius;

float heading;
float speed;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
heading = random(TWO_PI);
speed = 1;
}

void draw() {
pushStyle();
noFill();
stroke(0);
strokeWeight(1);
ellipseMode(RADIUS);
pushMatrix();
translate(x, y);
rotate(heading);
ellipse(0, 0, radius, radius);
line(0, 0, radius, 0);
popMatrix();
popStyle();
}

void update() {
behaviour1();
}

void behaviour1() {
// Constant linear motion
float dx = speed * cos(heading);
float dy = speed * sin(heading);
x += dx;
y += dy;
}
}``````

Change the initial value of speed assigned to each circle, or use a call to `random()` to produce circles that travel at different speeds. Notice that the description of Behaviour 1 in the Process Compendium library does not specify what speed forms should move at, or whether different forms should move at different speeds.

# Behaviour 2: Constrain to surface

The second behaviour in the Process Compendium library is to constrain an element to a surface. If we interpret this to mean that forms should not leave the display window, this can be simply achieved by adding a new method to our `Circle` class that checks the values of `x` and `y` against the bounds of the display window, like this:

``````void behaviour2() {
// Constrain to surface
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x > width) x = width;
if (y > height) y = height;
}``````

Having added the method to our `Circle` class we can use it by calling it in the `update()` method.

``````void update() {
behaviour1();
behaviour2();
}``````

The effect of these changes on our sketch is that now the circles are stopped from moving in a direction when their centre touches the edge of the display window:

• Show Code
``````/** @peep sketch **/
int NUM_CIRCLES = 100;
float MIN_RADIUS = 10;
float MAX_RADIUS = 20;

Circle[] circles;

void setup() {
size(300, 300);
frameRate(10);
smooth();
circles = new Circle[NUM_CIRCLES];
for (int i = 0; i < circles.length; i++) {
circles[i] = new Circle(random(width), random(height), random(MIN_RADIUS, MAX_RADIUS));
}
}

void update() {
for (int i = 0; i < circles.length; i++) {
circles[i].update();
}
}

void draw() {
update();
background(255);
for (int i = 0; i < circles.length; i++) {
circles[i].draw();
}
}

class Circle {
float x;
float y;
float radius;

float heading;
float speed;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
heading = random(TWO_PI);
speed = 1;
}

void draw() {
pushStyle();
noFill();
stroke(0);
strokeWeight(1);
ellipseMode(RADIUS);
pushMatrix();
translate(x, y);
rotate(heading);
ellipse(0, 0, radius, radius);
line(0, 0, radius, 0);
popMatrix();
popStyle();
}

void update() {
behaviour1();
behaviour2();
}

void behaviour1() {
// Constant linear motion
float dx = speed * cos(heading);
float dy = speed * sin(heading);
x += dx;
y += dy;
}

void behaviour2() {
// Constrain to surface
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x > width) x = width;
if (y > height) y = height;
}
}``````

We may decide that this is not what Casey Reas meant in the description of Behaviour 2, and that a form should be stopped such that no part of it may be drawn outside of the surface. We can do this simply for circles by changing the tests to take into consideration the radius of the circle:

``````void behaviour2() {
// Constrain to surface
if (x < radius) x = radius;
if (y < radius) y = radius;
if (x > width - radius) x = width - radius;
if (y > height - radius) y = height - radius;
}``````

The effect of this is that the circles appear to be blocked by the edges of the display window:

• Show Code
``````/** @peep sketch **/
int NUM_CIRCLES = 100;
float MIN_RADIUS = 10;
float MAX_RADIUS = 20;

Circle[] circles;

void setup() {
size(300, 300);
frameRate(10);
smooth();
circles = new Circle[NUM_CIRCLES];
for (int i = 0; i < circles.length; i++) {
circles[i] = new Circle(random(width), random(height), random(MIN_RADIUS, MAX_RADIUS));
}
}

void update() {
for (int i = 0; i < circles.length; i++) {
circles[i].update();
}
}

void draw() {
update();
background(255);
for (int i = 0; i < circles.length; i++) {
circles[i].draw();
}
}

class Circle {
float x;
float y;
float radius;

float heading;
float speed;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
heading = random(TWO_PI);
speed = 1;
}

void draw() {
pushStyle();
noFill();
stroke(0);
strokeWeight(1);
ellipseMode(RADIUS);
pushMatrix();
translate(x, y);
rotate(heading);
ellipse(0, 0, radius, radius);
line(0, 0, radius, 0);
popMatrix();
popStyle();
}

void update() {
behaviour1();
behaviour2();
}

void behaviour1() {
// Constant linear motion
float dx = speed * cos(heading);
float dy = speed * sin(heading);
x += dx;
y += dy;
}

void behaviour2() {
// Constrain to surface
if (x < radius) x = radius;
if (y < radius) y = radius;
if (x > width - radius) x = width - radius;
if (y > height - radius) y = height - radius;
}
}``````

Investigate the `constrain()` function in the Processing reference. Can you rewrite the function `behaviour2()` in a more concise way using this function? Is the resulting code easier to understand?

Regardless of how we implement Behaviour 2, the forms to which it is applied will get stuck in the corners of the surface. We won't attempt to change the way that Behaviour 2 operates to avoid this but as we will see, other behaviours when combined with this one will take care of moving elements out of the corners again.

# Behaviour 3: While touching another, change direction

Behaviour 3 states that while an element is touching another it should change direction. The specifics of how an element should change its direction aren't given in the Process Compendium but for the moment we will assume that a small amount is added to the heading. We can sketch out the implementation of Behaviour 3 as follows:

``````void behaviour3() {
// Check all of the circles in the sketch
for (int i = 0; i < circles.length; i++) {
// If the circle is not this one
if (circles[i] != this) {
// If touching the other circle
if (touching(circles[i])) {
// Change direction
heading += random(-DELTA_ANGLE, DELTA_ANGLE);
}
}
}
}``````

We can't run this code yet, as there are a few things missing from our class, in some ways this is somewhere between an algorithm and code as we usually write it. This approach to developing code can be very helpful in thinking through what we need in order to be able to implement the behaviour. To complete the implementation we can see that we will need to define a constant called DELTA_ANGLE that will control how much the heading changes when this behaviour is applied:

``float DELTA_ANGLE = TWO_PI/36;``

We can also see that we need to define a method that will test whether a circle is touching another, called `touching()` this method will need to take another circle as a parameter and return a Boolean value. Here's an outline of what that function might look like:

``````void touching(Circle other) {
return (distance(other) < radius + other.radius);
}``````

As you can see, the `touching()` function also calls for the creation of another function that calculates the distance between (the centres of) two circles. Here's an implementation of that function:

``````float distance(Circle other) {
return dist(x, y, other.x, other.y);
}``````

If we implement all of these in the `Circle` class we can simply change the `update()` function to use the new behaviour, as such:

``````void update() {
behaviour1();
behaviour2();
behaviour3();
}``````

To help us understand what is going on in our sketch, we can also add a little more to the `draw()` method in the `Circle` class to draw a red line between the centres of circles that are touching:

``````void draw() {
pushStyle();

// ... CODE TO DRAW CIRCLE HERE ...

stroke(192, 0, 0, 64);
for (int i = 0; i < circles.length; i++) {
Circle other = circles[i];
if (touching(other)) {
line(x, y, other.x, other.y);
}
}
popStyle();
}``````

The result of applying these changes is the following process:

• Show Code
``````/** @peep sketch **/
int NUM_CIRCLES = 100;
float MIN_RADIUS = 10;
float MAX_RADIUS = 20;

float DELTA_ANGLE = TWO_PI/36;

Circle[] circles;

void setup() {
size(300, 300);
frameRate(10);
smooth();
circles = new Circle[NUM_CIRCLES];
for (int i = 0; i < circles.length; i++) {
circles[i] = new Circle(random(width), random(height), random(MIN_RADIUS, MAX_RADIUS));
}
}

void update() {
for (int i = 0; i < circles.length; i++) {
circles[i].update();
}
}

void draw() {
update();
background(255);
for (int i = 0; i < circles.length; i++) {
circles[i].draw();
}
}

class Circle {
float x;
float y;
float radius;

float heading;
float speed;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
heading = random(TWO_PI);
speed = 1;
}

void draw() {
pushStyle();
noFill();
stroke(0);
strokeWeight(1);
ellipseMode(RADIUS);
pushMatrix();
translate(x, y);
rotate(heading);
ellipse(0, 0, radius, radius);
line(0, 0, radius, 0);
popMatrix();
stroke(192, 0, 0, 64);
for (int i = 0; i < circles.length; i++) {
Circle other = circles[i];
if (touching(other)) {
line(x, y, other.x, other.y);
}
}
popStyle();
}

void update() {
behaviour1();
behaviour2();
behaviour3();
}

void behaviour1() {
// Constant linear motion
float dx = speed * cos(heading);
float dy = speed * sin(heading);
x += dx;
y += dy;
}

void behaviour2() {
// Constrain to surface
if (x < radius) x = radius;
if (y < radius) y = radius;
if (x > width - radius) x = width - radius;
if (y > height - radius) y = height - radius;
}

void behaviour3() {
// While touching another, change direction
for (int i = 0; i < circles.length; i++) {
if (circles[i] != this) {
if (touching(circles[i])) {
heading += random(-DELTA_ANGLE, DELTA_ANGLE);
}
}
}
}

void touching(Circle other) {
return (distance(other) < radius + other.radius);
}

float distance(Circle other) {
return dist(x, y, other.x, other.y);
}
}``````

# Behaviour 4: While touching another, move away from its centre

We have almost completed all of the behaviours required to implement Process 4, the last behaviour is Behaviour 4, which states that an element should move away from another element that it is touching. Again, we will start by sketching out the method:

``````void behaviour4() {
// While touching another, move away from its centre
for (int i = 0; i < circles; i++) {
// If the current circle is not this one
if (circles[i] != this) {
// If the current circle is touching this one
if (touching(circles[i])) {
Circle other = circles[i];
// Calculate the distance to the other circle
float d = distance(other);
// Calculate the direction in x and y to the other circle
float dx = (other.x - x)/d;
float dy = (other.y - y)/d;
// Move this circle in the opposite direction using the current speed
x -= speed * dx;
y -= speed * dy;
}
}
}
}``````

Because this new method uses the same `distance()` and `touching()` methods as `behaviour3()` there is nothing more to implement for this behaviour to work, we just need to change the `update()` method to call `behaviour4()`:

• Show Sketch
``````/** @peep sketchcode **/
int NUM_CIRCLES = 100;
float MIN_RADIUS = 10;
float MAX_RADIUS = 20;

float DELTA_ANGLE = TWO_PI/36;

Circle[] circles;

void setup() {
size(300, 300);
frameRate(10);
smooth();
circles = new Circle[NUM_CIRCLES];
for (int i = 0; i < circles.length; i++) {
circles[i] = new Circle(random(width), random(height), random(MIN_RADIUS, MAX_RADIUS));
}
}

void update() {
for (int i = 0; i < circles.length; i++) {
circles[i].update();
}
}

void draw() {
update();
background(255);
for (int i = 0; i < circles.length; i++) {
circles[i].draw();
}
}

class Circle {
float x;
float y;
float radius;

float heading;
float speed;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
heading = random(TWO_PI);
speed = 1;
}

void draw() {
pushStyle();
noFill();
stroke(0);
strokeWeight(1);
ellipseMode(RADIUS);
pushMatrix();
translate(x, y);
rotate(heading);
ellipse(0, 0, radius, radius);
line(0, 0, radius, 0);
popMatrix();
stroke(192, 0, 0, 64);
for (int i = 0; i < circles.length; i++) {
Circle other = circles[i];
if (touching(other)) {
line(x, y, other.x, other.y);
}
}
popStyle();
}

void update() {
behaviour1();
behaviour2();
behaviour3();
behaviour4();
}

void behaviour1() {
// Constant linear motion
float dx = speed * cos(heading);
float dy = speed * sin(heading);
x += dx;
y += dy;
}

void behaviour2() {
// Constrain to surface
if (x < radius) x = radius;
if (y < radius) y = radius;
if (x > width - radius) x = width - radius;
if (y > height - radius) y = height - radius;
}

void behaviour3() {
// While touching another, change direction
for (int i = 0; i < circles.length; i++) {
if (circles[i] != this) {
if (touching(circles[i])) {
heading += random(-DELTA_ANGLE, DELTA_ANGLE);
}
}
}
}

void behaviour4() {
// While touching another, move away from its centre
for (int i = 0; i < circles; i++) {
if (circles[i] != this) {
if (touching(circles[i])) {
Circle other = circles[i];
float d = distance(other);
float dx = (other.x - x)/d;
float dy = (other.y - y)/d;
x -= speed * dx;
y -= speed * dy;
}
}
}
}

void touching(Circle other) {
return (distance(other) < radius + other.radius);
}

float distance(Circle other) {
return dist(x, y, other.x, other.y);
}
}``````

# Process 4

This completes the implementation of behaviours necessary to implement Process 4. Going back to the description of Process 4 from the Process Compendium we can see that we need to implement a different method of drawing:

Process 4: A rectangular surface filled with varying sizes of Element 1. Draw a line from the centers of Elements that are touching. Set the value of the shortest possible line to black and the longest to white, with varying grays representing values in between.

Rather than change the way that the Circle class draws itself, we will change the `draw()` function of the sketch, to use the distance between circles to determine how to draw lines between the centres of the circles:

``````void draw() {
update(); // Update all of the circles
strokeWeight(0.5);
for (int i = 0; i < circles.length; i++) {
// Get a first circle
Circle circle1 = circles[i];
for (int j = i+1; j < circles.length; j++) {
// Get a second circle
Circle circle2 = circles[j];
// If the circles are touching
if (circle1.touching(circle2)) {
// Calculate the grey value using the map function based on the distance between the circles
stroke(map(circle1.distance(circle2), 0, circle1.radius + circle2.radius, 0, 255));
// Draw a line between the centres of the circles
line(circle1.x, circle1.y, circle2.x, circle2.y);
}
}
}``````

Here's the complete implementation for Process 4:

• Show Sketch
``````/** @peep sketchcode **/
int NUM_CIRCLES = 200;
float MIN_RADIUS = 10;
float MAX_RADIUS = 20;

float DELTA_ANGLE = TWO_PI/36;

Circle[] circles;

void setup() {
size(300, 300);
frameRate(10);
background(255);
smooth();
circles = new Circle[NUM_CIRCLES];
for (int i = 0; i < circles.length; i++) {
circles[i] = new Circle(random(width), random(height), random(MIN_RADIUS, MAX_RADIUS));
}
}

void update() {
for (int i = 0; i < circles.length; i++) {
circles[i].update();
}
}

void draw() {
update(); // Update all of the circles
strokeWeight(0.5);
for (int i = 0; i < circles.length; i++) {
// Get a first circle
Circle circle1 = circles[i];
for (int j = i+1; j < circles.length; j++) {
// Get a second circle
Circle circle2 = circles[j];
// If the circles are touching
if (circle1.touching(circle2)) {
// Calculate the grey value using the map function based on the distance between the circles
stroke(map(circle1.distance(circle2), 0, circle1.radius + circle2.radius, 0, 255));
// Draw a line between the centres of the circles
line(circle1.x, circle1.y, circle2.x, circle2.y);
}
}
}
}

class Circle {
float x;
float y;
float radius;

float heading;
float speed;

Circle(float _x, float _y, float _radius) {
x = _x;
y = _y;
radius = _radius;
heading = random(TWO_PI);
speed = 1;
}

void draw() {
pushStyle();
noFill();
stroke(0);
strokeWeight(1);
ellipseMode(RADIUS);
pushMatrix();
translate(x, y);
rotate(heading);
ellipse(0, 0, radius, radius);
line(0, 0, radius, 0);
popMatrix();
stroke(192, 0, 0, 64);
for (int i = 0; i < circles.length; i++) {
Circle other = circles[i];
if (touching(other)) {
line(x, y, other.x, other.y);
}
}
popStyle();
}

void update() {
behaviour1();
behaviour2();
behaviour3();
behaviour4();
}

void behaviour1() {
// Constant linear motion
float dx = speed * cos(heading);
float dy = speed * sin(heading);
x += dx;
y += dy;
}

void behaviour2() {
// Constrain to surface
if (x < radius) x = radius;
if (y < radius) y = radius;
if (x > width - radius) x = width - radius;
if (y > height - radius) y = height - radius;
}

void behaviour3() {
// While touching another, change direction
for (int i = 0; i < circles.length; i++) {
if (circles[i] != this) {
if (touching(circles[i])) {
heading += random(-DELTA_ANGLE, DELTA_ANGLE);
}
}
}
}

void behaviour4() {
// While touching another, move away from its centre
for (int i = 0; i < circles; i++) {
if (circles[i] != this) {
if (touching(circles[i])) {
Circle other = circles[i];
float d = distance(other);
float dx = (other.x - x)/d;
float dy = (other.y - y)/d;
x -= speed * dx;
y -= speed * dy;
}
}
}
}

void touching(Circle other) {
return (distance(other) < radius + other.radius);
}

float distance(Circle other) {
return dist(x, y, other.x, other.y);
}
}``````

# Exercises

1. Experiment with the implementation of Process 4 to see what difference the number of circles and the ranges of speeds and sizes make to the behaviour of the process as a whole.
2. Using the implementation of Process 4 as a starting point, change the `draw()` function for the sketch to implement Process 5, which described in the Process Compendium as:
 `Process 5` `A rectangular surface filled with varying sizes of Element 1. Draw the perimeter of each Element as a black line and the center as a white dot. Draw a gray line from the centers of Elements that are touching.`

## Comments

Nobody has said anything yet.