diff --git a/content/00_5_introduction.html b/content/00_5_introduction.html index 4641c47..eb76440 100644 --- a/content/00_5_introduction.html +++ b/content/00_5_introduction.html @@ -42,6 +42,7 @@

You draw a shape at position x. With each frame of animation, you increment the value of x, redraw the shape, and voilà—the illusion of motion! Maybe you took it a step or two further and included a y position, as well as variables for speed along the x- and y-axes:

x = x + xspeed;
 y = y + yspeed;
+

Part 1 of this story will take this idea even further. After exploring how to use different flavors of randomness to drive an object’s motion (Chapter 0), I’m going to take these xspeed and yspeed variables and demonstrate how together they form a vector (Chapter 1). You won’t get any new functionality out of this, but it will build a solid foundation for programming motion in the rest of the book.

Once you know a little something about vectors, you’re going to quickly realize that a force (Chapter 2) is a vector. Kick a soccer ball and you’re applying a force. What does a force cause an object to do? According to Sir Isaac Newton, force equals mass times acceleration, so that force causes an object to accelerate. Modeling forces will allow you to create systems with dynamic motion, in which objects move according to a variety of rules.

Now, that soccer ball to which you applied a force might have also been spinning. If an object moves according to its linear acceleration, it can spin according to its angular acceleration (Chapter 3). Understanding the basics of angles and trigonometry will allow you to model rotating objects as well as grasp the principles behind oscillating motion, like a pendulum swinging or a spring bouncing.

Once you’ve tackled the basics of motion and forces for an individual inanimate object, I’ll show you how to make thousands upon thousands of those objects and manage them as a single unit called a particle system (Chapter 4). Particle systems are also a good excuse to look at some additional features of object-oriented programming—namely, inheritance and polymorphism.

diff --git a/content/04_particles.html b/content/04_particles.html index 78d7042..66ae2c6 100644 --- a/content/04_particles.html +++ b/content/04_particles.html @@ -676,9 +676,9 @@ class Cat extends Animal { } eat() { - // Call eat() from Animal. A child can execute a function from the parent while adding its own code. + // Call eat() from Animal. A child can execute a function from the parent. super.eat(); - // Add some additional code for a dog’s specific eating characteristics. + // Additional code for a dog’s specific eating characteristics. print("Woof!!!"); } @@ -1139,10 +1139,10 @@ class Repeller { } repel(particle) { - // {!1} + // {!2} let force = p5.Vector.sub(this.position, particle.position); - // {.continue.bottom-align} This is the same repel algorithm from Chapter 2: forces based on gravitational attraction. let distance = force.mag(); + // {.continue.bottom-align} This is the same repel algorithm from Chapter 2: forces based on gravitational attraction. distance = constrain(distance, 5, 50); let strength = -1 * this.power / (distance * distance); force.setMag(strength); @@ -1187,11 +1187,13 @@ class Repeller {

First, declare a variable to store the image:

let img;
-

Then, load the image in preload():

-
function preload() {
+
+

Then, load the image in preload():

+
function preload() {
   // Load the PNG.
   img = loadImage("texture.png");
 }
+

Next, when it comes time to draw the particle, use the img variable instead of drawing a circle or rectangle:

  show() {
     imageMode(CENTER);
diff --git a/content/05_steering.html b/content/05_steering.html
index 6745004..616d0aa 100644
--- a/content/05_steering.html
+++ b/content/05_steering.html
@@ -286,7 +286,6 @@ desired.setMag(this.maxspeed);
  arrive(target) {
     let desired = p5.Vector.sub(target, this.position);
-
     //{!1} The distance is the magnitude of
     // the vector pointing from
     // the position to the target.
@@ -531,9 +530,7 @@ let row = floor(this.position.y / this.resolution);
-
-
class Vehicle {
-  follow(flow) {
+
  follow(flow) {
     // What is the vector at that spot in the flow field?
     let desired = flow.lookup(this.position);
     desired.setMag(this.maxspeed);
@@ -542,7 +539,6 @@ let row = floor(this.position.y / this.resolution);
steer.limit(this.maxforce); this.applyForce(steer); }
-

Notice that lookup() is a method of the FlowField class, rather than of Vehicle. While you certainly could place lookup() within the Vehicle class instead, from my perspective, placing it in FlowField aligns best with the OOP principle of encapsulation. The lookup task, which retrieves a vector based on a position from the flow field, is inherently tied to the data of the FlowField object.

You may also notice some familiar elements from Chapter 4, such as the use of an array of vehicles. Although the vehicles here operate independently, this is a great first step toward thinking about the group behaviors that I’ll introduce later in this chapter.

@@ -948,7 +944,7 @@ for (let i = 0; i < path.points.length - 1; i++) {
  • Simple units operate in parallel. For every cycle through the draw() loop, each unit will calculate its own steering forces. This will create the appearance of all the units working in parallel.
  • Systems as a whole exhibit emergent phenomena. Complex behaviors, patterns, and intelligence can emerge from the interactions among simple units. This phenomenon occurs in nature, such as in ant colonies, migration patterns, earthquakes, and snowflakes. The question is whether the same results can be achieved in a p5.js sketch.
  • -

    Beyond these core principles, three additional qualities of complex systems will help frame the discussion, as well as provide guidelines for features to include in a software simulation. It’s important to acknowledge that this is a fuzzy set of characteristics, and not all complex systems have all of them:

    +

    Beyond these core principles, three additional qualities of complex systems will help frame the discussion, as well as provide guidelines for features to include in a software simulation. It’s important to acknowledge that this is a fuzzy set of characteristics, and not all complex systems have all
    of them:

    How do I write the code to make these connections for N particles? Look at the four columns illustrated in Figure 6.19. They iterate all the connections starting from particles 0 up to 3. This tells me that I need to access each particle in the list from 0 to N – 1:

    -
    +
        for (let i = 0; i < this.particles.length - 1; i++) {
           // Use the variable particle_i to store the particle reference.
           let particle_i = this.particles[i];
    @@ -1690,8 +1690,9 @@ let behavior = new AttractionBehavior(particle, distance, strength);

    Finally, in order for the force to be activated, the behavior needs to be added to the physics world:

    physics.addBehavior(behavior);

    Now everything that lives in the physics simulation will always be attracted to that particle, as long as it’s within the distance threshold.

    -

    The AttractionBehavior class is a very powerful tool. For example, even though Toxiclibs.js doesn’t automatically handle collisions like Matter.js does, you can create a collision-like simulation by adding an AttractionBehavior with a negative strength—a repulsive behavior—to each and every particle. If the force is strong and activated only within a short range (scaled to the particle’s radius), the result is much like a rigid-body collision. Here’s how to modify the Particle class to do this:

    -
    class Particle extends VerletParticle2D {
    +
    +

    The AttractionBehavior class is a very powerful tool. For example, even though Toxiclibs.js doesn’t automatically handle collisions like Matter.js does, you can create a collision-like simulation by adding an AttractionBehavior with a negative strength—a repulsive behavior—to each and every particle. If the force is strong and activated only within a short range (scaled to the particle’s radius), the result is much like a rigid-body collision. Here’s how to modify the Particle class to do this:

    +
    class Particle extends VerletParticle2D {
       constructor(x, y, r) {
         super(x, y);
         this.r = r;
    @@ -1708,6 +1709,7 @@ let behavior = new AttractionBehavior(particle, distance, strength);
    circle(this.x, this.y, this.r * 2); } }
    +

    I can now remake the attraction example from Chapter 2 with a single Attractor object that exerts an attraction behavior anywhere on the canvas. Even though the attractor is centered, I’m using a distance threshold of the full width to account for any movement of the attractor, and for particles located outside the canvas boundaries.

    Example 6.15: Attraction (and Repulsion) Behaviors