Lab Week 09 - Fall 2023

Tuesday, October 31, 2023

Happy Halloween!


Introduction

Remember: As you work on these steps with guidance from your teaching assistant, you should look toward your neighbor to see if they need help (or ask if you need help). You can collaborate in lab during these activities!

In this lab, you will


Create a folder named lab-09 for this lab in your 15-104 folder/directory.

Here is a link to the p5.js template we are using to create the program.

template2023-p5only.zip


Problem A: Color Particles

Modify the particle system example done in class (Lecture 20) so that particles have a variety of colors and sizes.

  1. Since each particle has two new properties, size and color, modify the makeParticle function so that each particle has two more properties. Set the size to a random value between 5 and 15 and the color to a random color but not black or white). Note that each particle should have a random size and color but this should not change for each particle throughout the program run. In other words, your objects shouldn’t flash different colors or change sizes as they’re moving.
  2. Which other function(s) must be modified? Make the appropriate modifications and test your program.
For convenience, here’s a copy of the code:

var gravity = 0.3;   // downward acceleration
var spring = 0.7; // how much velocity is retained after bounce
var drag = 0.0001;    // drag causes particles to slow down
var np = 100;      // how many particles


function particleStep() {
    this.age++;
    this.x += this.dx;
    this.y += this.dy;
    if (this.x > width) { // bounce off right wall
        this.x = width - (this.x - width);
        this.dx = -this.dx * spring;
    } else if (this.x < 0) { // bounce off left wall
        this.x = -this.x;
        this.dx = -this.dx * spring;
    }
    if (this.y > height) { // bounce off bottom
        this.y = height - (this.y - height);
        this.dy = -this.dy * spring;
    } else if (this.y < 0) { // bounce off top
        this.y = -this.y;
        this.dy = -this.dy * spring;
    }
    this.dy = this.dy + gravity; // force of gravity
    // drag is proportional to velocity squared
    // which is the sum of the squares of dx and dy
    var vs = Math.pow(this.dx, 2) + Math.pow(this.dy, 2);
    // d is the ratio of old velocty to new velocity
    var d = vs * drag;
    // d goes up with velocity squared but can never be
    // so high that the velocity reverses, so limit d to 1
    d = min(d, 1);
    // scale dx and dy to include drag effect
    this.dx *= (1 - d);
    this.dy *= (1 - d);
}


function particleDraw() {
    point(this.x, this.y);
}


// create a "Particle" object with position and velocity
function makeParticle(px, py, pdx, pdy) {
    p = {x: px, y: py,
         dx: pdx, dy: pdy,
         age: 0,
         stepFunction: particleStep,
         drawFunction: particleDraw
        }
    return p;
}

var particles = [];

function setup() {
    createCanvas(400, 400);
    for (var i = 0; i < np; i++) {
        // make a particle
        var p = makeParticle(200, 200,
                             random(-50, 50), random(-50, 50));
        // push the particle onto particles array
        particles.push(p);
    }
    frameRate(10);
}


// draw all particles in the particles array
//
function draw() {
    background(230);
    stroke(0);
    strokeWeight(10);

    if (mouseIsPressed) {
        var newp = makeParticle(mouseX, mouseY,
                                random(-10, 10), random(-10, 0));
        particles.push(newp);
    }

    // newParticles will hold all the particles that we want to
    // retain for the next call to draw() -- we will retain particles
    // if the age is < 200 (frames). Initially, newParticle is empty
    // because we have not found any "young" particles yet.
    newParticles = [];
    for (var i = 0; i < particles.length; i++) { // for each particle
        var p = particles[i];
        p.stepFunction();
        p.drawFunction();
        // since we are "looking" at every particle in order to
        // draw it, let's use the opportunity to see if particle[i]
        // is younger than 200 frames. If so, we'll push it onto the
        // end of newParticles.
        if (p.age < 200) {
            newParticles.push(p);
        }
    }
    // now, newParticles has EVERY particle with an age < 200 frames.
    // these are the particles we want to draw next time, so assign
    // particles to this new array. The old value of particles, i.e.
    // the entire array, is simply "lost" -- Javascript will reclaim
    // and reuse the memory since that array is no longer needed.
    particles = newParticles;
}

Here is one frame of a possible solution:

one frame of the color particles example


Problem B: Shrinking Particles

Copy the program from the previous problem into a new project. In this exercise, you will decrease the size of the particle as it gets older. After the particle ages every 40 steps, shrink the size of the particle by 20%. Think: where in the code would you make this update?

Test your program to make sure it works correctly. (You may want to reduce the number of particles so you can see what’s happening more easily.)


Problem C: Repelling Particles

Let (rpx, rpy) represent a location that repels particles. The value rpc represents a fixed, constant factor that you should adjust to get the right “look”: if rpc is too small, particles will not be noticeably affected by the force. If rpc is extremely large, particles will all be immediately pushed away, possibly “running” to the corners to “get away.”

  1. Copy the original mouse particles program into a new project. You don’t need the colors and sizes this time. In this new project, comment out (or remove) the code in particleStep that deals with drag and gravity. You don’t need them for this problem.
  2. Add global variables for rpx, rpy, and rpc. Set rpx = 200, rpy = 100, and try rpc = 1000 for now. You can change it later.
  3. In the particleStep function, after you compute the location of the particle after a possible bounce, compute the effect of the repeller on the particle. To implement repulsion, you want to accelerate away from (rpx, rpy) by the repulsive force. Let’s say the distance from the particle to the repeller is dp (use the dist function to compute dp) and the repelling force is:
    f = rpc / (Math.pow(dp, 2))
    Then the direction of the force is: dirx = (x – rpx) / dp ,
    diry = (y – rpy) / dp .
    Add f * dirx to the x coordinate of the particle, and add f * diry to the y coordinate of the particle.
  4. In the draw function, draw a small red square at the location (rpx, rpy) to give the impression that particles are being repelled by the object.
  5. Between runs of your program, modify rpc until you see the particles being repelled from the square.

Repelling particles example

This challenge is inspired by “Example 4.7: ParticleSystem with repeller” from Chapter 4 of Daniel Shiffman, The Nature of Code.


Problem D: Particles and Springs (no handin required for this task)

Review the implementation of the particles and springs arrays in the Try This exercise of Lecture 21. (Sample code is given in the Lecture 21 zip file on Canvas.) Be sure you understand how the code that initializes the two arrays leads to the initial particle/spring configuration on the canvas.


Once you run out of time, you should submit your final completed work for credit.