diff --git a/content/02_forces.html b/content/02_forces.html index 32b174b..3a4aaf2 100644 --- a/content/02_forces.html +++ b/content/02_forces.html @@ -1,7 +1,9 @@

Chapter 2. Forces

In the final example of Chapter 1, I demonstrated how to calculate a dynamic acceleration based on a vector pointing from a circle on the canvas to the mouse position. The resulting motion resembled a magnetic attraction between shape and mouse, as if some force was pulling the circle in toward the mouse. In this chapter, I’ll detail the concept of a force and its relationship to acceleration. The goal, by the end of this chapter, is to build a simple physics engine and understand how objects move around a canvas responding to a variety of environmental forces.

-

A physics engine is a computer program (or code library) that simulates the behavior of objects in a physical environment. In our case, the objects are two-dimensional shapes, and the environment is a rectangular canvas. Physics engines can be developed to be highly precise (requiring high-performance computing) or real-time (using simple and fast algorithms).

+
+

A physics engine is a computer program (or code library) that simulates the behavior of objects in a physical environment. In our case, the objects are two-dimensional shapes, and the environment is a rectangular canvas. Physics engines can be developed to be highly precise (requiring high-performance computing) or real-time (using simple and fast algorithms).

+

Forces and Newton’s Laws of Motion

Let’s begin by taking a conceptual look at what it means to be a force in the real world. Just like the word “vector,” the term “force” can have a variety of meanings. It can indicate a powerful physical intensity, as in “They pushed the boulder with great force,” or a powerful influence, as in “They’re a force to be reckoned with!” The definition of force that I’m interested in for this chapter is more formal and comes from Sir Isaac Newton’s three laws of motion:

A force is a vector that causes an object with mass to accelerate.

diff --git a/content/03_oscillation.html b/content/03_oscillation.html index b6b3357..4b69e5f 100644 --- a/content/03_oscillation.html +++ b/content/03_oscillation.html @@ -7,7 +7,7 @@

In Chapters 1 and 2, I carefully worked out an object-oriented structure to animate a shape in a p5.js canvas, using the concept of a vector to represent position, velocity, and acceleration driven by forces in the environment. I could move straight from here into topics such as particle systems, steering forces, group behaviors, and more. However, doing so would mean skipping a fundamental aspect of motion in the natural world: oscillation, or the back-and-forth movement of an object around a central point or position. In order to model oscillation, you’ll need to understand a little bit about trigonometry.

Trigonometry is the mathematics of triangles, specifically right triangles. Learning some trig will give you new tools to generate patterns and create new motion behaviors in a p5.js sketch. You’ll learn to harness angular velocity and acceleration to spin objects as they move. You’ll be able to use the sine and cosine functions to model nice ease-in, ease-out wave patterns. You’ll also learn to calculate the more complex forces at play in situations that involve angles, such as a pendulum swinging or a box sliding down an incline.

I’ll start the chapter with the basics of working with angles in p5.js, then cover several aspects of trigonometry. In the end, I’ll connect trigonometry with what you learned about forces in Chapter 2. What I cover here will pave the way for more sophisticated examples that require trig later in this book.

-

3.1 Angles

+

Angles

Before going any further, I need to make sure you understand what it means to be an angle in p5.js. If you have experience with p5.js, you’ve undoubtedly encountered this issue while using the rotate() function to rotate and spin objects.

You’re most likely to be familiar with the concept of an angle as measured in degrees (see Figure 3.1). A full rotation goes from 0 to 360 degrees, and 90 degrees (a right angle) is 1/4th of 360, shown in Figure 3.1 as two perpendicular lines.

@@ -36,7 +36,7 @@ rotate(radians(angle)); angleMode(DEGREES); rotate(angle); -

While degrees can be useful, for the purposes of this book, I’ll be working with radians because they’re the standard unit of measurement across many programming languages and graphics environments. If they're new to you, this is a good opportunity to practice! Additionally, if you aren’t familiar with how rotation is implemented in p5.js, I recommend watching my Coding Train video series on transformations in p5.js or reading Gene Kogan’s transformation tutorial at genekogan.com.

+

While degrees can be useful, for the purposes of this book, I’ll be working with radians because they’re the standard unit of measurement across many programming languages and graphics environments. If they're new to you, this is a good opportunity to practice! Additionally, if you aren’t familiar with how rotation is implemented in p5.js, I recommend watching my Coding Train video series on transformations in p5.js.

Exercise 3.1

Rotate a baton-like object (see below) around its center using translate() and rotate().

@@ -45,7 +45,7 @@ rotate(angle);
-

3.2 Angular Motion

+

Angular Motion

Another term for rotation is angular motion—that is, motion about an angle. Just as linear motion can be described in terms of velocity—the rate at which an object’s position changes—angular motion can be described in terms of angular velocity—that rate at which an object’s angle changes. By extension, angular acceleration describes changes in an object’s angular velocity.

Luckily, you already have all the math you need to understand angular motion. Remember the stuff I dedicated almost all of Chapters 1 and 2 to explaining

\overrightarrow{\text{velocity}} = \overrightarrow{\text{velocity}} + \overrightarrow{\text{acceleration}}
@@ -59,7 +59,9 @@ rotate(angle); rotate(angle); line(-60, 0, 60, 0); circle(60, 0, 16); -circle(-60, 0, 16, 16); +circle(-60, 0, 16, 16); + +angle = angle + 0.1;

Adding in the principles of angular motion, I can instead write the following example (the solution to Exercise 3.1).

Example 3.1: Angular Motion Using rotate()

@@ -71,9 +73,9 @@ circle(-60, 0, 16, 16);
// Position
 let angle = 0;
 // Velocity
-let aVelocity = 0;
+let angleVelocity = 0;
 //{!1} Acceleration
-let aAcceleration = 0.0001;
+let angleAcceleration = 0.0001;
 
 function setup() {
   createCanvas(640, 240);
@@ -94,18 +96,17 @@ function draw() {
   circle(-60, 0, 16);
 
   // Angular equivalent of velocity.add(acceleration);
-  aVelocity += aAcceleration;
+  angleVelocity += angleAcceleration;
   //{!1} Angular equivalent of position.add(velocity);
-  angle += aVelocity;
+  angle += angleVelocity;
 }
-

Instead of incrementing angle by a fixed amount to steadily rotate the baton, every frame I add aAcceleration to aVelocity, then add aVelocity to angle. As a result, the baton starts with no rotation, and then spins faster and faster as the angular velocity accelerates.

+

Instead of incrementing angle by a fixed amount to steadily rotate the baton, every frame I add angleAcceleration to angleVelocity, then add angleVelocity to angle. As a result, the baton starts with no rotation, and then spins faster and faster as the angular velocity accelerates.

Exercise 3.2

Add an interaction to the spinning baton. How can you control the acceleration with the mouse? Can you introduce the idea of drag, decreasing the angular velocity over time so the baton eventually comes to rest?

The logical next step is to incorporate this idea of angular motion into the Mover class. First, I need to add some variables to the class’s constructor.

class Mover {
-
   constructor(){
     this.position = createVector();
     this.velocity = createVector();
@@ -114,8 +115,8 @@ function draw() {
 
 		//{!3} Variables for angular motion
     this.angle = 0;
-    this.aVelocity = 0;
-    this.aAcceleration = 0;
+    this.angleVelocity = 0;
+    this.angleAcceleration = 0;
   }
 
 }
@@ -126,8 +127,8 @@ function draw() { this.position.add(this.velocity); //{!2} Newfangled angular motion - this.aVelocity += this.aAcceleration; - this.angle += this.aVelocity; + this.angleVelocity += this.angleAcceleration; + this.angle += this.angleVelocity; this.acceleration.mult(0); } @@ -147,11 +148,11 @@ function draw() { //{!1} pop() restores the previous state after rotation is complete pop(); } -

At this point, if you were to actually go ahead and create a Mover object, you wouldn’t see it behave any differently. This is because the angular acceleration is initialized to zero (this.aAcceleration = 0;) . For the object to rotate, it needs a non-zero acceleration! Certainly, one option is to hard-code a number in the constructor:

-
    this.aAcceleration = 0.01;
+

At this point, if you were to actually go ahead and create a Mover object, you wouldn’t see it behave any differently. This is because the angular acceleration is initialized to zero (this.angleAcceleration = 0;) . For the object to rotate, it needs a non-zero acceleration! Certainly, one option is to hard-code a number in the constructor:

+
    this.angleAcceleration = 0.01;

You can produce a more interesting result, however, by dynamically assigning an angular acceleration in the update() method according to forces in the environment. This could be my cue to start researching the physics of angular acceleration based on the concepts of torque and moment of inertia, but at this state, that level of simulation would be a bit of a rabbit hole. (I’ll cover more about modeling angular acceleration with a pendulum later in this chapter, as well as look at how other physics libraries realistically models rotational motion in Chapter 6.) Instead, a quick and dirty solution that yields creative results will suffice. A reasonable approach is to calculate angular acceleration as a function of the object's “linear acceleration,” its rate of change of velocity along a path vector, as opposed to its rotation. Here’s an example:

    // Using the x component of the object's linear acceleration to calculate angular acceleration
-    this.aAcceleration = this.acceleration.x;
+ this.angleAcceleration = this.acceleration.x;

Yes, this is arbitrary, but it does do something. If the object is accelerating to the right, its angular rotation accelerates in a clockwise direction; acceleration to the left results in a counterclockwise rotation. Of course, it’s important to think about scale in this case. The value of the acceleration vector’s x component might be too large, causing the object to spin in a way that looks ridiculous or unrealistic. You might even notice a visual illusion called the “wagon wheel effect,” where an object appears to be rotating slower or even in the opposite direction due to the large changes between each frame of animation.

Dividing the x component by some value, or perhaps constraining the angular velocity to a reasonable range, could really help. Here’s the entire update() function with these tweaks added.

@@ -162,20 +163,19 @@ function draw() {
  update() {
-
     this.velocity.add(this.acceleration);
     this.position.add(this.velocity);
 
     //{!1} Calculate angular acceleration according to acceleration’s x component.
-    this.aAcceleration = this.acceleration.x / 10.0;
-    this.aVelocity += this.aAcceleration;
+    this.angleAcceleration = this.acceleration.x / 10.0;
+    this.angleVelocity += this.angleAcceleration;
     //{!1} Use constrain() to ensure that angular velocity doesn’t spin out of control.
-    this.aVelocity = constrain(this.aVelocity, -0.1, 0.1);
-    this.angle += this.aVelocity;
+    this.angleVelocity = constrain(this.angleVelocity, -0.1, 0.1);
+    this.angle += this.angleVelocity;
 
     this.acceleration.mult(0);
   }
-

Notice that I’ve used multiple strategies to keep the object from spinning out of control. First, I divide acceleration.x by 10 before assigning it to aAcceleration. Then, for good measure, I also use constrain() to confine aVelocity to the range (-0.1, 0.1).

+

Notice that I’ve used multiple strategies to keep the object from spinning out of control. First, I divide acceleration.x by 10 before assigning it to angleAcceleration. Then, for good measure, I also use constrain() to confine angleVelocity to the range (-0.1, 0.1).

Exercise 3.3

Step 1: Create a simulation where objects are shot out of a cannon. Each object should experience a sudden force when shot (just once) as well as gravity (always present).

@@ -367,19 +367,19 @@ function draw() {
-

Oscillation Amplitude and Period

+

Properties of Oscillation

Take a look at the graph of the sine function in Figure 3.9, where y = \sin(x).

Figure 3.9: A graph of y = sin(x)
Figure 3.9: A graph of y = sin(x)

The output of the sine function is a smooth curve alternating between −1 and 1, also known as a sine wave. This behavior, a periodic movement between two points, is the oscillation I mentioned at the start of the chapter. Plucking a guitar string, swinging a pendulum, bouncing on a pogo stick—these are all examples of oscillating motion, and they can all be modeled using the sine function.

-

In a p5.js sketch, you can simulate oscillation by assigning the output of the sine function to an object’s position. I’ll begin with a basic scenario. I want a circle to oscillate between the left side and the right side of a canvas.

+

In a p5.js sketch, you can simulate oscillation by assigning the output of the sine function to an object’s position. I’ll begin with a basic scenario: I want a circle to oscillate between the left side and the right side of a canvas.

-

This pattern of oscillating back and forth around a central point is known as simple harmonic motion (or, to be fancier, “the periodic sinusoidal oscillation of an object”). The code to achieve this is remarkably simple, but before I get into it, I’d like to introduce some of the key terminology related to oscillation (and waves).

+

This pattern of oscillating back and forth around a central point is known as simple harmonic motion (or, to be fancier, “the periodic sinusoidal oscillation of an object”). The code to achieve it is remarkably simple, but before I get into it, I’d like to introduce some of the key terminology related to oscillation (and waves).

Simple harmonic motion can be expressed as any position (in this case, the x position) as a function of time, with the following two elements: