loops

Loops are used to repeat an action over and over for a given number of times, or until a specific condition is met.

In Processing there are two kinds of loops: for-loops and while-loops.

for-loops

For-loops are the most complex of the two loop structures, and also the most often used. They allow you to repeat a portion of code for a fixed number of times.

In Processing, you declare a for-loop as follows:

for (initialization; condition; follow-up action) {
  // statements
}

// out of the loop               

We will examine the following more specific version because this is how for-loops are generally used:

for (int i=0; i < max; i++) {
  // statements
}

// out of the loop               

As you can see, the loop definition has three distinct parts:

In practice, the example for-loop above will repeat its segment of code max number of times, and for every iteration, the value i will be incremented by 1, going from 0 to (max-1). This is very useful, since you can use the variable i inside the loop to perform some calculations or to act as an index in an array. (we will discuss arrays shortly)

Also note that even though it is generally recommended to use meaningful names for variables and not single letters, for-loop counters constitute an exception to this rule as they are throw-away variables. Common names for throw-away variables are i, j, k.

Let's build a sketch which draws a 20 x 20px grid on any canvas size.

for (int x=0; x < width; x = x+20) {
  line(x, 0, x, height);
}

for (int y=0; y < height; y = y+20) {
  line(0, y, width, y);
}

We can also use a different, shorter notation for incrementing the counter:

for (int x=0; x < width; x+=20) {
  line(x, 0, x, height);
}

for (int y=0; y < height; y+=20) {
  line(0, y, width, y);
}        

If our grid is symmetrical (width == height), we can condense the code down to:

// remember that width == height
for (int i=0; i < width; i = i+20) { 
  // draw a vertical line
  line(i, 0, i, height); 
  // draw a horizontal line
  line(0, i, width, i); 
}                 

nested for-loops

Now that we have seen the inner workings of a for-loop, we can introduce nested for-loops. A nested for-loop is nothing more than a loop inside another loop.

In Processing, a nested for-loop looks like this:

for (int i=0; i < 10; i++) {
  // the outer for-loop
  // 1. statements

  for (int j=0; j < 20; j++) {
    // the inner for loop
    // 2. more statements
  }
}

// outside the loops

In this case, two loops are being defined, an outer loop, and an inner loop. Note how the inner for-loop is part of the code segment that belongs to the outer for-loop. Therefore, for every iteration of the outer loop, the inner loop will run through a full cycle.

This means that, in the example above, 1. statements will be executed 10 times, and 2. more statements (which is inside the inner loop) will be executed 10 * 20 = 200 times. When i = 0, then j will go from 0 to 19; when i = 1, j will go from 0 to 19 again; and so on, resulting in every possible combinations of i and j.

Nested for-Loops are very convenient. Say for example you are writing code to do some calculation for every pixel on your screen at resolution 800x600. Then you could write:

for (int i=0; i < 800; i++) {
  for (int j=0; j < 600; j++) {
    // do the calculations

    // here you can use i and j as the (x,y) coordinates of a pixel
    // every pixel will be visited, column by column
  }
}

Or, to modify our grid example above, we can draw a grid of squares.

int cellSize = 20;  // size of our rectangles

// walk across the x-axis
for (int x=0; x < width ; x = x+cellSize) {
  // walk down the y-axis
  for (int y=0; y < height; y = y+cellSize) {
    rect(x, y, cellSize, cellSize);
  }
}

And turn it into a checkerboard.

int cellSize = 25;  // size of our rectangles
boolean white = true;  // flag to indicate the fill

// walk across the x-axis
for (int x=0; x < width ; x = x+cellSize) {
  // walk down the y-axis
  for (int y=0; y < height; y = y+cellSize) {
    if (white) {
      fill(255);
    } else {
      fill(0);
    }
    rect(x, y, cellSize, cellSize);

    // toggle the fill flag
    white = !white;
  }
}

You can nest an unlimited number of for-loops, given that your application requires it.

while-loops

While-loops can often perform similar tasks as for-loops, but they are a lot simpler in terms of their structure. A while loop is defined as follows:

while (condition) {
  // statements
}

// out of the loop           

The difference between the for-loop and the while-loop is that the latter can execute an undetermined number of times, as long as the condition you specified is true. So for example, if you wanted to loop until the user presses a key, you would write something like this (in pseudo-code):

boolean waitForKeyPress = true;

while (waitForKeyPress ==  true) {
  // check for keypress
  if (key was pressed) then waitForKeyPress = false;
}     

Note that you can often -but not always- rewrite for-loops as while-loops and vice-versa. For instance, we can rewrite our grid example:

// draw the vertical lines
int x = 0;
while(x < width) {
  line(x, 0, x, height);
  x = x+20;
}

// draw the horizontal lines
int y = 0;
while(y < height) {
  line(0, y, width, y);
  y = y+20;
}

infinite loops

You must be careful when working with loops, especially while-loops, so that you don't build infinite loops . You have to make sure that inside your loop, there is some mechanism that will eventually make the condition you specified become false. Otherwise, you will get stuck in an infinite loop, which will make your application (and even Processing in some cases) freeze.

modularity and boundary conditions

Modularity refers to the extent to which your code has been composed out of easily understandable and reusable segments, or modules. If you look closely at our checkerboard, you will notice that it works fine for a 300 x 300px window, but it breaks for a window size of 200 x 200px. A first solution idea would be to call white = !white; every time the inner-loop goes all the way through:

int cellSize = 20;  // size of our rectangles
boolean white = true;  // flag to indicate the fill

// walk across the x-axis
for (int x=0; x < width ; x = x+cellSize) {
  // walk down the y-axis
  for (int y=0; y < height; y = y+cellSize) {
    if (white) {
      fill(255);
    } else {
      fill(0);
    }
    rect(x, y, cellSize, cellSize);

    // toggle the fill flag
    white = !white;
  }
  // toggle the fill flag
  white = !white;
}

The code now works for the 200 x 200px window but breaks for the original 300 x 300px version. This problem exemplifies the need for boundary conditions. Alternating the fill color in the interior cells is relatively straightforward, but at the beginning or end of each column it becomes tricky. You will often find that the difficult part of your problem lies at the boundaries of the problem space, and that the boundary cases are often precisely those places where your solutions break.

We need to make the code more modular, i.e. we need to make it work in all possible situations. The first step in doing so is understanding what is going on, and establishing a general rule that applies for all situations.

Our code traverses the canvas column by column, and then draws cells one below the other until it reaches the bottom. Once it reaches the bottom, it goes to the top of the next column. A general rule is that every new column needs to start with an alternate color. This new color should not be based on the color used in the last cell of the previous column. We could say that all odd columns start off white, all even columns start off black.

The following code will work for any window and cell dimensions:

int cellSize = 20;  // size of our rectangles
boolean white = true;  // flag to indicate the fill
boolean oddColumn = true;  // flag to indicate the column type

// walk across the x-axis
for (int x=0; x < width ; x = x+cellSize) {
  // walk down the y-axis
  for (int y=0; y < height; y = y+cellSize) {
    if (white) {
      fill(255);
    } else {
      fill(0);
    }
    rect(x, y, cellSize, cellSize);

    // toggle the fill flag
    white = !white;
  }

  // flip the flag that tells us whether we're starting an even or odd column
  oddColumn = !oddColumn;

  // make sure that each successive column starts with a different fill
  white = oddColumn;
}

Processing Workshop

Elie Zananiri
Alberta College of Art + Design
3-5 April 2008