Chapter 3. Oscillation

“Trigonometry is a sine of the times.” — Anonymous

In Chapters 1 and 2, we carefully worked out an object-oriented structure to make something move on the screen, using the concept of a vector to represent location, velocity, and acceleration driven by forces in the environment. We could move straight from here into topics such as particle systems, steering forces, group behaviors, etc. If we did that, however, we’d skip an important area of mathematics that we’re going to need: trigonometry, or the mathematics of triangles, specifically right triangles.

Trigonometry is going to give us a lot of tools. We’ll get to think about angles and angular velocity and acceleration. Trig will teach us about the sine and cosine functions, which when used properly can yield an nice ease-in, ease-out wave pattern. It’s going to allow us to calculate more complex forces in an environment that involves angles, such as a pendulum swinging or a box sliding down an incline.

So this chapter is a bit of a mishmash. We’ll start with the basics of angles in Processing and cover many trigonometric topics, tying it all into forces at the end. And by taking this break now, we’ll also pave the way for more advanced examples that require trig later in this book.

3.1 Angles

OK. Before we can do any of this stuff, we need to make sure we understand what it means to be an angle in Processing. If you have experience with Processing, you’ve undoubtedly encountered this issue while using the rotate() function to rotate and spin objects.

The first order of business is to cover radians and degrees. You’re probably familiar with the concept of an angle in degrees. A full rotation goes from 0 to 360 degrees. 90 degrees (a right angle) is 1/4th of 360, shown below as two perpendicular lines.

Nature of Code Image

Figure 3.1

It’s fairly intuitive for us to think of angles in terms of degrees. For example, the square in Figure 3.2 is rotated 45 degrees around its center.

Nature of Code Image

Figure 3.3

Processing, however, requires angles to be specified in radians. A radian is a unit of measurement for angles defined by the ratio of the length of the arc of a circle to the radius of that circle. One radian is the angle at which that ratio equals one (see Figure 3.1). 180 degrees = PI radians, 360 degrees = 2*PI radians, 90 degrees = PI/2 radians, etc.

Nature of Code Image

Figure 3.3

The formula to convert from degrees to radians is:

radians = 2 * PI * (degrees / 360)

Thankfully, if we prefer to think in degrees but code with radians, Processing makes this easy. The radians() function will automatically convert values from degrees to radians, and the constants PI and TWO_PI provide convenient access to these commonly used numbers (equivalent to 180 and 360 degrees, respectively). The following code, for example, will rotate shapes by 60 degrees.

Show Raw
float angle = radians(60);
rotate(angle);

If you are not familiar with how rotation is implemented in Processing, I would suggest this tutorial: Processing - Transform 2D.

What is PI?

The mathematical constant pi (or π) is a real number defined as the ratio of a circle’s circumference (the distance around the perimeter) to its diameter (a straight line that passes through the circle’s center). It is equal to approximately 3.14159 and can be accessed in Processing with the built-in variable PI.

Exercise 3.1

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

Your browser does not support the canvas tag.

3.2 Angular Motion

Remember all this stuff?

location = location + velocity
velocity = velocity + acceleration

The stuff we dedicated almost all of Chapters 1 and 2 to? Well, we can apply exactly the same logic to a rotating object.

angle = angle + angular velocity
angular velocity = angular velocity + angular acceleration

In fact, the above is actually simpler than what we started with because an angle is a scalar quantity—a single number, not a vector!

Using the answer from Exercise 3.1 above, let’s say we wanted to rotate a baton in Processing by some angle. We would have code like:

Show Raw
  translate(width/2,height/2);
  rotate(angle);
  line(-50,0,50,0);
  ellipse(50,0,8,8);
  ellipse(-50,0,8,8);

Adding in our principles of motion brings us to the following example.

Your browser does not support the canvas tag.

Example 3.1: Angular motion using rotate()

Show Raw
Location
float angle = 0;
Velocity
float aVelocity = 0;
Acceleration
float aAcceleration = 0.001;
 
void setup() {
  size(640,360);
}
 
void draw() {
  background(255);
 
  fill(175);
  stroke(0);
  rectMode(CENTER);
  translate(width/2,height/2);
  rotate(angle);
  line(-50,0,50,0);
  ellipse(50,0,8,8);
  ellipse(-50,0,8,8);
 
Angular equivalent of velocity.add(acceleration);
  aVelocity += aAcceleration;
Angular equivalent of location.add(velocity);
  angle += aVelocity;
}

The baton starts onscreen with no rotation and then spins faster and faster as the angle of rotation accelerates.

This idea can be incorporated into our Mover object. For example, we can add the variables related to angular motion to our Mover.

Show Raw
class Mover {
 
  PVector location;
  PVector velocity;
  PVector acceleration;
  float mass;
 
  float angle = 0;
  float aVelocity = 0;
  float aAcceleration = 0;

And then in update(), we update both location and angle according to the same algorithm!

Show Raw
 void update() {
 
Regular old-fashioned motion
    velocity.add(acceleration);
    location.add(velocity);
 
Newfangled angular motion
    aVelocity += aAcceleration;
    angle += aVelocity;
 
    acceleration.mult(0);
  }

Of course, for any of this to matter, we also would need to rotate the object when displaying it.

Show Raw
  void display() {
    stroke(0);
    fill(175,200);
    rectMode(CENTER);
pushMatrix() and popMatrix() are necessary so that the rotation of this shape doesn’t affect the rest of our world.
    pushMatrix();
 
Set the origin at the shape’s location.
    translate(location.x,location.y);
Rotate by the angle.
    rotate(angle);
    rect(0,0,mass*16,mass*16);
    popMatrix();
  }

Now, if we were to actually go ahead and run the above code, we wouldn’t see anything new. This is because the angular acceleration (float aAcceleration = 0;) is initialized to zero. For the object to rotate, we need to give it an acceleration! Certainly, we could hard-code in a different number.

Show Raw
float aAcceleration = 0.01;

However, we can produce a more interesting result by dynamically assigning an angular acceleration according to forces in the environment. Now, we could head far down this road, trying to model the physics of angular acceleration using the concepts of torque and moment of inertia. Nevertheless, this level of simulation is beyond the scope of this book. (We will see more about modeling angular acceleration with a pendulum later in this chapter, as well as look at how Box2D realistically models rotational motion in Chapter 5.)

For now, a quick and dirty solution will do. We can produce reasonable results by simply calculating angular acceleration as a function of the object’s acceleration vector. Here’s one such example:

Show Raw
    aAcceleration = acceleration.x;

Yes, this is completely 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 x component of the acceleration vector might be a quantity that’s too large, causing the object to spin in a way that looks ridiculous or unrealistic. So 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.

Your browser does not support the canvas tag.

Example 3.2: Forces with (arbitrary) angular motion

Show Raw
  void update() {
 
    velocity.add(acceleration);
    location.add(velocity);
 
Calculate angular acceleration according to acceleration’s horizontal direction and magnitude.
    aAcceleration = acceleration.x / 10.0;
    aVelocity += aAcceleration;
Use constrain() to ensure that angular velocity doesn’t spin out of control.
    aVelocity = constrain(aVelocity,-0.1,0.1);
    angle += aVelocity;
 
    acceleration.mult(0);
  }

Exercise 3.2

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).

Step 2: Add rotation to the object to model its spin as it is shot from the cannon. How realistic can you make it look?

3.3 Trigonometry

I think it may be time. We’ve looked at angles, we’ve spun an object. It’s time for: sohcahtoa. Yes, sohcahtoa. This seemingly nonsensical word is actually the foundation for a lot of computer graphics work. A basic understanding of trigonometry is essential if you want to calculate an angle, figure out the distance between points, work with circles, arcs, or lines. And sohcahtoa is a mnemonic device (albeit a somewhat absurd one) for what the trigonometric functions sine, cosine, and tangent mean.

Nature of Code Image

Figure 3.4

  • soh: sine = opposite / hypotenuse

  • cah: cosine = adjacent / hypotenuse

  • toa: tangent = opposite / adjacent

Nature of Code Image

Figure 3.5

Take a look at Figure 3.4 again. There’s no need to memorize it, but make sure you feel comfortable with it. Draw it again yourself. Now let’s draw it a slightly different way (Figure 3.5).

See how we create a right triangle out of a vector? The vector arrow itself is the hypotenuse and the components of the vector (x and y) are the sides of the triangle. The angle is an additional means for specifying the vector’s direction (or “heading”).

Because the trigonometric functions allow us to establish a relationship between the components of a vector and its direction + magnitude, they will prove very useful throughout this book. We’ll begin by looking at an example that requires the tangent function.

3.4 Pointing in the Direction of Movement

Let’s go all the way back to Example 1.10, which features a Mover object accelerating towards the mouse.

Your browser does not support the canvas tag.

You might notice that almost all of the shapes we’ve been drawing so far are circles. This is convenient for a number of reasons, one of which is that we don’t have to consider the question of rotation. Rotate a circle and, well, it looks exactly the same. However, there comes a time in all motion programmers’ lives when they want to draw something on the screen that points in the direction of movement. Perhaps you are drawing an ant, or a car, or a spaceship. And when we say "point in the direction of movement," what we are really saying is “rotate according to the velocity vector.” Velocity is a vector, with an x and a y component, but to rotate in Processing we need an angle, in radians. Let’s draw our trigonometry diagram one more time, with an object’s velocity vector (Figure 3.6).

Nature of Code Image

Figure 3.6

OK. We know that the definition of tangent is:

t a n g e n t a n g l e = v e l o c i t y y v e l o c i t y x

The problem with the above is that we know velocity, but we don’t know the angle. We have to solve for the angle. This is where a special function known as inverse tangent comes in, sometimes referred to as arctangent or tan-1. (There is also an inverse sine and an inverse cosine.)

If the tangent of some value a equals some value b, then the inverse tangent of b equals a. For example:

if

tangent(a) = b

then

a = arctangent(b)

See how that is the inverse? The above now allows us to solve for the angle:

if

tangent(angle) = velocityy / velocityx

then

angle = arctangent(velocityy / velocityx)

Now that we have the formula, let’s see where it should go in our mover’s display() function. Notice that in Processing, the function for arctangent is called atan().

Show Raw
  void display() {
Solve for angle by using atan().
    float angle = atan(velocity.y/velocity.x);
 
    stroke(0);
    fill(175);
    pushMatrix();
    rectMode(CENTER);
    translate(location.x,location.y);
Rotate according to that angle.
    rotate(angle);
    rect(0,0,30,10);
    popMatrix();
  }

Now the above code is pretty darn close, and almost works. We still have a big problem, though. Let’s consider the two velocity vectors depicted below.

Nature of Code Image

Figure 3.7

Though superficially similar, the two vectors point in quite different directions—opposite directions, in fact! However, if we were to apply our formula to solve for the angle to each vector…

V1 ⇒ angle = atan(-4/3) = atan(-1.25) = -0.9272952 radians = -53 degrees
V2 ⇒ angle = atan(4/-3) = atan(-1.25) = -0.9272952 radians = -53 degrees

…we get the same angle for each vector. This can’t be right for both; the vectors point in opposite directions! The thing is, this is a pretty common problem in computer graphics. Rather than simply using atan() along with a bunch of conditional statements to account for positive/negative scenarios, Processing (along with pretty much all programming environments) has a nice function called atan2() that does it for you.

Your browser does not support the canvas tag.

Example 3.3: Pointing in the direction of motion

Show Raw
  void display() {
Using atan2() to account for all possible directions
    float angle = atan2(velocity.y,velocity.x);
 
    stroke(0);
    fill(175);
    pushMatrix();
    rectMode(CENTER);
    translate(location.x,location.y);
Rotate according to that angle.
    rotate(angle);
    rect(0,0,30,10);
    popMatrix();
  }

To simplify this even further, the PVector class itself provides a function called heading(), which takes care of calling atan2() for you so you can get the 2D direction angle, in radians, for any Processing PVector.

Show Raw
The easiest way to do this!
    float angle = velocity.heading();

Exercise 3.3

Create a simulation of a vehicle that you can drive around the screen using the arrow keys: left arrow accelerates the car to the left, right to the right. The car should point in the direction in which it is currently moving.

3.5 Polar vs. Cartesian Coordinates

Any time we display a shape in Processing, we have to specify a pixel location, a set of x and y coordinates. These coordinates are known as Cartesian coordinates, named for René Descartes, the French mathematician who developed the ideas behind Cartesian space.

Another useful coordinate system known as polar coordinates describes a point in space as an angle of rotation around the origin and a radius from the origin. Thinking about this in terms of a vector:

Cartesian coordinate—the x,y components of a vector
Polar coordinate—the magnitude (length) and direction (angle) of a vector

Processing’s drawing functions, however, don’t understand polar coordinates. Whenever we want to display something in Processing, we have to specify locations as (x,y) Cartesian coordinates. However, sometimes it is a great deal more convenient for us to think in polar coordinates when designing. Happily for us, with trigonometry we can convert back and forth between polar and Cartesian, which allows us to design with whatever coordinate system we have in mind but always draw with Cartesian coordinates.

Nature of Code Image

Figure 3.8: The Greek letter θ (theta) is often used to denote an angle. Since a polar coordinate is conventionally referred to as (r, θ), we’ll use theta as a variable name when referring to an angle.

sine(theta)   = y/r   →   y = r * sine(theta)
cosine(theta) = x/r   →   x = r * cosine(theta)

For example, if r is 75 and theta is 45 degrees (or PI/4 radians), we can calculate x and y as below. The functions for sine and cosine in Processing are sin() and cos(), respectively. They each take one argument, an angle measured in radians.

Show Raw
float r = 75;
float theta = PI / 4;
Converting from polar (r,theta) to Cartesian (x,y)
float x = r * cos(theta);
float y = r * sin(theta);

This type of conversion can be useful in certain applications. For example, to move a shape along a circular path using Cartesian coordinates is not so easy. With polar coordinates, on the other hand, it’s simple: increment the angle!

Here’s how it is done with global variables r and theta.

Your browser does not support the canvas tag.

Example 3.4: Polar to Cartesian

Show Raw
float r = 75;
float theta = 0;
 
void setup() {
  size(640,360);
  background(255);
}
 
void draw() {
 
Polar coordinates (r,theta) are converted to Cartesian (x,y) for use in the ellipse() function.
  float x = r * cos(theta);
  float y = r * sin(theta);
 
  noStroke();
  fill(0);
  ellipse(x+width/2, y+height/2, 16, 16);
 
  theta += 0.01;
}

Exercise 3.4

Using Example 3.4 as a basis, draw a spiral path. Start in the center and move outwards. Note that this can be done by only changing one line of code and adding one line of code!

Nature of Code Image

Exercise 3.5

Simulate the spaceship in the game Asteroids. In case you aren’t familiar with Asteroids, here is a brief description: A spaceship (represented as a triangle) floats in two dimensional space. The left arrow key turns the spaceship counterclockwise, the right arrow key, clockwise. The z key applies a “thrust” force in the direction the spaceship is pointing.

Your browser does not support the canvas tag.

3.6 Oscillation Amplitude and Period

Are you amazed yet? We’ve seen some pretty great uses of tangent (for finding the angle of a vector) and sine and cosine (for converting from polar to Cartesian coordinates). We could stop right here and be satisfied. But we’re not going to. This is only the beginning. What sine and cosine can do for you goes beyond mathematical formulas and right triangles.

Let’s take a look at a graph of the sine function, where y = sine(x).

Nature of Code Image

Figure 3.9: y = sine(x)

You’ll notice that the output of the sine function is a smooth curve alternating between –1 and 1. This type of a behavior is known as oscillation, a periodic movement between two points. Plucking a guitar string, swinging a pendulum, bouncing on a pogo stick—these are all examples of oscillating motion.

And so we happily discover that we can simulate oscillation in a Processing sketch by assigning the output of the sine function to an object’s location. Note that this will follow the same methodology we applied to Perlin noise in the Introduction.

Let’s begin with a really basic scenario. We want a circle to oscillate from the left side to the right side of a Processing window.

Your browser does not support the canvas tag.

This is what is known as simple harmonic motion (or, to be fancier, “the periodic sinusoidal oscillation of an object”). It’s going to be a simple program to write, but before we get into the code, let’s familiarize ourselves with some of the terminology of oscillation (and waves).

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

  • Amplitude: The distance from the center of motion to either extreme

  • Period: The amount of time it takes for one complete cycle of motion

Looking at the graph of sine (Figure 3.9), we can see that the amplitude is 1 and the period is TWO_PI; the output of sine never rises above 1 or below -1; and every TWO_PI radians (or 360 degrees) the wave pattern repeats.

Now, in the Processing world we live in, what is amplitude and what is period? Amplitude can be measured rather easily in pixels. In the case of a window 200 pixels wide, we would oscillate from the center 100 pixels to the right and 100 pixels to the left. Therefore:

Show Raw
Our amplitude is measured in pixels.
float amplitude = 100;

Period is the amount of time it takes for one cycle, but what is time in our Processing world? I mean, certainly we could say we want the circle to oscillate every three seconds. And we could track the milliseconds—using millis() —in Processing and come up with an elaborate algorithm for oscillating an object according to real-world time. But for us, real-world time doesn’t really matter. The real measure of time in Processing is in frames. The oscillating motion should repeat every 30 frames, or 50 frames, or 1000 frames, etc.

Show Raw
Our period is measured in frames (our unit of time for animation).
float period = 120;

Once we have the amplitude and period, it’s time to write a formula to calculate x as a function of time, which we now know is the current frame count.

Show Raw
float x = amplitude * cos(TWO_PI * frameCount / period);

Let’s dissect the formula a bit more and try to understand each component. The first is probably the easiest. Whatever comes out of the cosine function we multiply by amplitude. We know that cosine will oscillate between -1 and 1. If we take that value and multiply it by amplitude then we’ll get the desired result: a value oscillating between -amplitude and amplitude. (Note: this is also a place where we could use Processing’s map() function to map the output of cosine to a custom range.)

Now, let’s look at what is inside the cosine function:

TWO_PI * frameCount / period

What’s going on here? Let’s start with what we know. We know that cosine will repeat every 2*PI radians—i.e. it will start at 0 and repeat at 2*PI, 4*PI, 6*PI, etc. If the period is 120, then we want the oscillating motion to repeat when the frameCount is at 120 frames, 240 frames, 360 frames, etc. frameCount is really the only variable; it starts at 0 and counts upward. Let’s take a look at what the formula yields with those values.

frameCount frameCount / period TWO_PI * frameCount / period

0

0

0

60

0.5

PI

120

1

TWO_PI

240

2

2 * TWO_PI (or 4* PI)

etc.

frameCount divided by period tells us how many cycles we’ve completed—are we halfway through the first cycle? Have we completed two cycles? By multiplying that number by TWO_PI, we get the result we want, since TWO_PI is the number of radians required for one cosine (or sine) to complete one cycle.

Wrapping this all up, here’s the Processing example that oscillates the x location of a circle with an amplitude of 100 pixels and a period of 120 frames.

Example 3.5: Simple Harmonic Motion

Show Raw
void setup() {
  size(640,360);
}
 
void draw() {
  background(255);
 
  float period = 120;
  float amplitude = 100;
Calculating horizontal location according to the formula for simple harmonic motion
  float x = amplitude * cos(TWO_PI * frameCount / period);
  stroke(0);
  fill(175);
  translate(width/2,height/2);
  line(0,0,x,0);
  ellipse(x,0,20,20);
}

It’s also worth mentioning the term frequency: the number of cycles per time unit. Frequency is equal to 1 divided by period. If the period is 120 frames, then only 1/120th of a cycle is completed in one frame, and so frequency = 1/120. In the above example, we simply chose to define the rate of oscillation in terms of period and therefore did not need a variable for frequency.

Exercise 3.6

Using the sine function, create a simulation of a weight (sometimes referred to as a “bob”) that hangs from a spring from the top of the window. Use the map() function to calculate the vertical location of the bob. Later in this chapter, we’ll see how to recreate this same simulation by modeling the forces of a spring according to Hooke’s law.

3.7 Oscillation with Angular Velocity

An understanding of the concepts of oscillation, amplitude, and frequency/period is often required in the course of simulating real-world behaviors. However, there is a slightly easier way to rewrite the above example with the same result. Let’s take one more look at our oscillation formula:

Show Raw
float x = amplitude * cos(TWO_PI * frameCount / period);

And let’s rewrite it a slightly different way:

Show Raw
float x = amplitude * cos ( some value that increments slowly );

If we care about precisely defining the period of oscillation in terms of frames of animation, we might need the formula the way we first wrote it, but we can just as easily rewrite our example using the concept of angular velocity (and acceleration) from section 3.2. Assuming:

Show Raw
float angle = 0;
float aVelocity = 0.05;

in draw(), we can simply say:

Show Raw
angle += aVelocity;
float x = amplitude * cos(angle);

angle is our “some value that increments slowly.”

Example 3.6: Simple Harmonic Motion II

Show Raw
float angle = 0;
float aVelocity = 0.05;
 
void setup() {
  size(640,360);
}
 
void draw() {
  background(255);
 
  float amplitude = 100;
  float x = amplitude * cos(angle);
Using the concept of angular velocity to increment an angle variable
  angle += aVelocity;
 
  ellipseMode(CENTER);
  stroke(0);
  fill(175);
  translate(width/2,height/2);
  line(0,0,x,0);
  ellipse(x,0,20,20);
}

Just because we’re not referencing it directly doesn’t mean that we’ve eliminated the concept of period. After all, the greater the angular velocity, the faster the circle will oscillate (therefore lowering the period). In fact, the number of times it takes to add up the angular velocity to get to TWO_PI is the period or:

period = TWO_PI / angular velocity

Let’s expand this example a bit more and create an Oscillator class. And let’s assume we want the oscillation to happen along both the x-axis (as above) and the y-axis. To do this, we’ll need two angles, two angular velocities, and two amplitudes (one for each axis). Another perfect opportunity for PVector!

Your browser does not support the canvas tag.

Example 3.7: Oscillator objects

Show Raw
class Oscillator  {
 
Using a PVector to track two angles!
  PVector angle;
  PVector velocity;
  PVector amplitude;
 
  Oscillator()  {
    angle = new PVector();
    velocity = new PVector(random(-0.05,0.05),random(-0.05,0.05));
Random velocities and amplitudes
    amplitude = new PVector(random(width/2),random(height/2));
  }
 
  void oscillate()  {
    angle.add(velocity);
  }
 
  void display()  {
Oscillating on the x-axis
    float x = sin(angle.x)*amplitude.x;
Oscillating on the y-axis
    float y = sin(angle.y)*amplitude.y;
 
    pushMatrix();
    translate(width/2,height/2);
    stroke(0);
    fill(175);
Drawing the Oscillator as a line connecting a circle
    line(0,0,x,y);
    ellipse(x,y,16,16);
    popMatrix();
  }
}

Exercise 3.7

Try initializing each Oscillator object with velocities and amplitudes that are not random to create some sort of regular pattern. Can you make the oscillators appear to be the legs of a insect-like creature?

Exercise 3.8

Incorporate angular acceleration into the Oscillator object.

3.8 Waves

If you’re saying to yourself, “Um, this is all great and everything, but what I really want is to draw a wave onscreen,” well, then, the time has come. The thing is, we’re about 90% there. When we oscillate a single circle up and down according to the sine function, what we are doing is looking at a single point along the x-axis of a wave pattern. With a little panache and a for loop, we can place a whole bunch of these oscillating circles next to each other.

Your browser does not support the canvas tag.

This wavy pattern could be used in the design of the body or appendages of a creature, as well as to simulate a soft surface (such as water).

Here, we’re going to encounter the same questions of amplitude (height of pattern) and period. Instead of period referring to time, however, since we’re looking at the full wave, we can talk about period as the width (in pixels) of a full wave cycle. And just as with simple oscillation, we have the option of computing the wave pattern according to a precise period or simply following the model of angular velocity.

Let’s go with the simpler case, angular velocity. We know we need to start with an angle, an angular velocity, and an amplitude:

Show Raw
float angle = 0;
float angleVel = 0.2;
float amplitude = 100;

Then we’re going to loop through all of the x values where we want to draw a point of the wave. Let’s say every 24 pixels for now. In that loop, we’re going to want to do three things:

  1. Calculate the y location according to amplitude and sine of the angle.

  2. Draw a circle at the (x,y) location.

  3. Increment the angle according to angular velocity.

Show Raw
  for (int x = 0; x <= width; x += 24) {
 
1) Calculate the y location according to amplitude and sine of the angle.
    float y = amplitude*sin(angle);
 
2) Draw a circle at the (x,y) location.
    ellipse(x,y+height/2,48,48);
 
3) Increment the angle according to angular velocity.
    angle += angleVel;
  }

Let’s look at the results with different values for angleVel:

Your browser does not support the canvas tag.

angleVel = 0.05

Your browser does not support the canvas tag.

angleVel = 0.2

Your browser does not support the canvas tag.

angleVel = 0.4

Notice how, although we’re not precisely computing the period of the wave, the higher the angular velocity, the shorter the period. It’s also worth noting that as the period becomes shorter, it becomes more and more difficult to make out the wave itself as the distance between the individual points increases. One option we have is to use beginShape() and endShape() to connect the points with a line.

Your browser does not support the canvas tag.

Example 3.8: Static wave drawn as a continuous line

Show Raw
float angle = 0;
float angleVel = 0.2;
float amplitude = 100;
 
size(400,200);
background(255);
 
stroke(0);
strokeWeight(2);
noFill();
 
beginShape();
for (int x = 0; x <= width; x += 5) {
Here’s an example of using the map() function instead.
  float y = map(sin(angle),-1,1,0,height);
With beginShape() and endShape(), you call vertex() to set all the vertices of your shape.
  vertex(x,y);
  angle +=angleVel;
}
endShape();

You may have noticed that the above example is static. The wave never changes, never undulates. This additional step is a bit tricky. Your first instinct might be to say: “Hey, no problem, we’ll just let theta be a global variable and let it increment from one cycle through draw() to another.”

While it’s a nice thought, it doesn’t work. If you look at the wave, the righthand edge doesn’t match the lefthand; where it ends in one cycle of draw() can’t be where it starts in the next. Instead, what we need to do is have a variable dedicated entirely to tracking what value of angle the wave should start with. This angle (which we’ll call startAngle) increments with its own angular velocity.

Your browser does not support the canvas tag.

Example 3.9: The Wave

Show Raw
float startAngle = 0;
float angleVel = 0.1;
 
void setup() {
  size(400,200);
}
 
void draw() {
  background(255);
 
In order to move the wave, we start at a different theta value each frame. startAngle += 0.02;
float angle = startAngle;
 
  for (int x = 0; x <= width; x += 24) {
    float y = map(sin(angle),-1,1,0,height);
    stroke(0);
    fill(0,50);
    ellipse(x,y,48,48);
    angle += angleVel;
  }
}

Exercise 3.9

Try using the Perlin noise function instead of sine or cosine with the above example.

Exercise 3.10

Encapsulate the above examples into a Wave class and create a sketch that displays two waves (with different amplitudes/periods) as in the screenshot below. Move beyond plain circles and lines and try visualizing the wave in a more creative way.

Your browser does not support the canvas tag.

Exercise 3.11

More complex waves can be produced by the values of multiple waves together. Create a sketch that implements this, as in the screenshot below.

Your browser does not support the canvas tag.

3.9 Trigonometry and Forces: The Pendulum

Do you miss Newton’s laws of motion? I know I sure do. Well, lucky for you, it’s time to bring it all back home. After all, it’s been nice learning about triangles and tangents and waves, but really, the core of this book is about simulating the physics of moving bodies. Let’s take a look at how trigonometry can help us with this pursuit.

Nature of Code Image

Figure 3.10

Nature of Code Image

Figure 3.11

A pendulum is a bob suspended from a pivot. Obviously a real-world pendulum would live in a 3D space, but we’re going to look at a simpler scenario, a pendulum in a 2D space—a Processing window (see Figure 3.10).

In Chapter 2, we learned how a force (such as the force of gravity in Figure 3.11) causes an object to accelerate. F = M * A or A = F / M. In this case, however, the pendulum bob doesn’t simply fall to the ground because it is attached by an arm to the pivot point. And so, in order to determine its angular acceleration, we not only need to look at the force of gravity, but also the force at the angle of the pendulum’s arm (relative to a pendulum at rest with an angle of 0).

In the above case, since the pendulum’s arm is of fixed length, the only variable in the scenario is the angle. We are going to simulate the pendulum’s motion through the use of angular velocity and acceleration. The angular acceleration will be calculated using Newton’s second law with a little trigonometry twist.

Let’s zoom in on the right triangle from the pendulum diagram.

Nature of Code Image

Figure 3.12

We can see that the force of the pendulum (Fp) should point perpendicular to the arm of the pendulum in the direction that the pendulum is swinging. After all, if there were no arm, the bob would just fall straight down. It’s the tension force of the arm that keeps the bob accelerating towards the pendulum’s rest state. Since the force of gravity (Fp) points downward, by making a right triangle out of these two vectors, we’ve accomplished something quite magnificent. We’ve made the force of gravity the hypotenuse of a right triangle and separated the vector into two components, one of which represents the force of the pendulum. Since sine equals opposite over hypotenuse, we have:

sine(θ) = Fp / Fg

Therefore:

Fp = Fg * sine(θ)

Lest we forget, we’ve been doing all of this with a single question in mind: What is the angular acceleration of the pendulum? Once we have the angular acceleration, we’ll be able to apply our rules of motion to find the new angle for the pendulum.

angular velocity = angular velocity + angular acceleration
angle = angle + angular velocity

The good news is that with Newton’s second law, we know that there is a relationship between force and acceleration, namely F = M * A, or A = F / M. So if the force of the pendulum is equal to the force of gravity times sine of the angle, then:

pendulum angular acceleration = acceleration due to gravity * sine (θ)

This is a good time to remind ourselves that we’re Processing programmers and not physicists. Yes, we know that the acceleration due to gravity on earth is 9.8 meters per second squared. But this number isn’t relevant to us. What we have here is just an arbitrary constant (we’ll call it gravity), one that we can use to scale the acceleration to something that feels right.

angular acceleration = gravity * sine(θ)

Amazing. After all that, the formula is so simple. You might be wondering, why bother going through the derivation at all? I mean, learning is great and all, but we could have easily just said, "Hey, the angular acceleration of a pendulum is some constant times the sine of the angle." This is just another moment in which we remind ourselves that the purpose of the book is not to learn how pendulums swing or gravity works. The point is to think creatively about how things can move about the screen in a computationally based graphics system. The pendulum is just a case study. If you can understand the approach to programming a pendulum, then however you choose to design your onscreen world, you can apply the same techniques.

Of course, we’re not finished yet. We may be happy with our simple, elegant formula, but we still have to apply it in code. This is most definitely a good time to practice our object-oriented programming skills and create a Pendulum class. Let’s think about all the properties we’ve encountered in our pendulum discussion that the class will need:

  • arm length

  • angle

  • angular velocity

  • angular acceleration

Show Raw
class Pendulum  {
 
Length of arm
  float r;
Pendulum arm angle
  float angle;
Angular velocity
  float aVelocity;
Angular acceleration
  float aAcceleration;

We’ll also need to write a function update() to update the pendulum’s angle according to our formula…

Show Raw
  void update() {
Arbitrary constant
    float gravity = 0.4;
Calculate acceleration according to our formula.
    aAcceleration = -1 * gravity * sin(angle);
Increment velocity.
    aVelocity += aAcceleration;
Increment angle.
    angle += aVelocity;
  }
Nature of Code Image

Figure 3.13

…as well as a function display() to draw the pendulum in the window. This begs the question: “Um, where do we draw the pendulum?” We know the angle and the arm length, but how do we know the x,y (Cartesian!) coordinates for both the pendulum’s pivot point (let’s call it origin) and bob location (let’s call it location)? This may be getting a little tiring, but the answer, yet again, is trigonometry.

The origin is just something we make up, as is the arm length. Let’s say:

Show Raw
PVector origin = new PVector(100,10);
float r = 125;

We’ve got the current angle stored in our variable angle. So relative to the origin, the pendulum’s location is a polar coordinate: (r,angle). And we need it to be Cartesian. Luckily for us, we just spent some time (section 3.5) deriving the formula for converting from polar to Cartesian. And so:

Show Raw
PVector location = new PVector(r*sin(angle),r*cos(angle));

Since the location is relative to wherever the origin happens to be, we can just add origin to the location PVector:

Show Raw
location.add(origin);

And all that remains is the little matter of drawing a line and ellipse (you should be more creative, of course).

Show Raw
stroke(0);
fill(175);
line(origin.x,origin.y,location.x,location.y);
ellipse(location.x,location.y,16,16);

Before we put everything together, there’s one last little detail I neglected to mention. Let’s think about the pendulum arm for a moment. Is it a metal rod? A string? A rubber band? How is it attached to the pivot point? How long is it? What is its mass? Is it a windy day? There are a lot of questions that we could continue to ask that would affect the simulation. We’re living, of course, in a fantasy world, one where the pendulum’s arm is some idealized rod that never bends and the mass of the bob is concentrated in a single, infinitesimally small point. Nevertheless, even though we don’t want to worry ourselves with all of the questions, we should add one more variable to our calculation of angular acceleration. To keep things simple, in our derivation of the pendulum’s acceleration, we assumed that the length of the pendulum’s arm is 1. In fact, the length of the pendulum’s arm affects the acceleration greatly: the longer the arm, the slower the acceleration. To simulate a pendulum more accurately, we divide by that length, in this case r. For a more involved explanation, visit The Simple Pendulum website.

Show Raw
aAcceleration = (-1 * G * sin(angle)) / r;

Finally, a real-world pendulum is going to experience some amount of friction (at the pivot point) and air resistance. With our code as is, the pendulum would swing forever, so to make it more realistic we can use a “damping” trick. I say trick because rather than model the resistance forces with some degree of accuracy (as we did in Chapter 2), we can achieve a similar result by simply reducing the angular velocity during each cycle. The following code reduces the velocity by 1% (or multiplies it by 99%) during each frame of animation:

Show Raw
aVelocity *= 0.99;

Putting everything together, we have the following example (with the pendulum beginning at a 45-degree angle).

Your browser does not support the canvas tag.

Example 3.10: Swinging pendulum

Show Raw
Pendulum p;
 
void setup() {
  size(640,360);
We make a new Pendulum object with an origin location and arm length.
  p = new Pendulum(new PVector(width/2,10),125);
}
 
void draw() {
  background(255);
  p.go();
}
 
class Pendulum  {
Many, many variables to keep track of the Pendulum’s various properties
  PVector location;    // Location of bob
  PVector origin;      // Location of arm origin
  float r;             // Length of arm
  float angle;         // Pendulum arm angle
  float aVelocity;     // Angle velocity
  float aAcceleration; // Angle acceleration
  float damping;       // Arbitrary damping amount
 
  Pendulum(PVector origin_, float r_) {
    origin = origin_.get();
    location = new PVector();
    r = r_;
    angle = PI/4;
 
    aVelocity = 0.0;
    aAcceleration = 0.0;
An arbitrary damping so that the Pendulum slows over time
    damping = 0.995;
  }
 
  void go() {
    update();
    display();
  }
 
  void update() {
    float gravity = 0.4;
Formula we worked out for angular acceleration
    aAcceleration = (-1 * gravity / r) * sin(angle);
 
Standard angular motion algorithm
    aVelocity += aAcceleration;
    angle += aVelocity;
 
Apply some damping.
    aVelocity *= damping;
  }
 
  void display() {
Where is the bob relative to the origin? Polar to Cartesian coordinates will tell us!
    location.set(r*sin(angle),r*cos(angle),0);
    location.add(origin);
 
    stroke(0);
The arm
    line(origin.x,origin.y,location.x,location.y);
    fill(175);
The bob
    ellipse(location.x,location.y,16,16);
  }
}

(Note that the version of the example posted on the website has additional code to allow the user to grab the pendulum and swing it with the mouse.)

Exercise 3.12

String together a series of pendulums so that the endpoint of one is the origin point of another. Note that doing this may produce intriguing results but will be wildly inaccurate physically. Simulating an actual double pendulum involves sophisticated equations, which you can read about here: http://scienceworld.wolfram.com/physics/DoublePendulum.html.

Exercise 3.13

Nature of Code Image

Using trigonometry, what is the magnitude of the normal force in the illustration on the right (the force perpendicular to the incline on which the sled rests)? Note that, as indicated, the “normal” force is a component of the force of gravity.

Nature of Code Image

Exercise 3.14

Create an example that simulates a box sliding down the incline with friction. Note that the magnitude of the friction force is equal to the normal force.

3.10 Spring Forces

In section 3.6, we looked at modeling simple harmonic motion by mapping the sine wave to a pixel range. Exercise 3.6 asked you to use this technique to create a simulation of a bob hanging from a spring. While using the sin() function is a quick-and-dirty, one-line-of-code way of getting something up and running, it won’t do if what we really want is to have a bob hanging from a spring in a two-dimensional space that responds to other forces in the environment (wind, gravity, etc.) To accomplish a simulation like this (one that is identical to the pendulum example, only now the arm is a springy connection), we need to model the forces of a spring using PVector.

Nature of Code Image

Figure 3.14

The force of a spring is calculated according to Hooke’s law, named for Robert Hooke, a British physicist who developed the formula in 1660. Hooke originally stated the law in Latin: "Ut tensio, sic vis," or “As the extension, so the force.” Let’s think of it this way:

The force of the spring is directly proportional to the extension of the spring.

Nature of Code Image

Figure 3.15: x = current length - rest length

In other words, if you pull on the bob a lot, the force will be strong; if you pull on the bob a little, the force will be weak. Mathematically, the law is stated as follows:

Fspring = - k * x

  • k is constant and its value will ultimately scale the force. Is the spring highly elastic or quite rigid?

  • x refers to the displacement of the spring, i.e. the difference between the current length and the rest length. The rest length is defined as the length of the spring in a state of equilibrium.

Now remember, force is a vector, so we need to calculate both magnitude and direction. Let’s look at one more diagram of the spring and label all the givens we might have in a Processing sketch.

Nature of Code Image

Figure 3.16

Let’s establish the following three variables as shown in Figure 3.16.

Show Raw
PVector anchor;
PVector location;
float restLength;

First, let’s use Hooke’s law to calculate the magnitude of the force. We need to know k and x. k is easy; it’s just a constant, so let’s make something up.

Show Raw
float k = 0.1;

x is perhaps a bit more difficult. We need to know the “difference between the current length and the rest length.” The rest length is defined as the variable restLength. What’s the current length? The distance between the anchor and the bob. And how can we calculate that distance? How about the magnitude of a vector that points from the anchor to the bob? (Note that this is exactly the same process we employed when calculating distance in Example 2.9: gravitational attraction.)

Show Raw
A vector pointing from anchor to bob gives us the current length of the spring.
PVector dir = PVector.sub(bob,anchor);
float currentLength = dir.mag();
float x = restLength - currentLength;

Now that we’ve sorted out the elements necessary for the magnitude of the force (-1 * k * x), we need to figure out the direction, a unit vector pointing in the direction of the force. The good news is that we already have this vector. Right? Just a moment ago we thought to ourselves: “How we can calculate that distance? How about the magnitude of a vector that points from the anchor to the bob?” Well, that is the direction of the force!

Nature of Code Image

Figure 3.17

In Figure 3.17, we can see that if we stretch the spring beyond its rest length, there should be a force pulling it back towards the anchor. And if it shrinks below its rest length, the force should push it away from the anchor. This reversal of direction is accounted for in the formula with the -1. And so all we need to do is normalize the PVector we used for the distance calculation! Let’s take a look at the code and rename that PVector variable as “force.”

Show Raw
Magnitude of spring force according to Hooke’s law
float k = 0.1;
PVector force = PVector.sub(bob,anchor);
float currentLength = dir.mag();
float x = restLength - currentLength;
 
Direction of spring force (unit vector)
force.normalize();
 
Putting it together: direction and magnitude!
force.mult(-1 * k * x);

Now that we have the algorithm worked out for computing the spring force vector, the question remains: what object-oriented programming structure should we use? This, again, is one of those situations in which there is no “correct” answer. There are several possibilities; which one we choose depends on the program’s goals and one’s own personal coding style. Still, since we’ve been working all along with a Mover class, let’s keep going with this same framework. Let’s think of our Mover class as the spring’s “bob.” The bob needs location, velocity, and acceleration vectors to move about the screen. Perfect—we’ve got that already! And perhaps the bob experiences a gravity force via the applyForce() function. Just one more step—we need to apply the spring force:

Show Raw
Bob bob;
 
void setup() {
  bob = new Bob();
}
 
void draw()  {
Our Chapter 2 “make-up-a-gravity force”
  PVector gravity = new PVector(0,1);
  bob.applyForce(gravity);
We need to also calculate and apply a spring force!
  PVector springForce = _______________????
  bob.applyForce(springForce);
 
Our standard update() and display() functions
  bob.update();
  bob.display();
}
Nature of Code Image

Figure 3.18

One option would be to write out all of the spring force code in the main draw() loop. But thinking ahead to when you might have multiple bobs and multiple spring connections, it makes a good deal of sense to write an additional class, a Spring class. As shown in Figure 3.18, the Bob class keeps track of the movements of the bob; the Spring class keeps track of the spring’s anchor and its rest length and calculates the spring force on the bob.

This allows us to write a lovely main program as follows:

Show Raw
Bob bob;
Adding a Spring object
Spring spring;
 
void setup() {
  bob = new Bob();
  spring = new Spring();
}
 
void draw()  {
  PVector gravity = new PVector(0,1);
  bob.applyForce(gravity);
 
This new function in the Spring class will take care of computing the force of the spring on the bob.
  spring.connect(bob);
 
  bob.update();
  bob.display();
  spring.display();
}

You may notice here that this is quite similar to what we did in Example 2.6 with an attractor. There, we said something like:

Show Raw
  PVector force = attractor.attract(mover);
  mover.applyForce(force);

The analogous situation here with a spring would be:

Show Raw
  PVector force = spring.connect(bob);
  bob.applyForce(force);

Nevertheless, in this example all we said was:

Show Raw
  spring.connect(bob);

What gives? Why don’t we need to call applyForce() on the bob? The answer is, of course, that we do need to call applyForce() on the bob. Only instead of doing it in draw(), we’re just demonstrating that a perfectly reasonable (and sometimes preferable) alternative is to ask the connect() function to internally handle calling applyForce() on the bob.

Show Raw
  void connect(Bob b) {
    PVector force = some fancy calculations
 
The function connect() takes care of calling applyForce() and therefore doesn’t have to return a vector to the calling area.
    b.applyForce(force);
  }

Why do it one way with the Attractor class and another way with the Spring class? When we were first learning about forces, it was a bit clearer to show all the forces being applied in the main draw() loop, and hopefully this helped you learn about force accumulation. Now that we’re more comfortable with that, perhaps it’s simpler to embed some of the details inside the objects themselves.

Let’s take a look at the rest of the elements in the Spring class.

Your browser does not support the canvas tag.

Example 3.11: A Spring connection

Show Raw
class Spring {
 
We need to keep track of the spring’s anchor location.
  PVector anchor;
 
Rest length and spring constant variables
  float len;
  float k = 0.1;
 
The constructor initializes the anchor point and rest length.
  Spring(float x, float y, int l) {
    anchor = new PVector(x,y);
    len = l;
  }
 
Calculate spring force—our implementation of Hooke’s Law.
  void connect(Bob b) {
 
Get a vector pointing from anchor to Bob location.
    PVector force =
      PVector.sub(b.location,anchor);
    float d = force.mag();
Calculate the displacement between distance and rest length.
    float stretch = d - len;
 
Direction and magnitude together!
    force.normalize();
    force.mult(-1 * k * stretch);
 
Call applyForce() right here!
    b.applyForce(force);
  }
 
Draw the anchor.
  void display() {
    fill(100);
    rectMode(CENTER);
    rect(anchor.x,anchor.y,10,10);
  }
 
Draw the spring connection between Bob location and anchor.
  void displayLine(Bob b) {
    stroke(255);
    line(b.location.x,b.location.y,anchor.x,anchor.y);
  }
 
}

The full code for this example is included on the book website, and the Web version also incorporates two additional features: (1) the Bob class includes functions for mouse interactivity so that the bob can be dragged around the window, and (2) the Spring object includes a function to constrain the connection’s length between a minimum and a maximum.

Exercise 3.15

Before running to see the example online, take a look at this constrain function and see if you can fill in the blanks.

Show Raw
void constrainLength(Bob b, float minlen, float maxlen) {
Vector pointing from Bob to Anchor
  PVector dir = PVector.sub(______,______);
  float d = dir.mag();
 
Is it too short?
  if (d < minlen) {
    dir.normalize();
    dir.mult(________);
Keep location within constraint.
    b.location = PVector.add(______,______);
    b.velocity.mult(0);
Is it too long?
  } else if (____________) {
    dir.normalize();
    dir.mult(_________);
Keep location within constraint.
    b.location = PVector.add(______,______);
    b.velocity.mult(0);
  }
}

Exercise 3.16

Create a system of multiple bobs and spring connections. How would you have a bob connected to a bob with no fixed anchor?

The Ecosystem Project

Step 3 Exercise:

Take one of your creatures and incorporate oscillation into its motion. You can use the Oscillator class from Example 3.7 as a model. The Oscillator object, however, oscillates around a single point (the middle of the window). Try oscillating around a moving point. In other words, design a creature that moves around the screen according to location, velocity, and acceleration. But that creature isn’t just a static shape, it’s an oscillating body. Consider tying the speed of oscillation to the speed of motion. Think of a butterfly’s flapping wings or the legs of an insect. Can you make it appear that the creature’s internal mechanics (oscillation) drive its locomotion? For a sample, check out the “AttractionArrayWithOscillation” example with the code download.