# September 28 (Arrays)

## Announcement: The Points-Back Packet

For a limited time only, through the end of your lab/class session on Tuesday October 6th, we are offering you the chance to get some points back on your first exam.

To do this, complete the points-back-packet-1 linked here. You must complete all of the questions in the packet and submit them by the end of your October 6th session to get up to 50% of your lost points back on each exam.”

In other words, this take-home test is not timed. It’s open book, you can take it home, etc. But you are recommended to do it alone by hand first before you consult other references.

Consider the following program, which draws a star. This star has 10 vertex points, each with 2 values, for a total of 20 data elements.

``````var x0 = 50;
var y0 = 18;
var x1 = 61;
var y1 = 37;
var x2 = 83;
var y2 = 43;
var x3 = 69;
var y3 = 60;
var x4 = 71;
var y4 = 82;
var x5 = 50;
var y5 = 73;
var x6 = 29;
var y6 = 82;
var x7 = 31;
var y7 = 60;
var x8 = 17;
var y8 = 43;
var x9 = 39;
var y9 = 37;

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

function draw() {
background(255, 200, 200);

beginShape();
vertex (x0, y0);
vertex (x1, y1);
vertex (x2, y2);
vertex (x3, y3);
vertex (x4, y4);
vertex (x5, y5);
vertex (x6, y6);
vertex (x7, y7);
vertex (x8, y8);
vertex (x9, y9);
endShape();
}``````

That’s a lot of variables! Providing this data to a program requires either creating 20 variables or using an array that can somehow store these 20 numbers. In the next example, we group these data elements together in two arrays: one for the x-coordinates and one for the y-coordinates. (Note that we also use the `CLOSE` property in `endShape` to fix the visual bug.)

``````var x = [ 50, 61, 83, 69, 71, 50, 29, 31, 17, 39 ];
var y = [ 18, 37, 43, 60, 82, 73, 82, 60, 43, 37 ];

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

function draw() {
background (255, 200, 200);

beginShape();
vertex (x[0],y[0]);
vertex (x[1],y[1]);
vertex (x[2],y[2]);
vertex (x[3],y[3]);
vertex (x[4],y[4]);
vertex (x[5],y[5]);
vertex (x[6],y[6]);
vertex (x[7],y[7]);
vertex (x[8],y[8]);
vertex (x[9],y[9]);
endShape(CLOSE);
}``````

That’s tidier, but we can do even better to abstract this code. Notice any patterns? How about the fact that we are counting our way through the array elements? This counting should be an immediate tip-off to use an iterative code structure, like a `for()` loop, as shown below.

``````var x = [50, 61, 83, 69, 71, 50, 29, 31, 17, 39];
var y = [18, 37, 43, 60, 82, 73, 82, 60, 43, 37];

function setup() {
createCanvas(200, 200);
noLoop();
}

function draw() {
background(255, 200, 200);
var nPoints = x.length;

beginShape();
for (var i = 0; i < nPoints; i++) {
vertex(x[i], y[i]);
}
endShape(CLOSE);

}``````

Now we can do all sorts of tricks. For example, on each frame, we can draw a star in which each point is computed from one of the stored coordinates, to which has been added a random number:

``````var x = [50, 61, 83, 69, 71, 50, 29, 31, 17, 39];
var y = [18, 37, 43, 60, 82, 73, 82, 60, 43, 37];

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

function draw() {
background(255, 200, 200);
var nPoints = x.length;

beginShape();
for (var i = 0; i < nPoints; i++) {
var px = x[i] + random(-3,3);
var py = y[i] + random(-3,3);
vertex(px,py);
}
endShape(CLOSE);
}
``````

We can also progressively modify these arrays. Here, each point does a “random walk“:

``````var x = [50, 61, 83, 69, 71, 50, 29, 31, 17, 39];
var y = [18, 37, 43, 60, 82, 73, 82, 60, 43, 37];

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

function draw() {
background(255, 200, 200);
var nPoints = x.length;

// Progressively modify the arrays with a small amount of randomness.
// Each time draw() is called, the more randomness is added.
var amount = 0.2;
for (var i = 0; i < nPoints; i++) {
x[i] += random(-amount, amount);
y[i] += random(-amount, amount);
}

// Now render the star from those arrays
fill(255);
stroke(0);
beginShape();
for (var i = 0; i < nPoints; i++) {
vertex(x[i], y[i]);
}
endShape(CLOSE);
}``````

Incidentally, with a little extra work, it’s possible to use the same data to render the star with curves instead of straight lines:

``````var x = [50, 61, 83, 69, 71, 50, 29, 31, 17, 39];
var y = [18, 37, 43, 60, 82, 73, 82, 60, 43, 37];

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

function draw() {
background(255, 200, 200);
var nPoints = x.length;

fill(255);
stroke(0);
beginShape();
curveVertex(x[nPoints-1], y[nPoints-1]);
for (var i = 0; i < nPoints; i++) {
curveVertex(x[i], y[i]);
}
curveVertex(x[0], y[0]);
curveVertex(x[1], y[1]);
endShape();
}
``````

## Arrays that Grow

Consider the following sketch. Each time the user clicks, another point is accreted into the background image:

``````var ex;
var ey;

//---------------------------------
function setup() {
createCanvas(300, 300);
background(255, 200, 200);
}

//---------------------------------
function draw() {
fill(0);
ellipse(ex, ey, 5, 5);
}

//---------------------------------
function mousePressed() {
ex = mouseX;
ey = mouseY;
}``````

But what if one wanted to have an animated scene? You can’t do that if you don’t refresh the background on every frame. Here’s the beginning of a solution: store the user’s mouse clicks in an array, and draw all of the points each frame:

``````var x = [];
var y = [];

//---------------------------------
function setup() {
createCanvas(300,300);
}

//---------------------------------
function draw() {
background (255,200,200);

fill(0);
for (var i=0; i<x.length; i++){
var ex = x[i];
var ey = y[i];
ellipse(ex,ey, 5, 5);
}
}

//---------------------------------
function mousePressed(){
x.push(mouseX);
y.push(mouseY);
}``````

Looks the same, right? Well, no. Now we can again do all sorts of tricks with the data we are progressively storing. For example, we can draw the points with some animation, and meanwhile change the color of the background:

``````var x = [];
var y = [];

//---------------------------------
function setup() {
createCanvas(300,300);
}

//---------------------------------
function draw() {
var r = 230 + 25*sin(millis()/1000.0 );
var g = 230 + 25*sin(millis()/1000.0 + HALF_PI);
var b = 230 + 25*sin(millis()/1000.0 - HALF_PI);
background (r,g,b);
fill(0);

if (x.length < 1){
text("Click to begin", 5,15);
}

for (var i=0; i<x.length; i++){
var ex = x[i] + random(-3,3);
var ey = y[i] + random(-3,3);
ellipse(ex,ey, 5, 5);
}
}

//---------------------------------
function mousePressed(){
x.push(mouseX);
y.push(mouseY);
}``````

We can perform computations on these arrays, in order to calculate new information about the collection of user-added points. For example, in the sketch below, we compute the “centroid” or geometric center of the collection by calculating the average X-coordinate and the average Y-coordinate. (The centroid is also known as the center of mass.)

``````var x = [];
var y = [];

//---------------------------------
function setup() {
createCanvas(300, 300);
}

//---------------------------------
function draw() {
background(255, 200, 200);
fill(0);
noStroke();

var nPoints = x.length;
if (nPoints < 1) {
text("Click to begin", 5, 15);
}

// Draw the dots
for (var i = 0; i < nPoints; i++) {
ellipse (x[i],y[i], 5,5);
}

// Compute their centroid point: (xAverage, yAverage)
var xAverage = 0;
var yAverage = 0;
for (var i = 0; i < nPoints; i++) {
xAverage += x[i];
yAverage += y[i];
}
xAverage /= nPoints;
yAverage /= nPoints;

// Draw lines from the centroid to every point
strokeWeight(1);
stroke(0, 0, 0, 50);
for (var i = 0; i < nPoints; i++) {
line (x[i],y[i], xAverage,yAverage);
}

// Draw the centroid
fill(255);
stroke(0);
strokeWeight(2);
ellipse(xAverage, yAverage, 10, 10);
}

//---------------------------------
function mousePressed() {
x.push(mouseX);
y.push(mouseY);
}``````

Notice how the centroid is recalculated when the data is progressively modified on-the-fly:

``````var x = [];
var y = [];

//---------------------------------
function setup() {
createCanvas(300, 300);
for (var i = 0; i < 7; i++) {
var t = map(i,0,7, 0,TWO_PI);
x.push(width/2 + 75*cos(t));
y.push(height/2 + 75*sin(t));
}
}

//---------------------------------
function draw() {
background(255, 200, 200);
fill(0);
noStroke();

// Progressively modify the data
var nPoints = x.length;
for (var i = 0; i < nPoints; i++) {
x[i] += (noise(i * 1 + millis() / 1000.0) - 0.5);
y[i] += (noise(i * 2 + millis() / 1000.0) - 0.5);
ellipse(x[i], y[i], 5, 5);
}

// Draw the dots
for (var i = 0; i < nPoints; i++) {
ellipse(x[i], y[i], 5, 5);
}

// Compute their centroid point: (xAverage, yAverage)
var xAverage = 0;
var yAverage = 0;
for (var i = 0; i < nPoints; i++) {
xAverage += x[i];
yAverage += y[i];
}
xAverage /= nPoints;
yAverage /= nPoints;

// Draw lines from the centroid to every point
strokeWeight(1);
stroke(0, 0, 0, 50);
for (var i = 0; i < nPoints; i++) {
line(x[i], y[i], xAverage, yAverage);
}

// Draw the centroid
fill(255);
stroke(0);
strokeWeight(2);
ellipse(xAverage, yAverage, 10, 10);
}

//---------------------------------
function mousePressed() {
x.push(mouseX);
y.push(mouseY);
}``````

It is possible both to add as well as remove points. The following “drawing program” sketch deletes the last point and inserts a new one, when the number of points exceeds a certain constant. Notice also how the arrays are cleared in `mousePressed()`.

``````var x = [];
var y = [];
var maxNPoints = 10;

//---------------------------------
function setup() {
createCanvas(300, 300);
}

//---------------------------------
function draw() {
background(255, 200, 200);

var nPoints = x.length;
if (nPoints < 1) {
fill(0);
noStroke();
text("Click to begin", 5, 15);
}

// Draw little ellipses on each point
fill(0);
noStroke();
for (var i = 0; i < nPoints; i++) {
var xa = x[i];
var ya = y[i];
ellipse(xa, ya, 5, 5);
}

// Connect the dots with a line
stroke(0, 0, 0, 200);
strokeWeight(2);
noFill();
beginShape();
for (var i = 0; i<nPoints; i++) {
var xa = x[i];
var ya = y[i];
vertex(x[i], y[i]);
}
endShape();
}

//---------------------------------
function mousePressed() {
if (x.length > maxNPoints){
x.shift();
y.shift();
}
x.push(mouseX);
y.push(mouseY);
}

//---------------------------------
function keyPressed() {
x = [];
y = [];
}``````

In the next sketch, points are continually added (and removed) when the mouse button is pressed:

``````var x = [];
var y = [];
var maxNPoints = 40;

//---------------------------------
function setup() {
createCanvas(300, 300);
}

//---------------------------------
function draw() {
background(255, 200, 200);

var nPoints = x.length;
if (nPoints < 1) {
fill(0);
noStroke();
text("Click to begin", 5, 15);
}

// Draw little ellipses on each point
fill(0);
noStroke();
for (var i = 0; i < nPoints; i++) {
var xa = x[i];
var ya = y[i];
ellipse(xa, ya, 5, 5);
}

// Connect the dots with a line
stroke(0, 0, 0, 200);
strokeWeight(2);
noFill();
beginShape();
for (var i = 0; i < nPoints; i++) {
var xa = x[i];
var ya = y[i];
vertex(x[i], y[i]);
}
endShape();

if (mouseIsPressed) {
if (x.length > maxNPoints) {
x.shift();
y.shift();
}
x.push(mouseX);
y.push(mouseY);
}
}``````

It doesn’t take too much more to create some very stimulating experiences. In the next sketch, the distances between neighboring points are progressively exaggerated:

``````var x = [];
var y = [];
var maxNPoints = 40;
var mouseReleasedTime = 0;

//---------------------------------
function setup() {
createCanvas(300, 300);
}

//---------------------------------
function draw() {
background(255, 200, 200);

var nPoints = x.length;
if (nPoints < 1) {
fill(0);
noStroke();
text("Click to begin", 5, 15);
}

// Connect the dots with a line
stroke(0, 0, 0, 128);
strokeWeight(2);
noFill();
var amount = 0.01;
beginShape();
curveVertex(x[0], y[0]);
for (var i = 1; i < nPoints; i++) {
var xa = x[i - 1];
var ya = y[i - 1];
var xb = x[i];
var yb = y[i];
x[i] += amount * (xb - xa);
y[i] += amount * (yb - ya);
curveVertex(x[i], y[i]);
}
curveVertex(x[nPoints - 1], y[nPoints - 1]);
endShape();

// Draw little ellipses on each point
fill(0);
noStroke();
for (var i = 1; i < nPoints; i++) {
var xa = x[i];
var ya = y[i];
ellipse(xa, ya, 5, 5);
}

// Add points if the mouse button is pressed
if (mouseIsPressed) {
// And if the mouse has moved since the previous frame
if ((mouseX != pmouseX) || (mouseY != pmouseY)) {
if (x.length > maxNPoints) {
x.shift();
y.shift();
}
x.push(mouseX);
y.push(mouseY);
}
} else {
// Clear the screen if it has been a while
if ((millis() - mouseReleasedTime) > 12000) {
x = [];
y = [];
}
}
}

// Clear the line when we click.
function mousePressed() {
x = [];
y = [];
x.push(mouseX);
y.push(mouseY);
}

function mouseReleased() {
mouseReleasedTime = millis();
}``````

Watch Zach Lieberman discuss drawing with code at the 2015 Eyeo Festival (watch from 8:28 – 16:40):