October 9 & 12

More Objects

Today, I will borrow some code from Khan Academy on object-oriented programming. This online course is really well done. It uses a variant of p5.js and also a variant on function definition.

Functions as values

This is a good place to describe the alternative syntax for function definition used by Khan Academy and others.

Recall that we define functions as follows:

function area() { // compute area of canvas
    return width * height;
}

This gives the appearance that “area” is something special — a function — but at some level, “area” is just a global variable, like “width” or “height,” only its value is a function. It may seem odd that a function can be a value. What does that mean? If a function is a value, you should be able to assign it. You can!

var canvasArea = area;

Now, canvasArea is another name for area, and we can call it with canvasArea(). Notice we did not write:

var canvasArea = area();

By putting the “()” after area, we’re saying “call the area function and use the return value (in this case, a number), so in this case, canvasArea would be set to the area of the canvas, not the function that computes the area of the canvas.

Anonymous Functions

The syntax used by Khan Academy (and many others) for function definition looks like this:

var area = function() {
    return width * height;
}

In this expression, function() — and notice the important parentheses — means “create a new function as follows.” This is an expression that returns a function (which is a value). It does not give the function a name, so it is an anonymous function. The var area = part assigns the function value to a global variable, area, which has the same effect as our familiar style of function definition: function area() { ... }.

Note: When you write an anonymous function expression, you can also add parameters as you would expect, e.g. function(x, y) { ... }.

Syntactic Sugar

Languages often introduce special syntax to make things look pretty when no special syntax is needed. JavaScript does not really need the syntax we use for function definition. All you need as variable declarations and anonymous functions. The addition of a “prettier” form of function declaration is often called “syntactic sugar.” Another example might be “+=”. Instead of a += 2; we can always write a = a + 2, so “+=” does not add any new capability.

More Objects

Here is some code based on the Khan Academy “Challenge: Flower Grower” example. Click to “grow” the flowers.

base

function Tulip(x, y, height) {
    this.x = x;
    this.y = y;
    this.height = height;
    this.draw = function() {
        noStroke();
        fill(16, 122, 12);
        rect(this.x, this.y, 10, -this.height);

        // petals
        fill(255, 0, 0); // red
        var y = this.y - this.height;
        ellipse(this.x+5, y, 44, 44);
        triangle(this.x-16, y, this.x+20, y, this.x-20, y - 31);
        triangle(this.x-14, y, this.x+24, y, this.x+3, y - 39);
        triangle(this.x+-4, y, this.x+26, y, this.x+29, y - 36);
    };

    this.growBy = function(amount) {
        this.height += amount;
    }
};

function Sunflower(x, y, height) {
    this.x = x;
    this.y = y;
    this.height = height;

    this.draw = function() {
        noStroke();
        fill(16, 122, 12);
        rect(this.x, this.y, 10, -this.height);
    
        // petals
        stroke(0, 0, 0);
        fill(255, 221, 0); // yellow
        // var y = 
        ellipse(this.x-10, this.y-this.height, 20, 18);
        ellipse(this.x+5, this.y-this.height-15, 20, 18);
        ellipse(this.x+5, this.y-this.height+15, 20, 18);
        ellipse(this.x+20, this.y-this.height, 20, 18);
        fill(20, 20, 20); // dark center
        ellipse(this.x+5, this.y-this.height, 20, 20);
    };

    this.growBy = function(amount) {
        this.height += amount;
    }
};


var tulip;
var sunflower;

function setup() {
    createCanvas(400, 400);
    tulip = new Tulip(38, 390, 150);
    sunflower = new Sunflower(186, 390, 100);
}

function draw() {
    background(207, 250, 255);
    tulip.draw();
    sunflower.draw();
};

function mousePressed() {
    tulip.growBy(5);
    sunflower.growBy(10);
}

Using this

Note the use of this, e.g. this.y or this.height to reference properties of the object inside functions draw and growBy.

Common subexpressions

The Sunflower draw function computes this.y-this.heigth a total of 5 times! In Khan Academy’s version, Tulip’s draw used the expression 10 times! Notice, in Tulip’s draw function, I’ve defined a new variable, y = this.y - this.height. Now, I can rewrite the code in terms of y, saving a lot of typing and resulting in much nicer code.

Suggestion: do the same for Tulip. I’ve included // var y = to suggest how and where to make this change.

Methods vs. Functions

The functions draw and growBy are properties of flower objects, so we call them using “dot” notation, e.g. tulip.draw(). A function associated with an object or class of objects is conventionally called a method. (You may also hear the term member function.)

Why methods? Why not just write a function? Yes, we could define tulipDraw(tulip) and sunflowerDraw(sunflower) and accomplish the same thing. The advantage of using methods is that with methods we can write:
flower.draw() where flower is a variable containing either a tulip or a sunflower. The alternative is to figure out which function to call:

if (isTulip(flower)) {
    tulipDraw(flower);
} else if (isSunflower(flower)) {
    sunflowerDraw(flower);
}

Creating a New Class of Flowers

Here is a new version with a new flower type, Violet.

violet

function Tulip(x, y, height) {
    this.x = x;
    this.y = y;
    this.height = height;
    this.draw = function() {
        noStroke();
        fill(16, 122, 12);
        rect(this.x, this.y, 10, -this.height);

        // petals
        fill(255, 0, 0); // red
        var y = this.y - this.height;
        ellipse(this.x+5, y, 44, 44);
        triangle(this.x-16, y, this.x+20, y, this.x-20, y - 31);
        triangle(this.x-14, y, this.x+24, y, this.x+3, y - 39);
        triangle(this.x+-4, y, this.x+26, y, this.x+29, y - 36);
    };

    this.growBy = function(amount) {
        this.height += amount;
    }
};

function Sunflower(x, y, height) {
    this.x = x;
    this.y = y;
    this.height = height;

    this.draw = function() {
        noStroke();
        fill(16, 122, 12);
        rect(this.x, this.y, 10, -this.height);
    
        // petals
        stroke(0, 0, 0);
        fill(255, 221, 0); // yellow
        // var y = 
        ellipse(this.x-10, this.y-this.height, 20, 18);
        ellipse(this.x+5, this.y-this.height-15, 20, 18);
        ellipse(this.x+5, this.y-this.height+15, 20, 18);
        ellipse(this.x+20, this.y-this.height, 20, 18);
        fill(20, 20, 20); // dark center
        ellipse(this.x+5, this.y-this.height, 20, 20);
    };

    this.growBy = function(amount) {
        this.height += amount;
    }
};

function Violet(x, y, height) {
    this.x = x;
    this.y = y;
    this.height = height;

    this.draw = function() {
        noStroke()
        fill(16, 122, 12);
        rect(this.x, this.y, 10, -this.height);
    
        // petals -- rotate an ellipse
        stroke(0, 0, 0);
        fill(73, 92, 160);
        push();
        translate(this.x + 5, this.y - this.height);
        for (var i = 0; i < 5; i++) {
            ellipse(0, 15, 20, 30);
            rotate(radians(360/5));
        }
        fill(255, 221, 0); // yellow
        ellipse(0, 0, 15, 15);
        pop();
    };

    this.growBy = function(amount) {
        this.height += amount;
    }
};



var tulip;
var sunflower;
var violet;

function setup() {
    createCanvas(400, 400);
    tulip = new Tulip(38, 390, 150);
    sunflower = new Sunflower(186, 390, 100);
    violet = new Violet(250, 390, 125);
}

function draw() {
    background(207, 250, 255);
    tulip.draw();
    sunflower.draw();
    violet.draw();
};

function mousePressed() {
    tulip.growBy(5);
    sunflower.growBy(10);
    violet.growBy(8);
}

In Violet’s draw function, I tried to improve on the style of the other draw methods. The other methods drew a lot of detail at this.x and this.y - this.height. Thus the flower was “translated” in x and y by adding these x and y offsets. I thought it would be nicer to just use translate to make the origin (0, 0) be the center of the flower. Then, since flower petals have radial symmetry, let’s use rotate inside a loop to draw each petal. (Originally, I drew 4 petals, and it was quite gratifying to simply change the rotation angle and the loop count in order to draw 5 petals, which look much nicer.)

By now, you should be able to make another object, say a house or a dog, with position, size, and other properties and a draw method.

An Array of Flowers

This is getting a little tedious, with an explicit draw call to each flower on the canvas. Why don’t we just put everything to draw in an array and iterate through the array, drawing each object there?

Think about what you would change in the previous code example to use an array of flowers. See if your imagined changes match the code below.

array

function Tulip(x, y, height) {
    this.x = x;
    this.y = y;
    this.height = height;
    this.draw = function() {
        noStroke();
        fill(16, 122, 12);
        rect(this.x, this.y, 10, -this.height);

        // petals
        fill(255, 0, 0); // red
        var y = this.y - this.height;
        ellipse(this.x+5, y, 44, 44);
        triangle(this.x-16, y, this.x+20, y, this.x-20, y - 31);
        triangle(this.x-14, y, this.x+24, y, this.x+3, y - 39);
        triangle(this.x+-4, y, this.x+26, y, this.x+29, y - 36);
    };

    this.growBy = function(amount) {
        this.height += amount;
    }
};

function Sunflower(x, y, height) {
    this.x = x;
    this.y = y;
    this.height = height;

    this.draw = function() {
        noStroke();
        fill(16, 122, 12);
        rect(this.x, this.y, 10, -this.height);
    
        // petals
        stroke(0, 0, 0);
        fill(255, 221, 0); // yellow
        // var y = 
        ellipse(this.x-10, this.y-this.height, 20, 18);
        ellipse(this.x+5, this.y-this.height-15, 20, 18);
        ellipse(this.x+5, this.y-this.height+15, 20, 18);
        ellipse(this.x+20, this.y-this.height, 20, 18);
        fill(20, 20, 20); // dark center
        ellipse(this.x+5, this.y-this.height, 20, 20);
    };

    this.growBy = function(amount) {
        this.height += amount;
    }
};

function Violet(x, y, height) {
    this.x = x;
    this.y = y;
    this.height = height;

    this.draw = function() {
        noStroke()
        fill(16, 122, 12);
        rect(this.x, this.y, 10, -this.height);
    
        // petals -- rotate an ellipse
        stroke(0, 0, 0);
        fill(73, 92, 160);
        push();
        translate(this.x + 5, this.y - this.height);
        for (var i = 0; i < 5; i++) {
            ellipse(0, 15, 20, 30);
            rotate(radians(360/5));
        }
        // var y = 
        fill(255, 221, 0); // yellow
        ellipse(0, 0, 15, 15);
        pop();
    };

    this.growBy = function(amount) {
        this.height += amount;
    }
};


var tulip;
var sunflower;
var violet;
var flowers = []; // an empty array

function setup() {
    createCanvas(400, 400);
    flowers.push(new Tulip(38, 390, 150));
    flowers.push(new Sunflower(186, 390, 100));
    flowers.push(new Violet(250, 390, 125));
}

function draw() {
    background(207, 250, 255);
    for (var i = 0; i < flowers.length; i++) {
        flowers[i].draw();
    }
};

function mousePressed() {
    for (var i = 0; i < flowers.length; i++) {
        flowers[i].growBy(5);
    }
}

What changed? I made a flowers array and initialized it to an empty array []. I used flowers.push() to insert new flowers at the end of the array. Then, I wrote a for loop to call the draw method on every element of the array and another for loop to call growBy in mousePressed. (I did not implement a different growth amount for each flower.)

Challenge: How would you give each flower a different amount to grow by when the mouse is pressed?

Many Flowers — Random Acts of Violets

In this example, I removed the initialization that creates 3 flowers, and I add code to create a new random flower each time a key is pressed.

Think about how you would do this before reading the code.

many

function Tulip(x, y, height) {
    this.x = x;
    this.y = y;
    this.height = height;
    this.draw = function() {
        noStroke();
        fill(16, 122, 12);
        rect(this.x, this.y, 10, -this.height);

        // petals
        fill(255, 0, 0); // red
        var y = this.y - this.height;
        ellipse(this.x+5, y, 44, 44);
        triangle(this.x-16, y, this.x+20, y, this.x-20, y - 31);
        triangle(this.x-14, y, this.x+24, y, this.x+3, y - 39);
        triangle(this.x+-4, y, this.x+26, y, this.x+29, y - 36);
    };

    this.growBy = function(amount) {
        this.height += amount;
    }
};

function Sunflower(x, y, height) {
    this.x = x;
    this.y = y;
    this.height = height;

    this.draw = function() {
        noStroke();
        fill(16, 122, 12);
        rect(this.x, this.y, 10, -this.height);
    
        // petals
        stroke(0, 0, 0);
        fill(255, 221, 0); // yellow
        // var y = 
        ellipse(this.x-10, this.y-this.height, 20, 18);
        ellipse(this.x+5, this.y-this.height-15, 20, 18);
        ellipse(this.x+5, this.y-this.height+15, 20, 18);
        ellipse(this.x+20, this.y-this.height, 20, 18);
        fill(20, 20, 20); // dark center
        ellipse(this.x+5, this.y-this.height, 20, 20);
    };

    this.growBy = function(amount) {
        this.height += amount;
    }
};

function Violet(x, y, height) {
    this.x = x;
    this.y = y;
    this.height = height;

    this.draw = function() {
        noStroke()
        fill(16, 122, 12);
        rect(this.x, this.y, 10, -this.height);
    
        // petals -- rotate an ellipse
        stroke(0, 0, 0);
        fill(73, 92, 160);
        push();
        translate(this.x + 5, this.y - this.height);
        for (var i = 0; i < 5; i++) {
            ellipse(0, 15, 20, 30);
            rotate(radians(360/5));
        }
        // var y = 
        fill(255, 221, 0); // yellow
        ellipse(0, 0, 15, 15);
        pop();
    };

    this.growBy = function(amount) {
        this.height += amount;
    }
};


var tulip;
var sunflower;
var violet;
var flowers = []; // an empty array

function setup() {
    createCanvas(400, 400);
//    flowers.push(new Tulip(38, 390, 150));
//    flowers.push(new Sunflower(186, 390, 100));
//    flowers.push(new Violet(250, 390, 125));
}

function draw() {
    background(207, 250, 255);
    noStroke();
    fill(0);
    text("Press key for acts of random violets.", 10, 20);
    for (var i = 0; i < flowers.length; i++) {
        flowers[i].draw();
    }
};

function mousePressed() {
    for (var i = 0; i < flowers.length; i++) {
        flowers[i].growBy(5);
    }
}

function keyPressed() {
    // pick a random flower maker
    var AllFlowers = [Tulip, Sunflower, Violet];
    var index = floor(random(AllFlowers.length));
    var Flower = AllFlowers[index];
    flowers.push(new Flower(random(width), height - random(100), 90 + random(50)));
}

Choosing a Random Element

In keyPressed, we choose a flower to create. There are many ways to do this. I chose a method based on a p5.js example. Here, the choices are in an array, so we make an array with the choices:

var AllFlowers = [Tulip, Sunflower, Violet];

Notice that the choices are all functions!

Next we pick a random number between 0 and the length of the array. The number returned by random is a floating point (fractional) number, but we need an integer to serve as an array index, so we use the floor function, which rounds down to the nearest integer. (Don’t round to the nearest integer or round up because the result could be AllFlowers.length, which is greater than the last valid index. Recall than the valid index values go from 0 through length-1.)

var index = floor(random(AllFlowers.length));

Finally, we get the random flower function from the array:

var Flower = AllFlowers[index];

Even though this is truly a variable and the value of the variable is randomly determined, we know it is a function, and we can call it using new Flower(...) to make a new flower. The new flower is immediately pushed onto the end of the flowers array:

flowers.push(new Flower(random(width), height - random(100), 90 + random(50)));

Challenge: Modify the code to remove flowers one-by-one. How would you remove the oldest flower? How would you remove the youngest flower?

Infrequent Events

Here’s an interesting technique for making things happen infrequently and randomly. The condition choose a random number from 0 to 1. If the number is less than 0.01, the “unusual thing” happens (here, we just draw an ellipse). The probability is 0.01. You can of course use a larger number to make the “unusual thing” more frequent, or a smaller number to make the thing less frequent.

sketch

function setup() {
    createCanvas(100, 100);
    frameRate(10);
}

function draw() {
    background(200);
    if (random(1) < 0.01) {
        fill(250, 250, 0);
        ellipse(50, 50, 40, 40);
    }
}

October 12

Making Choices with Weights

Let’s extend the previous example. Now the challenge is this: Choose a flower according to a set of probabilities. These are sometimes called weights, as in “give more weight to some choices.” To make a selection with weighted probability, consider the following picture (credit: Eli Bendersky — eli.thegreenspace.net):

subweight

Here, w0 through w5 represent weights. First, we arrange intervals of size w0 through w5 along a line. Next, we pick a random location along the line. It should be clear that the chances of picking any particular interval is proportional to the size of the interval. Thus, we have weighted selection!

Now, what should the algorithm be? First, we pick a random number from 0 to 100 (let’s assume the weights are percents and add up to 100). Now, what interval does that number fall into? If it is less than w0, it falls into the first interval. If not, but it is less than w0 + w1, it falls into the second interval. Generalizing this to more intervals, we’ll just keep a running sum of interval sizes from w0 to wn (for each value of n). When the sum exceeds our random number, we’ve found our interval. Here’s a new version in which I’ve compressed all the code except for the Flower selection code. (I do not recommend this for any JavaScript, but here I want to direct your attention to changes, and you can find the rest nicely formatted above.) This version also prints the number of each type of flower and the percentage so you can see that the weighted selection really picks different numbers of each flower.

weights

function Tulip(x,y,height){this.x=x;this.y=y;this.height=height;
this.draw=function(){noStroke();fill(16,122,12);rect(this.x,this.y,
10,-this.height);fill(255,0,0);var y=this.y-this.height;ellipse(
this.x+5,y,44,44);triangle(this.x-16,y,this.x+20,y,this.x-20,y-31);
triangle(this.x-14,y,this.x+24,y,this.x+3,y-39);triangle(this.x+-4,
y,this.x+26,y,this.x+29,y-36);};this.growBy=function(amount){
this.height+=amount;}};
function Sunflower(x,y,height){this.x=x;this.y=y;this.height=
height;this.draw=function(){noStroke();fill(16,122,12);rect(this.x,
this.y,10,-this.height);stroke(0,0,0);fill(255,221,0);ellipse(
this.x-10,this.y-this.height,20,18);ellipse(this.x+5,this.y-
this.height-15,20,18);ellipse(this.x+5,this.y-this.height+15,20,18);
ellipse(this.x+20,this.y-this.height,20,18);fill(20,20,20);ellipse(
this.x+5,this.y-this.height,20,20);};this.growBy=function(amount){
this.height+=amount;}};
function Violet(x,y,height){this.x=x;this.y=y;this.height=height;
this.draw=function(){noStroke();fill(16,122,12);rect(this.x,this.y,
10,-this.height);stroke(0,0,0);fill(73,92,160);push();translate(
this.x+5,this.y-this.height);for(var i=0;i<5;i++){ellipse(0,15,
20,30);rotate(radians(360/5));}fill(255,221,0);ellipse(0,0,15,15);
pop();};this.growBy=function(amount){this.height+=amount;}};
var tulip;var sunflower;var violet;var flowers=[];
function setup(){createCanvas(400,400);frameRate(10);}
function draw(){background(207,250,255);noStroke();fill(0);
textSize(12);text(
"Press key for acts of random violets.",10,20);for(var i=0;i<
flowers.length;i++){flowers[i].draw();}
    noStroke();
    textSize(20);
    fill(0);
    var sum = 0;
    for (var i = 0; i < counts.length; i++) {
        text(counts[i], 10, 45 + 25 * i);
        sum = sum + counts[i];
    }
    for (var i = 0; i < counts.length; i++) {
        text((counts[i] * 100 / sum).toFixed(2) + "%", 50, 45 + 25 * i);
    }
};
function mousePressed(){for(var i=0;i<flowers.length;i++){
flowers[i].growBy(5);}}

var counts = [0, 0, 0];

function keyPressed() {
    // pick a random flower maker
    var AllFlowers = [Tulip, Sunflower, Violet];
    // We want to pick 10% Tulip, 20% Sunflower, 70% Violet
    var weights = [10, 20, 70]; // weights in percent
    // We'll pick a number from 0 to 100. If the number is in the range
    // 0-10, pick Tulip, if 10-30, pick Sunflower, if 30-100, pick Violet
    var percent = floor(random(100));
    var threshold = 0;
    var Flower = Violet; // default value in case percents don't add to 100
    for (var i = 0; i < weights.length; i++) {
        threshold += weights[i];
        if (percent < threshold) {
            Flower = AllFlowers[i];
            counts[i]++;
            break;
        }
    }
    flowers.push(new Flower(random(width), height - random(100), 90 + random(50)));
}

In the code, threshold keeps the running total of weights[i].

The break Statement

Important: When the interval (flower) is found, we need to stop the loop. Once we find that the random number (percent) is less than the running sum (threshold), then of course the condition will hold for the next iteration when threshold is even bigger, and the next iteration after that, etc. So the problem is we have to “break” out of the loop.

There are several ways to do that:

  1. In this example, we use the break command. The break command exits the innermost loop, resuming execution at the next statement after the loop, which is the flowers.push(...) statement.
  2. Alternatively, we could perform the flowers.push(...) immediately inside the if statement (in place of break). Then, we could simply exit the entire function using a return; statement. (There is no need to return a value from keyPressed, so it is legal to write return; rather than returning a value, such as return true;).
  3. Another approach will be shown below: Use a while loop.

The continue Statement

Sometimes, you want to skip the rest of the body of a loop and resume the loop at its next iteration. The continue statement does this. Every blue moon, continue is very handy and can make code more readable than bracketing the rest of the loop body with an if. You can read about continue, and it’s good to know about, but I don’t have a compelling example, so we will not dwell on this now.

While Loops

A while loop is actually simpler than for loops. We introduced looping with for loops because for loops are best for looping a certain number of times, and there’s a fairly standard form to loop 10 times or loop N times. The while loop is used to loop until some condition becomes true. We’ll use a while loop to iterate until threshold is greater than percent — a nice and direct expression of our random weighted selection algorithm.

while

function Tulip(x,y,height){this.x=x;this.y=y;this.height=height;
this.draw=function(){noStroke();fill(16,122,12);rect(this.x,this.y,
10,-this.height);fill(255,0,0);var y=this.y-this.height;ellipse(
this.x+5,y,44,44);triangle(this.x-16,y,this.x+20,y,this.x-20,y-31);
triangle(this.x-14,y,this.x+24,y,this.x+3,y-39);triangle(this.x+-4,
y,this.x+26,y,this.x+29,y-36);};this.growBy=function(amount){
this.height+=amount;}};
function Sunflower(x,y,height){this.x=x;this.y=y;this.height=
height;this.draw=function(){noStroke();fill(16,122,12);rect(this.x,
this.y,10,-this.height);stroke(0,0,0);fill(255,221,0);ellipse(
this.x-10,this.y-this.height,20,18);ellipse(this.x+5,this.y-
this.height-15,20,18);ellipse(this.x+5,this.y-this.height+15,20,18);
ellipse(this.x+20,this.y-this.height,20,18);fill(20,20,20);ellipse(
this.x+5,this.y-this.height,20,20);};this.growBy=function(amount){
this.height+=amount;}};
function Violet(x,y,height){this.x=x;this.y=y;this.height=height;
this.draw=function(){noStroke();fill(16,122,12);rect(this.x,this.y,
10,-this.height);stroke(0,0,0);fill(73,92,160);push();translate(
this.x+5,this.y-this.height);for(var i=0;i<5;i++){ellipse(0,15,
20,30);rotate(radians(360/5));}fill(255,221,0);ellipse(0,0,15,15);
pop();};this.growBy=function(amount){this.height+=amount;}};
var tulip;var sunflower;var violet;var flowers=[];
function setup(){createCanvas(400,400);frameRate(10);}
function draw(){background(207,250,255);noStroke();fill(0);
textSize(12);text(
"Press key for acts of random violets.",10,20);for(var i=0;i<
flowers.length;i++){flowers[i].draw();}
    noStroke();
    textSize(20);
    fill(0);
    var sum = 0;
    for (var i = 0; i < counts.length; i++) {
        text(counts[i], 10, 45 + 25 * i);
        sum = sum + counts[i];
    }
    for (var i = 0; i < counts.length; i++) {
        text((counts[i] * 100 / sum).toFixed(2) + "%", 50, 45 + 25 * i);
    }
};
function mousePressed(){for(var i=0;i<flowers.length;i++){
flowers[i].growBy(5);}}

var counts = [0, 0, 0];

function keyPressed() {
    // pick a random flower maker
    var AllFlowers = [Tulip, Sunflower, Violet];
    // We want to pick 10% Tulip, 20% Sunflower, 70% Violet
    var weights = [10, 20, 70]; // weights in percent
    // We'll pick a number from 0 to 100. If the number is in the range
    // 0-10, pick Tulip, if 10-30, pick Sunflower, if 30-100, pick Violet
    var percent = floor(random(100));
    var index = 0;
    var threshold = weights[index];
    while (percent >= threshold) {
        index = index + 1;
        threshold += weights[index];
    }
    var Flower = AllFlowers[index];
    counts[index]++;
    flowers.push(new Flower(random(width), height - random(100), 90 + random(50)));
}

With a while loop, there is no “initialization” part as in for loops, so you must be careful to set up variables before the while. We initialize index to zero, then initialize threshold to weights[index]. That way, if percent is less than threshold the while loop body will not execute even once, and we will continue with index = 0.

It’s usually a good idea to consider what happens if the while loop executes zero times and use this to your advantage.

Once the “zero” case is handled, fill in the loop body to consider each succeeding case. For weighted selection, we move to the next case by incrementing index to advance to the next weight, and adding weights[index] to threshold to get ready for the next comparison.

When a for loop exits, we usually do not use the loop variable(s). When a while loop exits, we usually need the variables updated inside the while loop body. Note that in this case the whole purpose of the while loop is to determine the final value of index, which is used after the loop in the expressions AllFlowers[index] and counts[index]++.

splice

See this online tutorial to learn about splice.

Converting a Program into an Object

This example is intended to give some perspective and insights into objects. When we started programming, we declared global variables and operated on them with functions. Let’s make a little program in that style.

simple

var ballX = 50;
var ballY = 50;

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

function drawBall() {
    ballX += random(-2, 2);
    ballY += random(-2, 2);
    fill(0, 222, 0);
    ellipse(ballX, ballY, 50, 50);
}

function draw() {
    background(255,250,180);
    drawBall();
};

This “works” but as you add more things to your program, it gets cluttered with unrelated global variables. Objects let us put essentially all the action here into one place:

object

var ball;

function Ball() {
    this.ballX = 50;
    this.ballY = 50;
    this.draw = function() {
        this.ballX += random(-2, 2);
        this.ballY += random(-2, 2);
        fill(0, 222, 0);
        ellipse(this.ballX, this.ballY, 50, 50);
    }
}
        
function setup() {
    createCanvas(200,200);frameRate(10);
    frameRate(10);
    ball = new Ball();
}

function draw() {
    background(255,250,180);
    ball.draw();
};

Notice that almost the whole of the previous program is “encapsulated” into the Ball object: it contains the variables ballX and ballY, and the draw function. The only global now is the ball object. Things are more organized. We can make a “face” with two balls as eyes:

face

var left, right;

function Ball() {
    this.ballX = 50;
    this.ballY = 50;
    this.draw = function() {
        this.ballX += random(-2, 2);
        this.ballY += random(-2, 2);
        fill(0, 222, 0);
        ellipse(this.ballX, this.ballY, 50, 50);
    }
}
        
function setup() {
    createCanvas(200,200);frameRate(10);
    frameRate(10);
    left = new Ball();
    right = new Ball();
    right.ballX = 150;
}

function draw() {
    background(255,250,180);
    fill(100);
    ellipse(100, 80, 200, 200);
    left.draw();
    right.draw();
};

Notice that with the object-oriented version of Ball, we do not have to duplicate lots of code to make two balls, and we do not clutter the code with many new global variables. In fact, if wanted to, we could make a Face object and put the “eye” Balls into Face properties — there’s no need to have global variables to represent the eyes.

Challenge: Clean up this code, making a Face object that creates Ball objects for two “eyes”. The Face draw method should call the draw method of each “eye.” If you do not understand how, give it a shot and post for help on Piazza.

The switch/case Statement

See examples and tutorial online.