From 453263812ed217fc5f0e9d634c83f03210c77f24 Mon Sep 17 00:00:00 2001 From: shiffman Date: Tue, 27 Feb 2024 14:57:20 +0000 Subject: [PATCH] Notion - Update docs --- content/01_vectors.html | 1 - content/02_forces.html | 28 ++++++++++++++-------------- content/04_particles.html | 8 ++------ 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/content/01_vectors.html b/content/01_vectors.html index 67431a1..8aa7d24 100644 --- a/content/01_vectors.html +++ b/content/01_vectors.html @@ -952,7 +952,6 @@ static add(v1, v2) {

When calling a static method, instead of referencing an object instance, you reference the name of the class. Here’s the right way to implement the vector addition example:

let v = createVector(0, 0);
 let u = createVector(4, 5);
-
 let w = v.add(u);
 let w = p5.Vector.add(v, u);

The p5.Vector class has static versions of add(), sub(), mult(), and div(). These static methods allow you to perform generic mathematical operations on vectors without changing the value of one of the input vectors in the process.

diff --git a/content/02_forces.html b/content/02_forces.html index 8287959..30a94ce 100644 --- a/content/02_forces.html +++ b/content/02_forces.html @@ -812,22 +812,22 @@ mover.applyForce(force); mover.show(); }

I’m almost there. Since I decided to put the attract() method inside the Attractor class, I still need to actually write that method. It should receive a Mover object and return a p5.Vector:

-
attract(m) {
-  // All the math
-  return ______________;
-}
+
  attract(m) {
+    // All the math
+    return ______________;
+  }

What goes inside the method? All of that nice math for gravitational attraction!

-
attract(mover) {
-  //{!1} What’s the force’s direction?
-  let force = p5.Vector.sub(this.position, mover.position);
-  let distance = force.mag();
-  // Calculate the strength of the attraction force.
-  let strength = (this.mass * mover.mass) / (distance * distance);
-  force.setMag(strength);
+
  attract(mover) {
+    //{!1} What’s the force’s direction?
+    let force = p5.Vector.sub(this.position, mover.position);
+    let distance = force.mag();
+    // Calculate the strength of the attraction force.
+    let strength = (this.mass * mover.mass) / (distance * distance);
+    force.setMag(strength);
 
-  // Return the force so it can be applied!
-  return force;
-}
+ // Return the force so it can be applied! + return force; + }

And I’m done. Sort of. Almost. I need to work out one small kink. Look at the code for the attract() method again. See that slash symbol for division? Whenever you have one of those, you should ask yourself this question: What would happen if the distance happened to be a really, really small number, or (even worse!) 0? You can’t divide a number by 0, and if you were to divide a number by something tiny like 0.0001, that’s the equivalent of multiplying that number by 10,000! That may be a viable outcome of this formula for gravitational attraction in the real world, but p5.js isn’t the real world. In the p5.js world, the mover could end up being very, very close to the attractor, and the resulting force could be so strong that the mover flies way off the canvas.

Conversely, what if the mover were to be, say, 500 pixels from the attractor (not unreasonable in p5.js)? You’re squaring the distance, so this will result in dividing the force by 250,000. That force might end up being so weak that it’s almost as if it’s not applied at all.

To avoid both extremes, it’s practical to constrain the range of distance before feeding it into the formula. Maybe, no matter where the Mover actually is, you should never consider it to be less than 5 pixels or more than 25 pixels away from the attractor, for the purposes of calculating the force of gravitational attraction:

diff --git a/content/04_particles.html b/content/04_particles.html index 3ab5da3..813cdb8 100644 --- a/content/04_particles.html +++ b/content/04_particles.html @@ -515,22 +515,18 @@ function setup() {

Imagine it’s a Saturday morning. You’ve just gone out for a lovely jog, had a delicious bowl of cereal, and are sitting quietly at your computer with a cup of warm chamomile tea. It’s your old friend so-and-so’s birthday, and you’ve decided you’d like to make a greeting card with p5.js. How about simulating some confetti? Purple confetti, pink confetti, star-shaped confetti, square confetti, fast confetti, fluttery confetti—all kinds of confetti, all with different appearances and different behaviors, exploding onto the screen all at once.

What you have is clearly a particle system: a collection of individual pieces (particles) of confetti. You might be able to cleverly redesign the Particle class to have variables that store color, shape, behavior, and more. To create a variety of particles, you might initialize those variables with random values. But what if some of your particles are drastically different? It could become very messy to have all sorts of code for different ways of being a particle in the same class. Another option might be to do the following:

class HappyConfetti {
-
 }
 
 class FunConfetti {
-
 }
 
 class WackyConfetti {
-
 }

This is a nice solution: create three classes to describe the different kinds of confetti that are part of your particle system. The Emitter constructor could then have some code to pick randomly from the three classes when filling the array (note that this probabilistic method is the same one I employed in the random walk examples in Chapter 0):

class Emitter {
   constructor(num) {
     this.particles = [];
-
     for (let i = 0; i < num; i++) {
       let r = random(1);
       // Randomly pick a kind of particle.
@@ -803,7 +799,7 @@ for (let animal of kingdom) {
 }

Let’s make this a bit more sophisticated. Say I want to have each Confetti particle rotate as it flies through the air. One option is to model angular velocity and acceleration, as described in Chapter 3. For ease, however, I’ll implement something less formal.

I know a particle has an x-position somewhere between 0 and the width of the canvas. What if I said that when the particle’s x-position is 0, its rotation should be 0; when its x-position is equal to the width, its rotation should be equal to 4\pi? Does this ring a bell? As discussed in Chapter 0, whenever a value has one range that you want to map to another range, you can use the map() function:

-
let angle = map(this.position.x, 0, width, 0, TWO_PI * 2);
+
    let angle = map(this.position.x, 0, width, 0, TWO_PI * 2);

Here’s how this code fits into the show() method:

  show() {
     let angle = map(this.position.x, 0, width, 0, TWO_PI * 2);
@@ -883,7 +879,7 @@ for (let animal of kingdom) {
     let f = force.copy();
     f.div(this.mass);
     this.acceleration.add(f);
-	}
+ }

Now that the Particle class is complete, I have an important question to ask: Where should I call the applyForce() method? Where in the code is it appropriate to apply a force to a particle? In my view, there’s no right or wrong answer; it really depends on the exact functionality and goals of a particular p5.js sketch. My quick-and-dirty solution in the previous examples was to create and apply a gravity force in the run() method of each particle: