Random Elements in a Grid

Basic grid

function setup() {
	createCanvas(windowWidth, windowHeight);
}

function draw() {
	background(200);
	grid(5, 6);
}

function grid(rows, columns) {
	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			square(x, y, 30);
		}
	}
}
image

Draw one of the cells differently

	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			let size = 30;
			if (row === 3) {
				if (column === 2) {
					size = 50;
				}
			}
			square(x, y, size);
		}
	}
image

Replace nested if by "and" (&&)

	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			let size = 30;
			if (row === 3 && column === 2) {
				size = 50;
			}
			square(x, y, size);
		}
	}

Replace a cell by a circle.

Rectangle positions are specified as the upper left corner, and circle positions are specified as the center, so this looks bad.

	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			let size = 30;
			if (row === 3 && column === 2) {
				circle(x, y, size);
			} else {
				square(x, y, size);
			}
		}
	}

rectMode() changes the behavior of square() (and rect()) to match circle(), so that it is easier to use them interchangeably.

image
	rectMode(CENTER);
	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			let size = 30;
			if (row === 3 && column === 2) {
				circle(x, y, size);
			} else {
				square(x, y, size);
			}
		}
	}

image

We can look at any combination of and row and column, and functions that combine them, to decide what to draw in a cell.

	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			let size = 30;
			if (row % 3 === 0) {
				circle(x, y, size);
			} else {
				square(x, y, size);
			}
		}
	}
image
	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			let size = 30;
			if ((row * column) % 3 === 0) {
				circle(x, y, size);
			} else {
				square(x, y, size);
			}
		}
	}
image
	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			let size = 30;
			if ((row ** 2 + column ** 2) % 3 === 0) {
				circle(x, y, size);
			} else {
				square(x, y, size);
			}
		}
	}
image
	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			let size = 30;
			if ((row ** 2 + column ** 2) % 3 === 0) {
				fill('blue');
				circle(x, y, size);
			} else {
				fill('green');
				square(x, y, size);
			}
		}
	}
image
	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			let size = 30;
			if ((row ** 3 + column ** 2) % 3 === 0) {
				fill('blue');
				circle(x, y, size);
			} else {
				fill('green');
				square(x, y, size);
			}
		}
	}
image

random()

Let's say we want a quarter of the cells to be circles, in no particular order.

random() is less than 1/4 a quarter of the time (on average).

	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			let size = 30;
			if (random() < 1/4) {
				fill('blue');
				circle(x, y, size);
			} else {
				fill('green');
				square(x, y, size);
			}
		}
	}
image

Each time grid() is called, it will produce a different random pattern.

image

Since it is called thirty times per second, this creates a flicker effect.

image

Preventing Flicker

Technique #1: Do all drawing inside of setup()

First approach: eliminate the draw() function, and do everything in setup().

function setup() {
	createCanvas(windowWidth, windowHeight);
	rectMode(CENTER);
	background(200);
	grid(30, 20);
}

Technique #2: noLoop()

Second approach: call noLoop() . This prevents draw() from being called a second time.

function setup() {
	createCanvas(windowWidth, windowHeight);
	rectMode(CENTER);
}

function draw() {
	background(200);
	grid(30, 20);
	noLoop();
}

The advantage of noLoop() over putting everything inside of setup() is that the drawing can sometimes be animated, and sometimes not. Adding the following function to the sketch above adds the behavior that a new grid is drawn each time that the mouse is pressed.

function mousePressed() {
	loop();
}

Technique #3: noise()

Third approach: use noise() instead of random(). noise(x, y) returns a different value for each different x and y, so noise(row, column) is a different value for each different grid position. Unlike random(), noise() with the same arguments always returns the same value. So for each position on the grid, noise(row, column) will always the same value. This eliminates the flicker.

function setup() {
	createCanvas(windowWidth, windowHeight);
	rectMode(CENTER);
}

function draw() {
	background(200;
	grid(30, 20);
}

function grid(rows, columns) {
	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			let size = 30;
			if (noise(row, column) < 1/4) {
				fill('blue');
				circle(x, y, size);
			} else {
				fill('green');
				square(x, y, size);
			}
		}
	}
}

This third approach allows us to keep animating.

function setup() {
	createCanvas(windowWidth, windowHeight);
	rectMode(CENTER);
}

function draw() {
	background(200);
	grid(30, 20);
}

function grid(rows, columns) {
	for (let row = 0; row < rows; row++) {
		for (let column = 0; column < columns; column++) {
			let x = 30 + 50 * column;
			let y = 40 + 50 * row;

			let size = map(mouseX, 0, width, 10, 30);
			if (noise(row, column) < 1/2) {
				fill('blue');
				circle(x, y, size);
			} else {
				fill('green');
				square(x, y, size);
			}
		}
	}
}

Technique #4: Store the random values in an Array

There is a fourth approach, which is to save the information about each cell in an Array. We will cover that later.