Nested Loops
// Getting Started with p5.js
// Lauren McCarthy, Casey Reas, Ben Fry
function setup() {
createCanvas(600, 400);
noStroke();
}
function draw() {
background(0);
for (var y = 0; y < height+25; y += 50) {
for (var x = 0; x < width+25; x += 50) {
fill(255, 140);
ellipse(x, y, 50, 50);
}
}
noLoop(); // save computation -- no animation
}
Exercise: Rewrite this program so that the loop variables take on the values 0, 1, 2, …. You can use fixed numbers for the number of rows and columns, e.g. 6 columns x 4 rows.
// Getting Started with p5.js
// Lauren McCarthy, Casey Reas, Ben Fry
function setup() {
createCanvas(480, 120);
}
function draw() {
background(0);
fill(255);
for (var y = 32; y <= height; y += 8) {
for (var x = 12; x <= width; x += 15) {
ellipse(x + y, y, 16 - y/10.0, 16 - y/10.0);
}
}
noLoop(); // save computation -- no animation
}
Exercise: Make this image, or something similar:
Click the mouse in this canvas to select what to draw, and move horizontally to change the spacing.
// FORM+CODE
// Casey Reas and Chandler McWilliams
var option = 1;
function setup() {
createCanvas(640, 480);
noFill();
frameRate(5); // reduce computation
}
function draw() {
background(255);
var density = map(mouseX, 0, width, 20, 50);
if (option == 1) {
// Option 1: Stitches
for (var x = 50; x <= width-50; x += density) {
for (var y = 50; y <= height-50; y+=density) {
line(x-5, y-5, x+5, y+5);
line(x+5, y-5, x-5, y+5);
}
}
}
else if (option == 2) {
// Option 2: Perspective
for (var x = 50; x <= width-50; x += density) {
for (var y = 50; y <= height-50; y+=density) {
line(x, y, width/2, height/2);
}
}
}
else if (option == 3) {
// Option 3: Overlapping circles
for (var x = 50; x <= width-50; x += density) {
for (var y = 50; y <= height-50; y+=density) {
ellipse(x, y, 40, 40);
}
}
}
else if (option == 4) {
// Option 4: Rotating arcs
var count = 120;
for (var x = 50; x <= width-50; x += density) {
for (var y = 50; y <= height-50; y+=density) {
var s = map(count, 120, 0, 0, TWO_PI*2);
arc(x, y, 14, 14, s, s + PI);
count--;
}
}
}
else if (option == 5) {
// Option 5: Groups of five
for (var x = 50; x < width-50; x += density) {
for (var y = 50; y < height-50; y+=density) {
//rect(x-10, y-10, 22, 22);
for (var i = 0; i < 16; i+=4) {
line(x + i, y, x + i, y + 12);
}
line(x, y, x + 12, y + 12);
}
}
}
}
function mousePressed() {
option++;
if (option > 5) option = 1;
}
Function Exercise
Exercise: Take a previous example with nested loops. Replace the inner loop with a call to a function named drawRow
, and implement the function.
Return Example
// Getting Started with p5.js
// Lauren McCarthy, Casey Reas, Ben Fry
function setup() {
createCanvas(300, 200);
noStroke();
frameRate(5);
}
function draw() {
background(0);
for (var y = 0; y < height; y += 20) {
for (var x = 0; x < width; x += 20) {
fill(0, 200, 0);
if (nearMouse(x, y)) {
rect(x, y, 10, 10);
}
}
}
}
function nearMouse(x, y) {
return dist(x, y, mouseX, mouseY) < 50;
}
// you could also write nearMouse like this:
/*
function nearMouse(x, y) {
if (distance(x, y, mouseX, mouseY) < 100) {
return true;
} else {
return false;
}
}
*/
Exercise: Modify the previous example so that nearMouse
takes a 3rd parameter which is the distance threshold (which is currently fixed at 50). Make a global variable distThreshold
in the program and initialize it to a new distance threshold, say, 30. Use the global variable distThreshold
as the third parameter in the call to nearMouse
.
Discussion
Imagine that instead of passing a third parameter to nearMouse
you just compared the distance to distThreshold
and did not add a third parameter.
- Do you think this would this be a better solution?
- When would this not be a better solution?
Sine = Sin, Cosine = Cos, Radius, and Angle
So far, we have talked about expressing locations in terms of X and Y, but you can also describe locations in terms of angle and radius. These are called polar coordinates.
Introducing Sin and Cos
cos
and sin
functions tell you X and Y coordinates of a point on a circle of radius 1. The input parameter for cos
and sin
is the angle: How far to rotate around the circle. The output is where you land in terms of X (cos
) and Y (sin
):
(From “The Amazing Unit Circle”) This shows that the X, Y coordinates can be computed in terms of the angle, represented by θ. Here, θ is a variable representing an angle, “cos θ” means cos(θ), and “sin θ” means sin(θ).
Here’s a graph showing how the functions sin()
and cos()
translate angle (here represented by the x coordinate) into a value shown as displacement in the Y axis.
function setup() {
createCanvas(400, 120);
noLoop();
}
function draw() {
background(150, 200, 150);
for (var x = 0; x < width; x = x + 1) {
stroke(0);
point(x, 60 - 50 * sin(radians(x)));
stroke(255, 255, 0);
point(x, 60 - 50 * cos(radians(x)));
}
}
Rotating
Perhaps more interestingly, we can now write a program that creates circular motion by translating radius and angle into X and Y. You should understand how the following program works and why the circle moves in a circle.
Exercise:Make the circle go faster. Make the circle bigger. Make circle orbit bigger. What happens if the radius is zero?
function setup() {
createCanvas(200, 200);
frameRate(30);
}
var gAngle = 45;
function draw() {
var centerx = width / 2;
var centery = height / 2;
var radius = 80;
background(150, 200, 150);
var x = cos(radians(gAngle)) * radius;
var y = sin(radians(gAngle)) * radius;
stroke(0);
line(centerx, centery, centerx + x, centery - y);
noStroke();
ellipseMode(CENTER);
ellipse(centerx + x, centery - y, 10, 10);
gAngle = gAngle + 2;
}
Spirals
Finally, change the previous example as follows:
- Start the radius at 1, but increase the radius for each frame.
- Move the background() call to setup() so it does not erase every frame.
- Remove the line, just draw the circle.
- For this web page version, call background and reset radius after a bunch of draw
calls.
function setup() {
createCanvas(200, 200);
frameRate(30);
background(150, 200, 150);
}
var gAngle = 45;
var gRadius = 1;
var gFrameCount = 0;
function draw() {
var centerx = width / 2;
var centery = height / 2;
var x = cos(radians(gAngle)) * gRadius;
var y = sin(radians(gAngle)) * gRadius;
noStroke();
ellipseMode(CENTER);
ellipse(centerx + x, centery - y, 10, 10);
gAngle = gAngle + 2;
gRadius = gRadius + 0.1;
gFrameCount = gFrameCount + 1;
if (gFrameCount > 1000) {
gFrameCount = 0;
gRadius = 1;
background(150, 200, 150);
}
}
The map Function
Rotate and Translate Example
var angle = 0;
function setup() {
createCanvas(300, 300);
frameRate(5);
}
function draw() {
background(200);
push();
translate(width / 2, height / 2);
fill(0, 200, 0);
rotate(radians(angle * 0.5));
rectMode(CENTER);
rect(0, 0, 100, 100);
pop();
push();
translate(width / 2, height / 2);
fill(0, 0, 200);
rotate(radians(angle));
rectMode(CENTER);
rect(0, 0, 50, 50);
pop();
angle = angle + 4;
}
Exercises: Place the rectangles side-by-side. Make the speed of rotation be controlled by two separate variables. Read the p5.js Reference about the noise()
function. Use noise()
to compute the angle of one or both rectangles.
Summary
Now you know:
- How to write nested for loops and draw grids and 2-D arrays.
- How to use return statements in functions.
- How to define and use parameters in functions.
- How to use cos/sin to convert from angle and radius to X and Y
- How to use the map function to convert data from one range to another range.