noc-book-2/content/02_forces.html
2022-08-19 19:38:25 +00:00

940 lines
No EOL
78 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<section data-type="chapter">
<h1>Chapter 2. Forces</h1>
<blockquote data-type="epigraph">
<p>“Dont underestimate the Force.”</p>
<p>— Darth Vader</p>
</blockquote><a data-type="indexterm" data-primary="forces"></a>
<p>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 resembles a magnetic attraction between shape and mouse, as if some <em>force</em> was pulling the circle in towards the mouse. In this chapter I will 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 by responding to a variety of environmental forces.</p>
<h2>2.1 Forces and Newtons Laws of Motion</h2><a data-type="indexterm" data-primary="forces" data-secondary="Newton&#x27;s laws of motion"></a><a data-type="indexterm" data-primary="Newton" data-secondary="Isaac"></a>
<p>Before I examine the practical realities of simulating forces and building a basic physics engine in code, lets take a conceptual look at what it means to be a force in the real world. Just like the word “vector,” the term “force” can be used to mean a variety of things. It can indicate a powerful intensity, as in “They pushed the boulder with great force” or “They spoke forcefully.” The definition of <strong><em>force</em></strong> that I care about is more formal and comes from Sir Isaac Newtons laws of motion:</p><a data-type="indexterm" data-primary="forces" data-secondary="defined"></a>
<p><span class="highlight">A force is a vector that causes an object with mass to accelerate.</span></p>
<p>The good news is that you hopefully recognize the first part of the definition: <em>a force is a vector</em>. Thank goodness you just spent a whole chapter learning what a vector is and how to program with vectors!</p>
<p>Lets define Newtons three laws of motion in relation to the concept of a force.</p>
<h3>Newtons First Law</h3><a data-type="indexterm" data-primary="Newton&#x27;s first law"></a>
<p>Newtons first law is commonly stated as:</p>
<p><span class="highlight">An object at rest stays at rest and an object in motion stays in motion.</span></p>
<p>However, this is missing an important element related to forces. I could expand the definition by stating:</p>
<p><span class="highlight">An object at rest stays at rest and an object in motion stays in motion at a constant speed and direction unless acted upon by an unbalanced force.</span></p><a data-type="indexterm" data-primary="Aristotle"></a>
<p>By the time Newton came along, the prevailing theory of motion—formulated by Aristotle—was nearly two thousand years old. It stated that if an object is moving, some sort of force is required to keep it moving. Unless that moving thing is being pushed or pulled, it will simply slow down or stop. Right?</p><a data-type="indexterm" data-primary="equilibrium"></a><a data-type="indexterm" data-primary="forces" data-secondary="equilibrium"></a><a data-type="indexterm" data-primary="forces" data-secondary="terminal velocity"></a><a data-type="indexterm" data-primary="terminal velocity"></a>
<p>This, of course, is not true. In the absence of any forces, no force is required to keep an object moving. An object (such as a ball) tossed in the earths atmosphere slows down because of air resistance (a force). An objects velocity will only remain constant in the absence of any forces or if the forces that act on it cancel each other out, i.e. the net force adds up to zero. This is often referred to as <strong><em>equilibrium</em></strong>. The falling ball will reach a terminal velocity (that stays constant) once the force of air resistance equals the force of gravity.</p>
<figure>
<img src="images/02_forces/02_forces_1.png" alt="Figure 2.1: The pendulum doesn&#x27;t move because all the forces cancel each other out (add up to a net force of zero).">
<figcaption>Figure 2.1: The pendulum doesn't move because all the forces cancel each other out (add up to a net force of zero). </figcaption>
</figure>
<p>Considering a p5.js canvas, I could restate Newtons first law as follows:</p><a data-type="indexterm" data-primary="Newton&#x27;s first law" data-secondary="PVector class and"></a><a data-type="indexterm" data-primary="PVector class (Processing)" data-secondary="Newton&#x27;s first law and"></a>
<p><span class="highlight">An objects velocity vector will remain constant if it is in a state of equilibrium.</span></p>
<p>Skipping Newtons second law (arguably the most important law for the purposes of this book) for a moment, lets move on to the third law.</p><a data-type="indexterm" data-primary="Newton&#x27;s third law"></a>
<h3>Newtons Third Law</h3>
<p>This law is often stated as:</p>
<p><span class="highlight">For every action there is an equal and opposite reaction.</span></p>
<p>This law frequently causes confusion in the way that it is stated. For one, it sounds like one force causes another. Yes, if you push someone, that someone may <em>actively</em> decide to push you back. But this is not the action and reaction referred to in Newtons third law.</p>
<p>Lets say you push against a wall. The wall doesnt actively decide to push back on you. There is no “origin” force. Your push simply includes both forces, referred to as an “action/reaction pair.”</p>
<p>A better way of stating the law might be:</p>
<p><span class="highlight">Forces always occur in pairs. The two forces are of equal strength, but in opposite directions.</span></p>
<p>Now, this still causes confusion because it sounds like these forces would always cancel each other out. This is not the case. Remember, the forces act on different objects. And just because the two forces are equal, it doesnt mean that the movements are equal (or that the objects will stop moving).</p>
<p>Consider pushing on a stationary truck. Although the truck is far more powerful than you, unlike a moving one, a stationary truck will never overpower you and send you flying backwards. The force you exert on it is equal and opposite to the force exerted on your hands. The outcome depends on a variety of other factors. If the truck is a small truck on an icy downhill, youll probably be able to get it to move. On the other hand, if its a very large truck on a dirt road and you push hard enough (maybe even take a running start), you could injure your hand.</p>
<p>And if you are wearing roller skates when you push on that truck?</p>
<figure>
<img src="images/02_forces/02_forces_2.png" alt="Figure 2.2">
<figcaption>Figure 2.2</figcaption>
</figure>
<p>Youll accelerate away from the truck, sliding along the road while the truck stays put. Why do you slide but not the truck? For one, the truck has a much larger mass (which Ill get into with Newtons second law). There are other forces at work too, namely the friction of the trucks tires and your roller skates against the road.</p>
<h3>Newtons Third Law (as seen through the eyes of p5.js)</h3><a data-type="indexterm" data-primary="Newton&#x27;s third law" data-secondary="PVector class and"></a><a data-type="indexterm" data-primary="PVector class (Processing)" data-secondary="Newton&#x27;s third law and"></a>
<p><span class="highlight">If you calculate a p5.Vector <code>f</code> that is a force of object A on object B, you must also apply the force—<code>p5.Vector.mult(f, -1)</code>;—that B exerts on object A.</span></p>
<p>Youll soon see that in the world of coding simulation, its often not necessary to stay true to the above. Sometimes, such as in the case of gravitational attraction between bodies, Ill want to model equal and opposite forces in my example code. Other times, such as a scenario where Ill say, “Hey, theres some wind in the environment,” Im not going to bother to model the force that a body exerts back on the air. In fact, Im not going to bother modeling the air at all! Remember, this examples in this book are taking inspiration from the physics of the natural world for the purposes of creativity and interactivity and do not require perfect precision.</p>
<h2>2.2 Forces and p5.js—Newtons Second Law as a Function</h2><a data-type="indexterm" data-primary="acceleration" data-secondary="Newton&#x27;s second law"></a><a data-type="indexterm" data-primary="natural phenomena" data-secondary="Newton&#x27;s second law" data-tertiary="modeling"></a><a data-type="indexterm" data-primary="Newton&#x27;s second law"></a>
<p>Now its time for most important law for you, the p5.js coder.</p>
<h3>Newtons Second Law</h3>
<p>This law is stated as:</p>
<p><span class="highlight">Force equals mass times acceleration.</span></p>
<p>Or:</p>
<div data-type="equation">\vec{F} = M \times \vec{A}</div>
<p>Why is this the most important law for this book? Well, lets write it a different way.</p>
<div data-type="equation">\vec{A} = \vec{F} / M</div>
<p>Acceleration is directly proportional to force and inversely proportional to mass. This means that if you get pushed, the harder you are pushed, the faster youll move (accelerate). The bigger you are, the slower youll move.</p><a data-type="indexterm" data-primary="density"></a><a data-type="indexterm" data-primary="mass" data-secondary="weight vs."></a><a data-type="indexterm" data-primary="weight" data-secondary="mass vs."></a>
<div data-type="note">
<h3>Weight vs. Mass</h3>
<ul>
<li>The <strong><em>mass</em></strong> of an object is a measure of the amount of matter in the object (measured in kilograms).</li>
<li><strong><em>Weight</em></strong>, though often mistaken for mass, is technically the force of gravity on an object. From Newtons second law, we can calculate it as mass times the acceleration of gravity (<code>w = m * g</code>). Weight is measured in newtons.</li>
<li><strong><em>Density</em></strong> is defined as the amount of mass per unit of volume (grams per cubic centimeter, for example).</li>
</ul>
<p>Note that an object that has a mass of one kilogram on earth would have a mass of one kilogram on the moon. However, it would weigh only one-sixth as much.</p>
</div>
<p>Now, in the world of p5.js, what is mass anyway? Arent we dealing with pixels? To start in a simpler place, lets say that in a pretend pixel world, all objects have a mass equal to 1. <code>F / 1 = F</code>. And so:</p>
<div data-type="equation">\vec{A} = \vec{F}</div>
<p>The acceleration of an object is equal to force. This is great news. After all, in Chapter 1 I described acceleration as the key to controlling the movement of objects on a canvas. <code>position</code> changes according to <code>velocity</code>, and <code>velocity</code> according to <code>acceleration</code>. Acceleration was where it all began. Now you can see that <em>force</em> is truly where it all begins.</p>
<p>Lets take the <code>Mover</code> class, with position, velocity, and acceleration.</p>
<pre class="codesplit" data-code-language="javascript">class Mover {
constructor(){
this.position = createVector();
this.velocity = createVector();
this.acceleration = createVector();
}
}</pre>
<p>Now the goal is to be able to add forces to this object, with code something like:</p>
<pre class="codesplit" data-code-language="javascript">mover.applyForce(wind);</pre>
<p>or:</p>
<pre class="codesplit" data-code-language="javascript">mover.applyForce(gravity);</pre>
<p>where wind and gravity are <code>p5.Vector</code> objects. According to Newtons second law, I could implement this function as follows.</p>
<pre class="codesplit" data-code-language="javascript">applyForce(force) {
//{!1} Newton's second law at its simplest.
this.acceleration = force;
}</pre>
<h2>2.3 Force Accumulation</h2><a data-type="indexterm" data-primary="forces" data-secondary="accumulation of"></a>
<p>This looks pretty good. After all, <em>acceleration = force</em> is a literal translation of Newtons second law (in a world without mass). Nevertheless, theres a pretty big problem here which Ill quickly encounter when I return to my original goal: creating object that responds to wind and gravity forces.</p>
<pre class="codesplit" data-code-language="javascript">mover.applyForce(wind);
mover.applyForce(gravity);
mover.update();</pre>
<p>OK, Ill <em>be</em> the computer for a moment. First, I call <code>applyForce()</code> with wind. And so the <code>Mover</code> objects acceleration is now assigned the vector <code>wind</code>. Second, I call <code>applyForce()</code> with gravity. Now the <code>Mover</code> objects acceleration is set to the gravity vector. Finally, I call <code>update()</code>. What happens in <code>update()</code>? Acceleration is added to velocity.</p>
<pre class="codesplit" data-code-language="javascript">this.velocity.add(this.acceleration);</pre>
<p>If you run this code, you will not see error in the console, but zoinks! Theres a major problem. What is the value of acceleration when it is added to velocity? It is equal to the gravity vector. Wind has been left out! Anytime <code>applyForce()</code> is called, acceleration is overwritten. How can I handle more than one force?</p><a data-type="indexterm" data-primary="force accumulation"></a>
<p>The answer lies in the full definition of Newtons second law itself which I now to confess to having simplified. Heres a more accurate way to put it:</p>
<p><span class="highlight">Net Force equals mass times acceleration.</span></p>
<p>Or, acceleration is equal to the <em>sum of all forces</em> divided by mass. This makes perfect sense. After all, as you saw in Newtons first law, if all the forces add up to zero, an object experiences an equilibrium state (i.e. no acceleration). This can be implemented through a process known as <strong><em>force accumulation</em></strong>— adding all of the forces together. At any given moment, there might be 1, 2, 6, 12, or 303 forces. As long as the object knows how to accumulate them, it doesnt matter how many forces act on it.</p>
<pre class="codesplit" data-code-language="javascript">applyForce(force) {
//{!1} Newton's second law, but with force accumulation. I now add each force to acceleration, one at a time.
this.acceleration.add(force);
}</pre>
<p>Now, Im not finished just yet. Force accumulation has one more piece. Since Im adding all the forces together at any given moment, I have to make sure that I clear acceleration (i.e. set it to zero) before each time <code>update()</code> is called. Consider a wind force for a moment. Sometimes wind is very strong, sometimes its weak, and sometimes theres no wind at all. For example, you might write code that creates a gust of wind, say, when the user holds down the mouse.</p>
<pre class="codesplit" data-code-language="javascript">if (mouseIsPressed) {
let wind = createVector(0.5, 0);
mover.applyForce(wind);
}</pre><a data-type="indexterm" data-primary="acceleration" data-secondary="force accumulation and"></a>
<p>When the user releases the mouse, the wind should stop, and according to Newtons first law, the object continues moving at a constant velocity. However, if I forgot to reset acceleration to zero, the gust of wind would still be in effect. Even worse, it would add onto itself from the previous frame! Acceleration, in a time-based physics simulation, has no memory; it is calculated based on the environmental forces present at any given moment (frame) in time. This is different than, say, position, which must remember its previous location in order to move properly to the next.</p>
<p>One way to implement clearing the acceleration for each frame is to multiply the vector by 0 at the end of <code>update()</code>.</p>
<pre class="codesplit" data-code-language="javascript">update() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
// Clearing acceleration after it's been applied
this.acceleration.mult(0);
}</pre>
<div data-type="exercise">
<h3>Exercise 2.1</h3>
<p>Using forces, simulate a helium-filled balloon floating upward and bouncing off the top of a window. Can you add a wind force that changes over time, perhaps according to Perlin noise?</p>
</div>
<h2>2.4 Dealing with Mass</h2><a data-type="indexterm" data-primary="mass" data-secondary="modeling"></a><a data-type="indexterm" data-primary="natural phenomena" data-secondary="mass" data-tertiary="modeling"></a>
<p>OK. Ive got one small (but fundamental) addition to make before integrating forces into the <code>Mover</code> class. After all, Newtons second law is really <span data-type="equation">\vec{F} = M \times \vec{A}</span>, not <span data-type="equation">\vec{F} = \vec{A}</span>. Incorporating mass is as easy as adding an instance variable to the class, but I need to spend a little more time here because of another impending complication.</p>
<p>First Ill add mass.</p>
<pre class="codesplit" data-code-language="javascript">class Mover {
constructor(){
this.position = createVector();
this.velocity = createVector();
this.acceleration = createVector();
//{!1} Adding mass as a number
this.mass = ????;
}
}</pre><a data-type="indexterm" data-primary="mass" data-secondary="units of measurement" data-tertiary="defining"></a>
<div data-type="note">
<h3>Units of Measurement</h3>
<p>Now that I am introducing mass, its important to make a quick note about units of measurement. In the real world, things are measured in specific units. Two objects are 3 meters apart, the baseball is moving at a rate of 90 miles per hour, or this bowling ball has a mass of 6 kilograms. As youll see later in this book, sometimes you will want to take real-world units into consideration. However, in this chapter, Im going to ignore them for the most part. The units of measurement are in pixels (“These two circles are 100 pixels apart”) and frames of animation (“This circle is moving at a rate of 2 pixels per frame”). In the case of mass, there isnt any unit of measurement to use. Im just going to make something up. In this example, I will arbitrarily picking the number 10. There is no unit of measurement, though you might enjoy inventing a unit of your own, like “1 moog” or “1 yurkle.” It should also be noted that, for demonstration purposes, Ill tie mass to pixels (drawing, say, a circle with a radius of 10). This will allow me to visualize the mass of an object albeit inaccurately. In the real world size does not indicate mass. A small metal ball could have a much higher mass than a large balloon due to its higher density. And for two circular objects with equal density, the mass should be tied to the formula for area which is not as simple as the radius alone.</p>
</div>
<p>Mass is a scalar, not a vector, as its just one number describing the amount of matter in an object. I could get fancy and compute the area of a shape as its mass, but its simpler to begin by saying, “Hey, the mass of this object is…um, I dunno…how about 10?”</p>
<pre class="codesplit" data-code-language="javascript">constructor() {
this.position = createVector(random(width), random(height));
this.velocity = createVector(0, 0);
this.acceleration = createVector(0, 0);
this.mass = 10;
}</pre><a data-type="indexterm" data-primary="forces" data-secondary="applying to objects"></a>
<p>This isnt so great since things only become interesting once I have objects with varying mass, but its enough to get us started. Where does mass come in? Its needed for applying Newtons second law to the object.</p>
<pre class="codesplit" data-code-language="javascript">applyForce(force) {
//{!2} Newton's second law (with force accumulation and mass)
force.div(mass);
this.acceleration.add(force);
}</pre>
<p>Yet again, even though the code looks quite reasonable, there is a major problem here. Consider the following scenario with two <code>Mover</code> objects, both being blown away by a wind force.</p>
<pre class="codesplit" data-code-language="javascript">let m1 = new Mover();
let m2 = new Mover();
let wind = createVector(1, 0);
m1.applyForce(wind);
m2.applyForce(wind);</pre>
<p>Again, Ill <em>be</em> the computer. Object <code>m1</code> receives the wind force—(1,0)—divides it by mass (10), and adds it to acceleration.</p>
<table>
<tbody>
<tr>
<td>m1 equals wind force</td>
<td>(1,0)</td>
</tr>
<tr>
<td>Divided by mass of 10</td>
<td>(0.1,0)</td>
</tr>
</tbody>
</table><a data-type="indexterm" data-primary="object-oriented programming" data-secondary="references to vs. copies of objects"></a>
<p>OK. Moving on to object <code>m2</code>. It also receives the wind force—(1,0). Wait. Hold on a second. What is the value of the wind force? Taking a closer look, the wind force is actually now—(0.1,0)!! Do you remember this little tidbit about working with objects? When you pass an object (in this case a <code>p5.Vector</code>) into a function, you are passing a reference to that object. Its not a copy! So if a function makes a change to that object (which, in this case, it does by dividing by mass) then that object is permanently changed! But I dont want <code>m2</code> to receive a force divided by the mass of object <code>m1</code>. I want it to receive that force in its original state—(1,0). And so I must protect the original vector and make a copy of the it before dividing it by mass. Fortunately, the <code>p5.Vector</code> class has a convenient method for making a copy—<code>copy()</code>. <code>copy()</code> returns a new <code>p5.Vector</code> object with the same data. And so I can revise <code>applyForce()</code> as follows:</p>
<pre class="codesplit" data-code-language="javascript">applyForce(force) {
//{!1} Making a copy of the vector before using it!
let f = force.copy();
f.div(this.mass);
this.acceleration.add(f);
}</pre>
<p>Theres another way I could write the above function, using the static method <code>div()</code>. For help with this exercise, review static methods in Chapter 1.</p>
<div data-type="exercise">
<h3>Exercise 2.2</h3>
<p>Rewrite the <code>applyForce()</code> method using the static method <code>div()</code> instead of <code>copy()</code>.</p>
<pre class="codesplit" data-code-language="javascript">applyForce(force) {
const f = _______._______(_______,_______);
this.acceleration.add(f);
}</pre>
</div>
<h2>2.5 Creating Forces</h2><a data-type="indexterm" data-primary="forces" data-secondary="creating"></a>
<p>Lets take a moment to recap what Ive covered so far. I've defined what a force is (a vector), and how to apply a force to an object (divide it by mass and add it to the objects acceleration vector). What is missing? Well, I have yet to figure out how to calculate a force in the first place. Where do forces come from?</p>
<p>In this chapter, Ill look at two methods for creating forces in a p5.js world.</p><a data-type="indexterm" data-primary="natural phenomena" data-secondary="forces" data-tertiary="modeling"></a>
<ol>
<li><strong>Make up a force!</strong> After all, you are the programmer, the creator of your world. Theres no reason why you cant just make up a force and apply it.</li>
<li><strong>Model a force!</strong> Yes, forces exist in the real world. And physics textbooks often contain formulas for these forces. You can take these formulas, translate them into source code, and model real-world forces in JavaScript.</li>
</ol>
<p>The easiest way to make up a force is to just pick a number (or two numbers really). Lets start with the idea of simulating wind. How about a wind force that points to the right and is fairly weak? Assuming a <code>Mover</code> object <code>mover</code>, the code would look like:</p>
<pre class="codesplit" data-code-language="javascript">let wind = createVector(0.01, 0);
mover.applyForce(wind);</pre>
<p>The result isnt terribly interesting, but it is a good place to start. I create a <code>p5.Vector</code> object, initialize it, and pass it into an object (which in turn will apply it to its own acceleration). To finish off this example, Ill add one more forcegravity (pointing down)and only m1 equals wind forceengage the wind force when the mouse is pressed.</p>
<div data-type="example">
<h3>Example 2.1: Forces</h3>
<figure>
<img src="images/02_forces/02_forces_3.png" alt="">
<figcaption> </figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">let gravity = createVector(0, 0.1);
mover.applyForce(gravity);
if (mouseIsPressed) {
let wind = createVector(0.1, 0);
mover.applyForce(wind);
}</pre>
<p>Now I have two forces, pointing in different directions with different magnitudes, both applied to object <code>mover</code>. Im beginning to get somewhere. Ive built a world, an environment with forces that act on objects.</p>
<p>Lets look at what happens now when I add a second object with a variable mass. To do this, well need a quick review of object-oriented programming. Again, Im not covering all the basics of programming here (for that you can check out any of the intro p5.js books or video tutorials listed in the introduction). However, since the idea of creating a world filled with objects is pretty fundamental to all the examples in this book, its worth taking a moment to walk through the steps of going from one object to many.</p>
<p>This is where I left the <code>Mover</code> class. Notice how it is identical to the <code>Mover</code> class created in Chapter 1, with two additions—<code>mass</code> and a new <code>applyForce()</code> function.</p>
<pre class="codesplit" data-code-language="javascript">class Mover {
constructor() {
//{!1} And for now, we'll just set the mass equal to 1 for simplicity.
this.mass = 1;
this.position = createVector(width / 2, 30);
this.velocity = createVector(0, 0);
this.acceleration = createVector(0, 0);
}
// Newton's second law.
applyForce(force) {
//{!2} Receive a force, divide by mass, and add to acceleration.
let f = p5.Vector.div(force, this.mass);
this.acceleration.add(f);
}
update() {
//{!2} Motion 101 from Chapter 1
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
// Now add clearing the acceleration each time!
this.acceleration.mult(0);
}
display() {
stroke(0);
fill(175);
//{!1} Scaling the size according to mass.
ellipse(this.position.x, this.position.y, this.mass * 16, this.mass * 16);
}
// Somewhat arbitrarily, I have decided that an object bounces when it hits the edges of a window.
checkEdges() {
if (this.position.x > width) {
this.position.x = width;
this.velocity.x *= -1;
} else if (this.position.x &#x3C; 0) {
this.velocity.x *= -1;
this.position.x = 0;
}
if (this.position.y > height) {
//{!2} Even though I said not to touch position and velocity directly, there are some exceptions. Here I are doing so as a quick and easy way to reverse the direction of the object when it reaches the edge.
this.velocity.y *= -1;
this.position.y = height;
}
}
}</pre>
<p>Now that the class is set, we can choose to create more than one <code>Mover</code> objects.</p>
<pre class="codesplit" data-code-language="javascript">let moverA = new Mover();
let moverB = new Mover();</pre>
<p>But there is an issue. Referring back to the <code>Mover</code> objects constructor…</p>
<pre class="codesplit" data-code-language="javascript">constructor() {
//{!2} Every object has a mass of 1 and a position of (width / 2, 30).
this.mass = 1;
this.position = createVector(width / 2, 30);
this.velocity = createVector(0, 0);
this.acceleration = createVector(0, 0);
}</pre><a data-type="indexterm" data-primary="constructor" data-secondary="arguments" data-tertiary="adding to"></a>
<p>…you will notice that every <code>Mover</code> object is made exactly the same way. What I want are <code>Mover</code> objects of variable mass that start at variable positions. A nice way to accomplish this is to add constructor arguments.</p>
<pre class="codesplit" data-code-language="javascript">Mover(x, y, m) {
//{!2} Now setting these variables with arguments
this.mass = m;
this.position = createVector(x, y);
this.velocity = createVector(0, 0);
this.acceleration = createVector(0, 0);
}</pre>
<p>Notice how the mass and position are no longer set to hardcoded numbers, but rather initialized via arguments passed through the constructor. This means I can create a variety of <code>Mover</code> objects: big ones, small ones, ones that start on the left side of the screen, ones that start on the right, etc.</p>
<pre class="codesplit" data-code-language="javascript">// A large Mover on the left side of the window
let moverA = new Mover(100, 30, 10);
// A smaller Mover on the right side of the window
let moverB = new Mover(400, 30, 2);</pre>
<p>There are all sorts of ways I could choose to initialize the values (random, perlin noise, in a grid, etc.) and an array most likely makes more sense to manage large numbers of Mover objects. This is just a demonstration of the basic idea to get started and Ill introduce other techniques throughout this chapter.</p>
<p>Once the objects are declared and initialized, the rest of the code follows as before. For each object, pass the forces in the environment to <code>applyForce</code>, and enjoy the show.</p>
<div data-type="example">
<h3>Example 2.2: Forces acting on two objects</h3>
<figure>
<img src="images/02_forces/02_forces_4.png" alt="">
<figcaption> </figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">function draw() {
background(51);
//{!3} Make up a gravity force and apply it.
let gravity = createVector(0, 0.1);
moverA.applyForce(gravity);
moverB.applyForce(gravity);
//{!3} Make up a wind force and apply when mouse is pressed.
if (mouseIsPressed) {
let wind = createVector(0.1, 0);
moverA.applyForce(wind);
moverB.applyForce(wind);
}
moverA.update();
moverA.display();
moverA.checkEdges();
moverB.update();
moverB.display();
moverB.checkEdges();
}
</pre>
<p>Note how in the above image, the small circle reaches the bottom of the window faster than the larger one. This is because of our formula: <em>acceleration = force divided by mass</em>. The larger the mass, the smaller the acceleration. While such a difference makes sense for the wind force this is not physically accurate for a simulation of the earths gravitational pull! Ill get into why and how to correct this in the next section.</p>
<div data-type="exercise">
<h3>Exercise 2.3</h3>
<p>Instead of objects bouncing off the edge of the wall, create an example in which an invisible force pushes back on the objects to keep them in the window. Can you weight the force according to how far the object is from an edge—i.e., the closer it is, the stronger the force?</p>
</div>
<p><strong>Exercise 2.x</strong></p>
<p>Create many objects with array.</p>
<p><strong>Exercise 2.x</strong></p>
<p>Fix the bouncing off the sides of the canvas so that the circle changes direction when its edge hits the side, rather than its center.</p>
<p><strong>Exercise 2.x</strong></p>
<p>Create a wind force that is variable. Can you make it interactive, for example, think of a fan located where the mouse is and pointed towards the circles?</p>
<h2>2.6 Gravity on Earth and Modeling a Force</h2><a data-type="indexterm" data-primary="forces" data-secondary="gravity" data-tertiary="modeling"></a><a data-type="indexterm" data-primary="Galileo"></a><a data-type="indexterm" data-primary="natural phenomena" data-secondary="gravity"></a>
<p>I left the last section with a reference to the inaccuract of this example 2.3. The smaller the circle, the faster it falls. There is a logic to this; after all, according to Newtons second law the smaller the mass, the higher the acceleration. But this is not what happens in the real world. If you were to climb to the top of the Leaning Tower of Pisa and drop two balls of different masses, which one will hit the ground first? According to legend, Galileo performed this exact test in 1589, discovering that they fell with the same acceleration, hitting the ground at the same time. Why is this? Ill dive even deeper into this shortly, but the quick answer is that the force of gravity is calculated relative to an objects mass. The bigger the object, the stronger the force. So if the force is scaled according to mass, it is canceled out when acceleration is divided by mass. A quick fix is to implement this in the sketch by multiplying the made-up gravity force by mass.</p>
<div data-type="example">
<h3>Example 2.3: Gravity scaled by mass</h3>
<figure>
<img src="images/02_forces/02_forces_5.png" alt="">
<figcaption> </figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">// Made-up gravity force
let gravity = createVector(0, 0.1);
// Scaled by mover A's mass
let gravityA = p5.Vector.mult(gravity, moverA.mass);
moverA.applyForce(gravityA);
// Scaled by mover B's mass
let gravityB = p5.Vector.mult(gravity, moverB.mass);
moverB.applyForce(gravityB);</pre>
<p>While the objects now fall at the same rate, because the strength of the wind force is independent of mass, when the mouse is pressed the smaller circle still accelerates to the right more quickly. ()ve also included a solultion to exercise 2.x in this example with the addition of a <code>radius</code> variable in the <code>Mover</code> class.)</p>
<p>Making up forces will actually get you quite far. The world of p5.js is an orchestra of pixels and you are its conductor. So whatever you deem appropriate to be a force, well by golly, thats the force it should be. Nevertheless, there may come a time where you find yourself wondering: “But how does it really all work?”</p><a data-type="indexterm" data-primary="forces" data-secondary="models of" data-tertiary="building"></a><a data-type="indexterm" data-primary="natural phenomena" data-secondary="physics (real world)" data-tertiary="modeling"></a><a data-type="indexterm" data-primary="physics" data-secondary="modeling"></a>
<p>Open up any high school physics textbook and you will find some diagrams and formulas describing many different forces—gravity, electromagnetism, friction, tension, elasticity, and more. In this chapter Im going to look at two forces—friction and gravity. The point Id like to make here is not that friction and gravity are fundamental forces that you always need to have in your simulations. Rather, I want to demonstrate these two forces as case studies for the following process:</p>
<ul>
<li>Understanding the concept behind a force</li>
<li>Deconstructing the forces formula into two parts:</li>
<li>Translating that formula into p5.js code that calculates a vector to be sent through the <code>Mover</code>'s <code>applyForce()</code> function.</li>
</ul>
<p>If you can follow the above steps with two example forces Ill provide here, then hopefully when you find yourself Googling “atomic nuclei weak nuclear force” at 3 a.m., you will have the skills to take what you find and adapt it for p5.js.</p><a data-type="indexterm" data-primary="formulae" data-secondary="evaluating in code"></a>
<div data-type="note">
<h3>Dealing with formulas</h3>
<p>OK, in a moment Im going to write out the formula for friction. This wont be the first time youve seen a formula in this book; I just finished up the discussion of Newtons second law, <span data-type="equation">\vec{F} = M \times \vec{A}</span> (or force = mass * acceleration). You hopefully didnt spend a lot of time worrying about this formula because its a nice and simple one. Nevertheless, its a scary world out there. Just take a look at the equation for a “normal” distribution, which I covered (without looking at the formula) in the Introduction.</p>
<div data-type="equation">\frac{1}{\sigma\sqrt{2\pi}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}</div><a data-type="indexterm" data-primary="friction" data-secondary="formula for"></a>
<p>Formulas are regularly written with many symbols (often with letters from the Greek alphabet). Heres the formula for friction.</p>
<div data-type="equation">\vec{friction} = -\mu * N * \hat{\nu}</div>
<p>If its been a while since youve looked at a formula from a math or physics textbook, there are three key points that are important to cover before I move on.</p>
<ul>
<li><strong><em>Evaluate the right side, assign to the left side.</em></strong> This is just like in code! In the case above, I want to calculate the force of friction—the left side represents what I want to calculate and the right side elaborates on how to do it.</li>
<li><strong><em>Am I talking about a vector or a scalar?</em></strong> Its important to realize that in some cases, youll be calculating a vector; in others, a scalar. For example, in this case the force of friction is a vector. That is indicated by the arrow above the word “friction.” It has a magnitude and direction. The right side of the equation also has a vector, as indicated by the symbol {unitv}, which in this case stands for the velocity unit vector.</li>
<li><strong><em>When symbols are placed next to each other, this typically means multiply them.</em></strong> The formula above has four elements: -1, <em>μ</em>, <em>N</em>, and <span data-type="equation">\hat{v}</span> . They should be multiplied together, reading the formula as: <span data-type="equation">\vec{friction} = -1 * \mu * N * \hat{\nu}</span></li>
</ul>
</div>
<h2>2.7 Friction</h2><a data-type="indexterm" data-primary="forces" data-secondary="friction" data-tertiary="modeling"></a><a data-type="indexterm" data-primary="friction" data-secondary="modeling with formulae"></a><a data-type="indexterm" data-primary="natural phenomena" data-secondary="friction"></a>
<p>Lets begin with friction and follow the above steps.</p><a data-type="indexterm" data-primary="dissipative force"></a>
<p>Friction is a <strong><em>dissipative force</em></strong>. A dissipative force is one in which the total energy of a system decreases when an object is in motion. Lets say you are driving a car. When you press your foot down on the brake pedal, the cars brakes use friction to slow down the motion of the tires. Kinetic energy (motion) is converted into thermal energy (heat). Whenever two surfaces come into contact, they experience friction. A complete model of friction would include separate cases for static friction (a body at rest against a surface) and kinetic friction (a body in motion against a surface), but for simplification here, I am only going to look at the kinetic case.</p>
<p>Heres the formula for friction:</p>
<figure>
<img src="images/02_forces/02_forces_6.png" alt="Figure 2.3">
<figcaption>Figure 2.3</figcaption>
</figure><a data-type="indexterm" data-primary="friction" data-secondary="determining direction/magnitude of"></a>
<p>Its now time to separate this formula into two components that determine the direction of friction as well as the magnitude. The diagram above indicates that <em>friction points in the opposite direction of velocity.</em> In fact, thats the part of the formula that says -1 * <span data-type="equation">\hat{v}</span>, or -1 times the velocity unit vector. In p5.js, this would mean taking the velocity vector, normalizing it, and multiplying by -1.</p>
<pre class="codesplit" data-code-language="javascript">let friction = this.velocity.copy();
friction.normalize();
// Lets figure out the direction of the friction force
// (a unit vector in the opposite direction of velocity).
friction.mult(-1);</pre>
<p>Notice two additional steps here. First, its important to make a copy of the velocity vector, as I dont want to reverse the objects direction by accident. Second, the vector is normalized. This is because the magnitude of friction is not associated with the speed of the object, and I want to start with a vector of length 1 so that it can easily be scaled.</p><a data-type="indexterm" data-primary="mu (μ)"></a><a data-type="indexterm" data-primary="coefficient of friction"></a><a data-type="indexterm" data-primary="coefficient of friction" data-secondary="mu (μ)"></a><a data-type="indexterm" data-primary="friction" data-secondary="mu (μ)"></a><a data-type="indexterm" data-primary="friction" data-secondary="coefficient of friction"></a>
<p>According to the formula, the magnitude is <code>μ</code> * <code>N</code>. <code>μ</code>, the Greek letter <em>mu</em> (pronounced “mew”), is used here to describe the <strong><em>coefficient of friction</em></strong>. The coefficient of friction establishes the strength of a friction force for a particular surface. The higher it is, the stronger the friction; the lower, the weaker. A block of ice, for example, will have a much lower coefficient of friction than, say, sandpaper. Since this is a pretend p5.js world, I can arbitrarily set the coefficient based on how much friction I want to simulate.</p>
<pre class="codesplit" data-code-language="javascript">let c = 0.01;</pre><a data-type="indexterm" data-primary="friction" data-secondary="normal force"></a><a data-type="indexterm" data-primary="normal force"></a>
<p>Now for the second part: <code>N</code>. <code>N</code> refers to the <strong><em>normal force</em></strong>, the force perpendicular to the objects motion along a surface. Think of a vehicle driving along a road. The vehicle pushes down against the road with gravity, and Newtons third law tells us that the road in turn pushes back against the vehicle. Thats the normal force. The greater the gravitational force, the greater the normal force. As youll see in the next section, gravity is associated with mass, and so a lightweight sports car would experience less friction than a massive tractor trailer truck. With the diagram above, however, where the object is moving along a surface at an angle, computing the magnitude and direction of the normal force is a bit more complicated because it doesnt point in the opposite direction of gravity. Youd need to know something about angles and trigonometry.</p>
<p>All of these specifics are important; however, a “good enough” simulation can be achieved without them. I can, for example, make friction work with the assumption that the normal force will always have a magnitude of 1. When I get into trigonometry in the next chapter, you could return to this question and make the friction example more sophisticated. Therefore:</p>
<pre class="codesplit" data-code-language="javascript">let normal = 1;</pre>
<p>Now that I have the magnitude and direction for friction, I can put it all together in code.</p>
<pre class="codesplit" data-code-language="javascript">let c = 0.1;
let normal = 1;
//{!1} Calculate the magnitude of friction (really just an arbitrary constant).
let frictionMag = c * normal;
let friction = mover.velocity.copy();
friction.mult(-1);
friction.normalize();
// Take the unit vector and multiply it by magnitude and this is the force vector!
friction.mult(frictionMag);
</pre>
<p>The above code calculates a friction force, but doesnt answer the question of <em>when</em> to apply it? There is no answer to this question, of course, given this is all a made-up world visualized in a two dimensional p5.js canvas. Ill make the aribitrary, but logical, decision to apply friction when the circle comes into contact with the bottom of the canvas by adding a function to the <code>Mover</code> class called <code>contactEdge()</code></p>
<pre class="codesplit" data-code-language="javascript">contactEdge() {
// The mover is touching the edge when it's within one pixel
return (this.position.y > height - this.radius - 1);
}</pre>
<p>This is a good time for me to also mention that the actual bouncing off the edge here simulates an “idealized elsatic collision”, meaning no kinetic energy is lost when the circle and the edge collide. This is rarely true in the real world, pick up a tennis ball and drop it against any surface and the height at which it bounces will slowly lower until it rests against the ground. There are many factors at play here (including air resistence which I will cover in the next section), but a quick way to simulate an inelastic collision is by reducing the magnitude of velocity by a percentage with each bounce.</p>
<pre class="codesplit" data-code-language="javascript">bounceEdges() {
// A new variable to simulate an inelastic collision
// 10% of the velocity's x or y component is lost
let bounce = -0.9;
if (this.position.y > height - this.radius) {
this.position.y = height - this.radius;
this.velocity.y *= bounce;
}
}</pre>
<p>Finally, I can add all these pieces to the “forces” example, and simulate the object experiencing three forces: wind (when mouse is clicked), gravity (always), and now friction (when in contact with the bottom of the canvas):</p>
<div data-type="example">
<h3>Example 2.4: Including friction</h3>
<figure>
<img src="images/02_forces/02_forces_7.png" alt="The circle eventually rolls to a stop with friction.">
<figcaption>The circle eventually rolls to a stop with friction.</figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">function draw() {
background(51);
let gravity = createVector(0, 1);
//{!1} I should scale by mass to be more accurate, but this example only has one circle
mover.applyForce(gravity);
if (mouseIsPressed) {
let wind = createVector(0.5, 0);
mover.applyForce(wind);
}
if (mover.contactEdge()) {
//{!5 .bold}
let c = 0.1;
let friction = mover.velocity.copy();
friction.mult(-1);
friction.setMag(c);
//{!1 .bold} Apply the friction force vector to the object.
mover.applyForce(friction);
}
mover.update();
mover.display();
mover.checkEdges();
}</pre>
<p>Running this example, youll notice that the circle eventually comes to rest. You can make this happen more or less quickly by varying the coefficient of friction as well as the percentage speed loss in the <code>bounceEdges()</code> function.</p>
<div data-type="exercise">
<h3>Exercise 2.x</h3>
<p>Add a second object to example 2.x. How do you handle having two objects of different masses? What if each object has its own coefficient of friction relative to the bottom surface? Does it make sense to encapsulate the friction force calculation into a Mover method? Try an array of mover objects.</p>
</div>
<div data-type="exercise">
<h3>Exercise 2.x</h3>
<p>Instead of wind, can you add functionality to this example that allows you to toss the circle with mouse interaction?</p>
</div>
<h2>2.8 Air and Fluid Resistance</h2>
<figure>
<img src="images/02_forces/02_forces_8.png" alt="Figure 2.4">
<figcaption>Figure 2.4</figcaption>
</figure><a data-type="indexterm" data-primary="drag force"></a><a data-type="indexterm" data-primary="fluid resistance" data-secondary="modeling"></a><a data-type="indexterm" data-primary="forces" data-secondary="fluid resistance"></a><a data-type="indexterm" data-primary="natural phenomena" data-secondary="fluid resistance" data-tertiary="modeling"></a><a data-type="indexterm" data-primary="viscous force"></a>
<p>Friction also occurs when a body passes through a liquid or gas. This force has many different names, all really meaning the same thing: <em>viscous force</em>, <em>drag force</em>, <em>fluid resistance</em>. While the result is ultimately the same as our previous friction examples (the object slows down), the calculation of a drag force is slightly different. Lets look at the formula:</p>
<div data-type="equation">F_d = - \frac{1}{2}\rho\nu^2 A C_d\hat{\nu}</div>
<p>Now lets break this down and see what we really need for an effective simulation in p5, making a simpler formula in the process.</p>
<ul>
<li><span data-type="equation">F_d</span> refers to <em>drag force</em>, the vector you ultimately want to compute and pass into the <code>applyForce()</code> method.</li>
<li>- 1/2 is a constant: -0.5. This is irrelevant in terms of a p5.js world, as I will be making up values for other constants anyway. However, the fact that it is negative is important, as it indicates that the force points in the opposite direction of velocity (just as with friction).</li>
</ul><a data-type="indexterm" data-primary="rho (ρ)"></a><a data-type="indexterm" data-primary="friction" data-secondary="rho (ρ)"></a>
<ul>
<li><span data-type="equation">\rho</span> is the Greek letter <em>rho</em>, and refers to the density of the liquid, something you dont need to worry about at the moment. For my example I will consider this to have a constant value of 1.</li>
<li><span data-type="equation">v</span> refers to the speed of the object moving. OK, youve got this one! The objects speed is the magnitude of the velocity vector: <code>velocity.mag()</code>. And <span data-type="equation">v^2</span> just means <span data-type="equation">v</span> squared or <span data-type="equation">v \times v</span>.</li>
<li><span data-type="equation">A</span> refers to the frontal area of the object that is pushing through the liquid (or gas). An aerodynamic Lamborghini, for example, will experience less air resistance than a boxy Volvo. Nevertheless, for a basic simulation, I will consider the object to be spherical and ignore this element.</li>
<li><span data-type="equation">C_d</span> is the coefficient of drag, exactly the same as the coefficient of friction (μ). This constant will determine the relative strength of the drag force.</li>
<li><span data-type="equation">\hat{v}</span> Look familiar? It should. This refers to the velocity unit vector, i.e. <code>velocity.normalize()</code>. Just like with friction, drag is a force that points in the opposite direction of velocity.</li>
</ul>
<p>Now that Ive analyzed each of these components and determined what is needed for a simple simulation, I can reduce the formula to:</p>
<figure>
<img src="images/02_forces/02_forces_9.png" alt="Figure 2.5: My simplified drag force formula">
<figcaption>Figure 2.5: My simplified drag force formula </figcaption>
</figure>
<p>or:</p>
<pre class="codesplit" data-code-language="javascript">let c = 0.1;
let speed = this.velocity.mag();
//{!1} Part 1 of the formula (magnitude): Cd * v2
let dragMagnitude = c * speed * speed;
let drag = this.velocity.copy();
//{!1} Part 2 of the formula (direction): -1 * velocity
drag.mult(-1);
drag.normalize();
// Magnitude and direction together!
drag.mult(dragMagnitude);</pre><a data-type="indexterm" data-primary="friction" data-secondary="applying to an object"></a>
<p>Lets implement this force in the <code>Mover</code> example with an added feature. For the friction example, I enabled the force whenever the object was in contact with the bottom edge of the canvas. Whenever the object was in contact, friction would slow it down. Here, I will introduce an element to the environment—a “liquid” that the mover passes through. The <code>Liquid</code> object will be drawn as a rectangle with position, width, height, and “coefficient of drag”—i.e., is it easy for objects to move through it (like air) or difficult (like molasses)? In addition, it will a <code>display()</code> method and more.</p>
<pre class="codesplit" data-code-language="javascript">class Liquid {
constructor(x, y, w, h, c) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
//{!1} The liquid object includes a variable defining its coefficient of drag.
this.c = c;
}
display() {
noStroke();
fill(175);
rect(this.x, this.y, this.w, this.h);
}
}</pre>
<p>The sketch also includes a variable <code>liquid</code> that is initialized in <code>setup()</code>.</p>
<pre class="codesplit" data-code-language="javascript">let liquid;
function setup() {
//{!1} Initialize a Liquid object. Note the coefficient is low (0.1), otherwise the object would come to a halt fairly quickly (which may someday be the effect you want).
liquid = new Liquid(0, height/2, width, height/2, 0.1);
}</pre>
<p>Now comes an interesting question: how does the <code>Mover</code> object talk to the <code>Liquid</code> object? In other words, I want to implement the following:</p>
<p><em>When a mover passes through a liquid it experiences a drag force.</em></p>
<p>…or in object-oriented speak:</p>
<pre class="codesplit" data-code-language="javascript">// If a Mover is inside a Liquid, apply the drag force.
if (liquid.contains(mover) {
liquid.drag(mover);
} </pre>
<p>The above code serves as instructions for what I need to add to the <code>Liquid</code> class: (1) a function that determines if a <code>Mover</code> object is inside the <code>Liquid</code> objects area, and (2) a function that computes and applies a drag force on that mover.</p>
<p>The first is easy; I can use a Boolean expression to determine if the position vector rests inside the rectangle defined by the liquid.</p>
<pre class="codesplit" data-code-language="javascript">contains(mover) {
// Store position in a separate variable to make the code more readable
let pos = mover.position;
//{.offset-top} This Boolean expression determines if the position vector is contained within the rectangle defined by the Liquid class.
return (pos.x > this.x &#x26;&#x26; pos.x &#x3C; this.x + this.w &#x26;&#x26; pos.y > this.y &#x26;&#x26; pos.y &#x3C; this.y + this.h);
}</pre>
<p>The <code>drag()</code> function requires a bit more complexity; however, Ive written the code for it already. This is an implementation of the drag formula! The drag force is equal to <em>the coefficient of drag multiplied by the speed of the mover squared in the opposite direction of velocity</em>.</p>
<pre class="codesplit" data-code-language="javascript"> drag(mover) {
let speed = mover.velocity.mag();
// The force's magnitude: Cd * v~2~
let dragMagnitude = this.c * speed * speed;
// The force's direction: -1 * velocity
let drag = mover.velocity.copy();
// Finalize the force: magnitude and direction together.
drag.setMag(dragMagnitude);
//{!1} Apply the force.
mover.applyForce(drag);
}</pre>
<p>And with these two functions added to the <code>Liquid</code> class, Im ready to put it all together! In the following example, Ill expand the code to use an array (with mover objects spaced out evenly) to demonstrate how the drag force behaves with objects of variable mass.</p>
<div data-type="example">
<h3>Example 2.5: Fluid Resistance</h3>
<figure>
<img src="images/02_forces/02_forces_10.png" alt="">
<figcaption> </figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">let movers = [];
let liquid;
function setup() {
createCanvas(360, 640);
// Initializee an array of Mover objects
for (let i = 0; i &#x3C; 10; i++) {
// Random mass
let mass = random(0.1, 5);
// x value is spaced out evenly according to i
movers[i] = new Mover(i * 20, 0, mass);
}
liquid = new Liquid(0, height/2, width, height/2, 0.1);
}
function draw() {
background(255);
liquid.display();
for (let i = 0; i &#x3C; movers.length; i++) {
if (liquid.contains(movers[i])) {
liquid.drag(movers[i]);
}
//{!2} Note that gravity is scaled according to mass.
let m = 0.1 * movers[i].mass;
let gravity = createVector(0, m);
movers[i].applyForce(gravity);
movers[i].update();
movers[i].display();
movers[i].checkEdges();
}
}</pre>
<p>Running the example, you may notice that it appears to simulate objects falling into water. The objects only slow down when crossing through the gray area at the bottom of the window (representing the liquid). Youll also notice that the smaller objects slow down a great deal more than the larger objects. Remember Newtons second law? <code>A</code> = <code>F</code> / <code>M</code>. Acceleration equals force <em>divided</em> by mass. A massive object will accelerate less. A smaller object will accelerate more. In this case, the acceleration is the “slowing down” due to drag. The smaller objects slow down at a greater rate than the larger ones.</p>
<div data-type="exercise">
<h3>Exercise 2.5</h3>
<p>Take a look at the formula for drag again: <strong><em>drag force = coefficient * speed * speed</em></strong>. The faster an object moves, the greater the drag force against it. In fact, an object not moving (velocity of zero) experiences no drag at all. Expand the example to drop the objects from variable height. How does this affect the drag as they hit the liquid?</p>
</div>
<div data-type="exercise">
<h3>Exercise 2.6</h3>
<p>The formula for drag also included surface area. Can you create a simulation of boxes falling into water with a drag force dependent on the length of the side hitting the water?</p>
</div>
<div data-type="exercise">
<h3>Exercise 2.7</h3>
<p>In addition to drag being a force in opposition to the velocity vector, a drag force can be also be perpendicular. This is known as “lift-induced drag” and will cause an airplane with an angled wing to rise in altitude. Try creating a simulation of lift.</p>
</div>
<h2>2.9 Gravitational Attraction</h2><a data-type="indexterm" data-primary="gravity" data-secondary="modeling"></a><a data-type="indexterm" data-primary="natural phenomena" data-secondary="gravity"></a>
<figure class="half-width-right">
<img src="images/02_forces/02_forces_11.png" alt="Figure 2.6">
<figcaption>Figure 2.6</figcaption>
</figure>
<p>Probably the most famous force of all is gravity. We humans on earth think of gravity as an apple hitting Isaac Newton on the head. Gravity means that stuff falls down. But this is only <em>our</em> experience of gravity. In truth, just as the earth pulls the apple towards it due to a gravitational force, the apple pulls the earth as well. The thing is, the earth is just so freaking big that it overwhelms all the other gravity interactions. Every object with mass exerts a gravitational force on every other object (this is Newtons third law). And there is a formula for calculating the strengths of these forces, as depicted in Figure 2.6.</p>
<p>Lets examine this formula a bit more closely.</p><a data-type="indexterm" data-primary="forces" data-secondary="universal gravitational constant"></a><a data-type="indexterm" data-primary="gravity" data-secondary="universal gravitational constant"></a><a data-type="indexterm" data-primary="universal gravitational constant"></a>
<ul>
<li><code>F</code> refers to the gravitational force, the vector to compute and pass into the <code>applyForce()</code> function.</li>
<li><code>G</code> is the <em>universal gravitational constant</em>, which in our world equals 6.67428 x 10-11 meters cubed per kilogram per second squared. This is a pretty important number if you are a human being. Its not an important number if you are a shape wandering around a p5.js canvas. Again, its a constant can be used to to make the forces in the world stronger or weaker. Just making it equal to one and ignoring it isnt such a terrible choice either.</li>
<li><code>m1</code> and <code>m2</code> are the masses of objects 1 and 2. As I initially did with Newtons second law (<span data-type="equation">\vec{F} = M \times \vec{A}</span>), mass is also something I could choose to ignore. After all, shapes drawn on the screen dont have a physical mass. However, if you keep track of this value, you can create more interesting simulations in which “bigger” objects exert a stronger gravitational force than smaller ones.</li>
<li><span data-type="equation">\hat{r}</span> refers to the unit vector pointing from object 1 to object 2. As youll see in a moment, this direction vector can be computed by subtracting the position of one object from the other.</li>
<li><span data-type="equation">r^2</span> refers to the distance between the two objects squared. Lets take a moment to think about this a bit more. With everything on the top of the formula—<code>G</code>, <code>m1</code>, <code>m2</code>—the bigger its value, the stronger the force. Big mass, big force. Big <code>G</code>, big force. Now, when you divide by something, the the opposite occurs. The strength of the force is inversely proportional to the distance squared. The <em>farther away</em> an object is, the <em>weaker</em> the force; the <em>closer</em>, the <em>stronger</em>.</li>
</ul>
<p>Hopefully by now the formula makes some sense. Ive shown you a diagram and dissected the individual components of the formula. Now its time to figure out how to translate the math into p5.js code. Lets make the following assumptions.</p>
<p>We have two objects, and:</p><a data-type="indexterm" data-primary="gravity" data-secondary="implementing model of"></a><a data-type="indexterm" data-primary="natural phenomena" data-secondary="gravity"></a>
<ol>
<li>Each object has a position: <code>position1</code> and <code>position2</code>.</li>
<li>Each object has a mass: <code>mass1</code> and <code>mass2</code>.</li>
<li>There is a variable <code>G</code> for the universal gravitational constant.</li>
</ol>
<p>Given these assumptions, I want to compute a vector, the force of gravity. Ill do it in two parts. First, Ill compute the direction of the force <span data-type="equation">\hat{r}</span> in the formula above. Second, Ill calculate the strength of the force according to the masses and distance.</p>
<figure class="half-width-right">
<img src="images/02_forces/02_forces_12.png" alt="Figure 2.7">
<figcaption>Figure 2.7</figcaption>
</figure>
<p>Remember in Chapter 1, when I created an example of an object accelerating towards the mouse? (See Figure 2.7.)</p>
<p>A vector is the difference between two points. To make a vector that points from the circle to the mouse, I subtracted one point from another:</p>
<pre class="codesplit" data-code-language="javascript">let dir = p5.Vector.sub(mouse, position);</pre>
<p>In this case, the direction of the attraction force that object 1 exerts on object 2 is equal to:</p>
<pre class="codesplit" data-code-language="javascript">let dir = p5.Vector.sub(position1, position2);
dir.normalize();</pre>
<p>Dont forget that since you want a unit vector, a vector that indicates direction only, its important to <em>normalize</em> the vector after subtracting the positions. (Later, I might skip this step and use <code>setMag()</code> instead.)</p>
<p>OK, Ive got the direction of the force. Now I need to compute the magnitude and scale the vector accordingly.</p>
<pre class="codesplit" data-code-language="javascript">let magnitude = (G * mass1 * mass2) / (distance * distance);
dir.mult(magnitude);</pre>
<figure>
<img src="images/02_forces/02_forces_13.png" alt="Figure 2.8">
<figcaption>Figure 2.8</figcaption>
</figure>
<p>The only problem is that I dont know the distance. <code>G</code>, <code>mass1</code>, and <code>mass2</code> are all givens, but I need to calculate <code>distance</code> before the above code will work. Didnt I just make a vector that points all the way from one position to another? Wouldnt the length of that vector be the distance between the two objects?</p>
<p>Indeed, if I add one more line of code and grab the magnitude of that vector before normalizing it, then Ill have the distance. And this time, Ill skip the <code>normalize()</code> step and use <code>setMag()</code>.</p>
<pre class="codesplit" data-code-language="javascript">//{!1} The vector that points from one object to another
let force = p5.Vector.sub(position1, position2);
//{!1} The length (magnitude) of that vector is the distance between the two objects.
const distance = force.mag();
//{!1} Use the formula for gravity to compute the strength of the force.
let magnitude = (G * mass1 * mass2) / (distance * distance);
//{!1} Normalize and scale the force vector to the appropriate magnitude.
force.setMag(magnitude);</pre>
<p>Note that I also renamed the vector <code>dir</code> to <code>force</code>. After all, when the calculations are finished, the vector I started with ends up being the actual force vector I wanted all along.</p>
<p>Now that Ive worked out the math and code for calculating an attractive force (emulating gravity), lets turn our attention to applying this technique in the context of an actual p5.js sketch. In Example 2.1, I set up the foundation for all of these examples, a <code>Mover</code> class—a template for making objects with <code>p5.Vector</code> objects for position, velocity, and acceleration as well as an <code>applyForce()</code> method. Lets take this exact class and put it in a sketch with:</p>
<figure>
<img src="images/02_forces/02_forces_14.png" alt="Figure 2.9">
<figcaption>Figure 2.9</figcaption>
</figure>
<ul>
<li>A single <code>Mover</code> object.</li>
<li>A single <code>Attractor</code> object (a new class that will have a fixed position).</li>
</ul>
<p>The <code>Mover</code> object will experience a gravitational pull towards the <code>Attractor</code> object, as illustrated in Figure 2.9.</p>
<p>Ill start with the most basic <code>Attractor</code> class—giving it a position and a mass, along with a function to display itself (tying mass to size).</p>
<pre class="codesplit" data-code-language="javascript">class Attractor {
//{!2} The Attractor is an object that doesnt move. I just need a mass and a position.
constructor() {
this.position = createVector(width/2, height/2);
this.mass = 20;
}
display() {
stroke(0);
fill(175, 200);
ellipse(this.position.x, this.position.y, this.mass * 2);
}
}</pre>
<p>And in the sketch, I can add an instance of the <code>Attractor</code> class.</p>
<pre class="codesplit" data-code-language="javascript">let mover;
let attractor;
function setup() {
createCanvas(640, 360);
mover = new Mover(300, 100, 5);
//{!1} Initialize Attractor object.
attractor = new Attractor();
}
function draw() {
background(255);
//{!1} Display Attractor object.
attractor.display();
mover.update();
mover.display();
}</pre><a data-type="indexterm" data-primary="object" data-secondary="interaction between"></a>
<p>This is a good start: a sketch with a <code>Mover</code> object and an <code>Attractor</code> object, made from classes that handle the variables and behaviors of movers and attractors. The last piece of the puzzle is how to get one object to attract the other. How do these two objects communicate with each other?</p>
<p>There are a number of ways this could be done. Here are just a few possibilities.</p>
<p>and so on. . .</p>
<p>Its good to look at a range of options, and you could probably make arguments for each of the above possibilities. Id like to at least discard the first one, since I lean towards an object-oriented approach which in my view is a better choice over an arbitrary function not tied to either the <code>Mover</code> or <code>Attractor</code> class. Whether you pick option 2 or option 3 is the difference between saying “The attractor attracts the mover” or “The mover is attracted to the attractor.” Number 4 is really my favorite, at least in terms of the exmaples here. After all, I spent a lot of time working out the <code>applyForce()</code> function, and I think the examples are clearer continuing with the same methodology.</p>
<p>In other words, where I once wrote:</p>
<pre class="codesplit" data-code-language="javascript">// Made-up force
let force = createVector(0, 0.1);
mover.applyForce(force);</pre>
<p>I now have:</p>
<pre class="codesplit" data-code-language="javascript">//{!1 .bold} Attraction force between two objects
let force = attractor.attract(mover);
mover.applyForce(force);</pre>
<p>And so the <code>draw()</code> function can be written as:</p>
<pre class="codesplit" data-code-language="javascript">function draw() {
background(255);
//{!2 .bold} Calculate attraction force and apply it.
let force = attractor.attract(mover);
mover.applyForce(force);
mover.update();
attractor.display();
mover.display();
}</pre>
<p>Im almost there. Since I decided to put the <code>attract()</code> function inside of the <code>Attractor</code> class, I still need to actually write that function. The function should receive a <code>Mover</code> object and return a Vector, i.e.:</p>
<pre class="codesplit" data-code-language="javascript">attract(m) {
// all the math
return ______________;
}</pre>
<p>And what goes inside that function? All of that nice math for gravitational attraction!</p>
<pre class="codesplit" data-code-language="javascript">attract(mover) {
//{!1} What's the force's direction?
let force = p5.Vector.sub(this.position, mover.position);
let distance = force.mag();
//{!2} What's the force's magnitude?
float strength = (this.G * this.mass * m.mass) / (distance * distance);
force.setMag(strength);
//{!1} Return the force so that it can be applied!
return force;
}</pre><a data-type="indexterm" data-primary="gravity" data-secondary="placing limits on model of"></a>
<p>And Im done. Sort of. Almost. Theres one small kink I need to work out. Lets look at the above code again. See that symbol for divide, the slash? Whenever you have one of these, you should ask yourself the question: What would happen if the distance happened to be a really, really small number or (even worse!) zero??! Well, you cant divide a number by 0, and if you were to divide a number by something like 0.0001, that is the equivalent of multiplying that number by 10,000! Yes, this is the real-world formula for the strength of gravity, but p5.js is not the real world. And in the p5.js world, the mover could end up being very, very close to the attractor and the force could become so strong the mover would fly way off the canvas. And so with this formula, its can be useful to be practical and constrain the range of what <code>distance</code> can actually be. Maybe, no matter where the <code>Mover</code> actually is, you should never consider it less than 5 pixels or more than 25 pixels away from the attractor.</p>
<pre class="codesplit" data-code-language="javascript"> distance = constrain(distance, 5, 25);</pre>
<p>For the same reason that you need to constrain the minimum distance, its useful to do the same with the maximum. After all, if the mover were to be, say, 500 pixels from the attractor (not unreasonable), that is the equivalent of dividing the force by 250,000. That force might end up being so weak that its almost as if its not applied at all.</p>
<p>Now, its really up to you to decide what behaviors you want. But in the case of, “I want reasonable-looking attraction that is never absurdly weak or strong,” then constraining the distance is a good technique.</p>
<p>The <code>Mover</code> class hasnt changed at all, so lets just look at the main sketch and the <code>Attractor</code> class as a whole, adding a variable <code>G</code> for the universal gravitational constant. (On the website, youll find that this example also has code that allows you to move the <code>Attractor</code> object with the mouse.)</p>
<div data-type="example">
<h3>Example 2.6: Attraction</h3>
<figure>
<img src="images/02_forces/02_forces_15.png" alt="">
<figcaption> </figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">//{!2} A Mover and an Attractor
let mover;
let attractor;
function setup() {
size(640, 360);
mover = new Mover(300, 100, 5);
attractor = new Attractor();
}
function draw() {
background(255);
//{!2} Apply the attraction force from the Attractor on the Mover.
const force = a.attract(m);
mover.applyForce(force);
mover.update();
attractor.display();
mover.display();
}
class Attractor {
constructor() {
this.position = createVector(width/2, height/2);
this.mass = 20;
this.G = 0.4;
}
attract(mover) {
let force = p5.Vector.sub(position, mover.position);
let distance = force.mag();
//{!1} Remember, we need to constrain the distance so that our circle doesn't spin out of control.
distance = constrain(distance, 5, 25);
let strength = (this.G * this.mass * m.mass) / (distance * distance);
force.setMag(strength);
return force;
}
display() {
stroke(0);
fill(175,200);
ellipse(this.position.x, this.position.y, this.mass * 2);
}
}</pre>
<p>And you could, of course, expand this example to use an array for many <code>Mover</code> objects, just as I did with drag:</p>
<div data-type="example">
<h3>Example 2.7: Attraction with many Movers</h3>
<figure>
<img src="images/02_forces/02_forces_16.png" alt="">
<figcaption> </figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">//{!1} Now we have 10 Movers!
let movers = [];
let attractor;
function setup() {
size(640, 360);
for (let i = 0; i &#x3C; 10; i++) {
//{!1 .offset-top} Each Mover is initialized randomly.
movers[i] = new Mover(random(width), random(height), random(0.1, 2));
}
attractor = new Attractor();
}
function draw() {
background(255);
aattractor.display();
for (let i = 0; i &#x3C; movers.length; i++) {
//{!1} Calculate an attraction force for each Mover object.
let force = a.attract(movers[i]);
movers[i].applyForce(force);
movers[i].update();
movers[i].display();
}
}</pre>
<div data-type="exercise">
<h3>Exercise 2.8</h3>
<p>In the example above, there is a system (i.e. array) of <code>Mover</code> objects and one <code>Attractor</code> object. Build an example that has systems of both movers and attractors. What if you make the attractors invisible? Can you create a pattern/design from the trails of objects moving around attractors? See the Metropop Denim project by Clayton Cubitt and Tom Carden for an example.</p>
</div>
<div data-type="exercise">
<h3>Exercise 2.9</h3>
<p>Its worth noting that gravitational attraction is a model you can follow to invent your own forces. This chapter isnt suggesting that you should exclusively create sketches that use gravitational attraction. Rather, you should be thinking creatively about how to design your own rules to drive the behavior of objects. For example, what happens if you design a force that is weaker the closer it gets and stronger the farther it gets? Or what if you design your attractor to attract faraway objects, but repel close ones?</p>
</div>
<h2>2.10 Everything Attracts (or Repels) Everything</h2>
<p>Hopefully, you found it helpful that I started with a simple scenario—<em>one object attracts another object</em><code></code>and moved on to <em>one object attracts many objects</em>. However, its likely that you are going to find yourself in a slightly more complex situation: <em>many objects attract each other</em>. In other words, every object in a given system attracts every other object in that system (except for itself).</p>
<p>Youve really done almost all of the work for this already. Lets consider a p5.js sketch with an array of <code>Mover</code> objects:</p>
<pre class="codesplit" data-code-language="javascript">let movers = [];
function setup() {
createCanvas(640, 360);
for (let i = 0; i &#x3C; 10; i++) {
movers[i] = new Mover(random(width), random(height), random(0.1, 2));
}
}
function draw() {
background(255);
for (let i = 0; i &#x3C; movers.length; i++) {
movers[i].update();
movers[i].display();
}
}</pre>
<p>The <code>draw()</code> function is where I need to work some magic. Currently, Im saying: “for every mover <code>i</code>, update and display yourself.” Now what I need to say is: “for every mover <code>i</code>, be attracted to every other mover <code>j</code>, and update and display yourself.”</p>
<p>To do this, I need to nest a second loop.</p>
<pre class="codesplit" data-code-language="javascript"> for (let i = 0; i &#x3C; movers.length; i++) {
//{!1} For every Mover, check every Mover!
for (let j = 0; j &#x3C; movers.length; j++) {
let force = movers[j].attract(movers[i]);
movers[i].applyForce(force);
}
movers[i].update();
movers[i].display();
}</pre>
<p>In the previous example, I had an <code>Attractor</code> object with a function named <code>attract()</code>. Now, since there are movers attracting movers, all I need to do is copy the <code>attract()</code> function into the <code>Mover</code> class itself.</p>
<pre class="codesplit" data-code-language="javascript">class Mover {
//[inline] All the other stuff from before plus. . .
//{!1} The Mover now knows how to attract another Mover.
attract(other) {
const force = p5.Vector.sub(this.position, other.position);
let distance = force.mag();
distance = constrain(distance, 5, 25);
force.normalize();
// Note to add a value for gravity in the Mover class: this.G
let strength = (this.G * this.mass * m.mass) / (distance * distance);
force.setMag(strength);
return force;
}
}</pre>
<p>Of course, theres one small problem. When every mover <code>i</code> attracts every mover <code>j</code>, what about when <code>i</code> equals <code>j</code>? Should mover #3 attract mover #3? The answer, of course, is no. If there are five objects, you only want mover #3 to attract 0, 1, 2, and 4, skipping itself. And so, I finish this example by adding a conditional statement to skip applying the force when i equals j.</p>
<div data-type="example">
<h3>Example 2.8: Mutual attraction</h3>
<figure>
<img src="images/02_forces/02_forces_17.png" alt="">
<figcaption> </figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">let movers = [];
function setup() {
createCanvas(640, 360);
for (let i = 0; i &#x3C; 20; i++) {
movers[i] = new Mover(random(width), random(height), random(0.1, 2));
}
}
function draw() {
background(255);
for (let i = 0; i &#x3C; movers.length; i++) {
for (let j = 0; j &#x3C; movers.length; j++) {
//{!1} Don't attract yourself!
if (i !== j) {
let force = movers[j].attract(movers[i]);
movers[i].applyForce(force);
}
}
movers[i].update();
movers[i].display();
}
}</pre>
<div data-type="exercise">
<h3>Exercise 2.10</h3>
<p>Change the attraction force in Example 2.8 to a repulsion force. Can you create an example in which all of the <code>Mover</code> objects are attracted to the mouse, but repel each other? Think about how you need to balance the relative strength of the forces and how to most effectively use distance in your force calculations.</p>
</div>
<div data-type="project">
<h3>The Ecosystem Project</h3>
<p>Step 2 Exercise:</p>
<p>Incorporate the concept of forces into your ecosystem. Try introducing other elements into the environment (food, a predator) for the creature to interact with. Does the creature experience attraction or repulsion to things in its world? Can you think more abstractly and design forces based on the creatures desires or goals?</p>
</div>
</section>