Generative Typography

Loops and Grids

Students will leave the session with an understanding of loops as a form of automation and the ability to control the flow of programs with conditionals.

Class Agenda

Morning Sketching 8:30 - 9am
Presentations & Critique 9 - 10am
BREAK 30min
Read & Discussion 10:30 - 11:30am
Code Lecture 11:30 - 12pm
BREAK 30min
Workshop 12:30 - 2pm

Morning Sketch

Recreate one of Piet Mondrian's geometric paintings, such as Composition No. III.

Weekly Assignment

Create a grid/pattern of tiles using loops. Create functions as needed to abstract away some of the more complex visual logic.

Readings

Discussion Questions

Further Resources

Code Lecture

Loops

Loops allow us to repeatedly run an operation. For example let's manually create a line of 4 columns:

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);

  rect(0, 0, 100, height);
  rect(100, 0, 100, height);
  rect(200, 0, 100, height);
  rect(300, 0, 100, height);
}

This first example achieves the goal but at the same time is repetitive and doesn't make our code and logic easy to maintain. If you wanted to change the size of the column you would have to update it in multiple places. Let's swap in a variable to see how that clean up our logic.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);

  var columnWidth = 100;

  rect(0, 0, columnWidth, height);
  rect(columnWidth, 0, columnWidth, height);
  rect(columnWidth * 2, 0, columnWidth, height);
  rect(columnWidth * 3, 0, columnWidth, height);
}

This example is better and makes our program more resilient but it is still repetitive as we have multiple copies of the column logic. We can further automate this using a loop. The purpose of a loop is that they allow us to repeat a task. One example of a loop is a for loop and the syntax for it is:

for ([initialization]; [condition]; [final - expression]) {
  // logic
}

Initialization is where you create the variable that will track the current state of the loop. Condition is a statement that is evaluated at the start of each loop. The loop continues to run while this statement is true. The final expression is run at the end of every loop and is usually where you update the state of the variable created during initialization. Let's create a simple loop that runs 10 times.

for (var index = 0; index < 10; index += 1) {
  // logic
}

So this example loop will run 10 times. We can verify this by outputting the value of index during each loop.

for (var index = 0; index < 10; index += 1) {
  // You can use the + operator to join Strings together.
  console.log("index " + index);
}

Some things to note. In our example above index is initialized with a value of 0 because in coding we usually start loops at 0 instead of 1. A common use case for a loop is to iterate over a list of items and lists in a lot of programming languages have their first item at the index of 0 instead of 1. We will cover this more once we get to the lecture on arrays. You can start your loop at whatever value you would like and update it however you would like as well.

for (var index = 0; index < 10; index += 2) {
  console.log("increment by 2: " + index);
}

for (var index = 10; index > 0; index -= 1) {
  console.log("decrease index: " + index);
}

Revisiting our example with the rectangles we can now use the value of index to compute the square's position.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);

  var columnWidth = 100;

  for (var index = 0; index < 4; index += 1) {
    // x position increases by columnWidth each loop
    var x = columnWidth * index;

    rect(x, 0, columnWidth, height);
  }
}

Conditionals

Conditionals allow us to introduce control and conditions to our program. The first conditional we will look at is an if statement. The syntax for it is:

if (condition) {
  // the steps to perform
}

The following code is a if statement that will output our message as long as number is less than 1.

var number = 0;

if (number < 1) {
  console.log("this number is less than 1");
}

You can start to bridge your knowledge to branch out and handle different use cases. Another type of conditional is an if...else statement. The syntax for it is:

if (condition) {
  // logic
} else {
  // alternative logic
}

With this we can update our code to always output something.

var number = 0;

if (number < 1) {
  console.log("this number is less than 1");
} else {
  console.log("this number is greater than 1");
}

But this logic doesn't behave as expected when number is given the value of 1. We can further cover this use case by adding an else if statement.

if (condition) {
  // logic
} else if (second_condition) {
  // logic
} else {
  // logic
}

With this our example can correctly be updated to cover all use case.

var number = 0;

if (number < 1) {
  console.log("this number is less than 1");
} else if (number > 1) {
  console.log("this number is greater than 1");
} else {
  console.log("this number is equal to 1");
}

Booleans

Boolean is a javascript data type that represents true or false. When you use conditionals you are using a boolean value and similar to strings and numbers they can be assigned to variables and reused throughout your code.

var number = 0;
var isNumberGreaterThanOne = number < 1;
var isNumberLessThanOne = number > 1;

if (isNumberGreaterThanOne) {
  console.log("this number is less than 1");
} else if (isNumberLessThanOne) {
  console.log("this number is greater than 1");
} else {
  console.log("this number is equal to 1");
}

// you can use the value of the condition elsewhere...

More Operators

Now that we are starting to add control logic to our programs let's introduce more operators as ways to write conditions.

Relational Operation operator
Less than operator. <
Greater than operator. >
Less than or equal operator. <=
> Greater than or equal operator. >=
Equality Operation operator
Strict equality operator. ===
Strict inequality operator. !==
Other Operators operator
Remainder Operator %

Relational Operators allow us to check the relation between two values, for example if one is bigger than the other. Equality Operators allow us to check if two values are equal or not. The Remainder Operator returns the leftover value when one number is divided by another. This comes in handy when we need to add rhythm to our sketches. Now, let's revisit our example with the columns:

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);

  var columnWidth = 100;

  for (var index = 0; index < 4; index += 1) {
    // x position increases by columnWidth each loop
    var x = columnWidth * index;

    if (index % 2 === 0) {
      push();
      fill("black");
      rect(x, 0, columnWidth, height);
      pop();
    } else {
      rect(x, 0, columnWidth, height);
    }
  }
}

Grid (Nested Loops)

We can nest loops to create more complex calculations, like for example a grid. So far we've been creating a column along the width of our canvas. With nesting we can create a loop for the height of the canvas and create a grid.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);

  var cellHeight = 100;
  var cellWidth = 100;

  for (var rowIndex = 0; rowIndex < 4; rowIndex += 1) {
    for (var columnIndex = 0; columnIndex < 4; columnIndex += 1) {
      // x position increases by cellWidth each loop
      var x = cellWidth * columnIndex;
      // y position increases by cellHeight each loop
      var y = cellHeight * rowIndex;

      rect(x, y, cellWidth, cellHeight);
    }
  }
}

Patterns

Knowing the cell's place in a grid means we can compose shapes to make different patterns and variations.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);

  var cellHeight = 100;
  var cellWidth = 100;
  var circleSize = 90;

  for (var rowIndex = 0; rowIndex < 4; rowIndex += 1) {
    for (var columnIndex = 0; columnIndex < 4; columnIndex += 1) {
      var x = cellWidth * columnIndex;
      var y = cellHeight * rowIndex;
      var isAlternatingRow = rowIndex % 2 === 0;

      rect(x, y, cellWidth, cellHeight);

      // alternate between these two sets of logic every other row
      if (isAlternatingRow) {
        push();
        fill("pink");
        ellipse(x + cellWidth / 2, y + cellHeight / 2, circleSize, circleSize);
        pop();
      } else {
        push();
        fill("papayawhip");
        ellipse(x + cellWidth / 2, y + cellHeight / 2, circleSize, circleSize);
        pop();
      }
    }
  }
}