noc-book-2/content/02_forces.html
2023-02-11 21:02:03 +00:00

990 lines
No EOL
86 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 id="chapter-2-forces">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 resembled 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 responding to a variety of environmental forces.</p>
<p><span class="highlight">A physics engine is defined as a computer program (or code library) that simulates the behavior of objects in a physical environment. In our case, the objects are two-dimensional shapes and the environment is a rectangular canvas. Physics engines can be developed to be highly precise (requiring high performance computing) or real-time (using simple and fast algorithms).</span></p>
<h2 id="21-forces-and-newtons-laws-of-motion">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>Lets begin by taking a conceptual look at what it means to be a force in the real world. Just like the word “vector,” the term “force” can have a variety of meanings. It can indicate a powerful physical intensity, as in “They pushed the boulder with great force” or a powerful influence, as in “They are a force to be reckoned with!” The definition of <strong><em>force</em></strong> that I am interested in for this chapter about is more formal and comes from Sir Isaac Newtons three 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>Hopefully you 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! Well start from there, and build up an understanding of the rest of the definition as we go.</p>
<p>Lets define Newtons three laws of motion in relation to the concept of a force.</p>
<h3 id="newtons-first-law">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>When 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 slow down or stop. This theory was borne out through observation of the world. For example, if you toss a ball, it falls to the ground and eventually stops moving, seemingly because the force of the toss is no longer being applied.</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 older theory, of course, is not true. As Newton established, in the absence of any forces, no force is required to keep an object moving. When an object (such as the aforementioned ball) is tossed in the earths atmosphere, its velocity changes because of unseen forces such as air resistance and gravity. 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, meaning the net force adds up to zero. This is often referred to as <strong><em>equilibrium</em></strong> (see Figure 2.1). 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 toy mouse 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 toy mouse 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>In other words, in a <code>Mover</code> class, the <code>update()</code> function should not apply any mathematical operations on the <code>velocity</code> vector unless there is a non-zero net force present!</p>
<h3 id="newtons-third-law">Newtons Third Law</h3><a data-type="indexterm" data-primary="Newton&#x27;s third law"></a>
<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. 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>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 objects 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 pointed down an icy hill, 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 what if, as in Figure 2.2, 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: Demonstrating Newtons third law of motion by pushing a heavy truck wearing roller skates.">
<figcaption>Figure 2.2: Demonstrating Newtons third law of motion by pushing a heavy truck wearing roller skates.</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><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>Considering p5.js again, I could restate Newtons third law as follows:</p>
<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 opposite force that B exerts on object A. You can calculate this other force as <code>p5.Vector.mult(f, -1)</code>.</span></p>
<p>Youll soon see that in the world of coding simulation, its often not necessary to stay true to Newtons third law. Sometimes, such as in the case of <a href="#example-26-attraction">gravitational attraction between bodies</a>, 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, the 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>
<h3 id="newtons-second-law">Newtons Second Law</h3><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, Newtons second law, 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. Consider what this means if you are pushed. The harder youre pushed, the faster youll move (accelerate). On the other hand, the bigger you are, the slower youll accelerate.</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 id="weight-vs-mass">Weight vs. Mass</h3>
<p>Mass in not to be confused with weight. The <strong><em>mass</em></strong> of an object is a measure of the amount of matter in the object (measured in kilograms). An object that has a mass of 1 kilogram on earth would have a mass of 1 kilogram on the moon.</p>
<p><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 weight as mass times the acceleration of gravity (<code>w = m * g</code>). Weight is measured in newtons. Because weight is tied to gravity, an object on the moon weighs one-sixth as much as it does on earth.</p>
<p>Related to mass is the concept of <strong><em>density</em></strong>, which is defined as the amount of mass per unit of volume (grams per cubic centimeter, for example).</p>
</div>
<p>In the world of p5.js, what is mass anyway? Arent we dealing with pixels? Lets start simple and 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 in a canvas. I said that <code>position</code> changes according to <code>velocity</code>, and <code>velocity</code> according to <code>acceleration</code>. Acceleration seemed to be 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 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 <code>wind</code> and <code>gravity</code> 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>
<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 an object that responds to wind and gravity forces. Consider this code:</p>
<pre class="codesplit" data-code-language="javascript">mover.applyForce(wind);
mover.applyForce(gravity);
mover.update();</pre>
<p>Imagine youre the computer for a moment. First, you call <code>applyForce()</code> with <code>wind</code>, and so the <code>Mover</code> objects acceleration is now assigned the vector <code>wind</code>. Second, you call <code>applyForce()</code> with <code>gravity</code>. Now the <code>Mover</code> objects acceleration is set to the <code>gravity</code> vector. Finally, you 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 an 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>
<h2 id="23-force-accumulation-net-force">2.3 Force Accumulation (”Net” Force)</h2><a data-type="indexterm" data-primary="forces" data-secondary="accumulation of"></a><a data-type="indexterm" data-primary="force accumulation"></a>
<p>The answer is that the forces must <strong><em>accumulate</em></strong>, or be added together. This is actually stated 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>In other words, acceleration is equal to the <em>sum of all forces</em> divided by mass. At any given moment, there might be 1, 2, 6, 12, or 303 forces acting on an object. As long as the object knows how to add them together (accumulate them), it doesnt matter how many forces there are. The sum total will give you the objects acceleration (again, ignoring mass). This makes perfect sense. After all, as you saw in Newtons first law, if all the forces acting on an add up to zero, the object experiences an equilibrium state (that is, no acceleration).</p>
<p>I can now revise the <code>applyForce()</code> method to take force accumulation into account.</p>
<pre class="codesplit" data-code-language="javascript">applyForce(force) {
//{!1} Newton's second law, but with force accumulation, adding all input forces to acceleration.
this.acceleration.add(force);
}</pre>
<p>Im not finished just yet, though. 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 (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 when holding 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 mouse is released, the wind should stop, and according to Newtons first law, the object should continue moving at a constant velocity. However, if I forget to reset acceleration to zero, the gust of wind will still be in effect. Even worse, it will 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. An object 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>
<p>This is also a good spot for me to note that in addition to assuming that all objects have a mass equal to 1, I am choosing to ignore yet another detail which is typically part of a physics engine. I am referring to the time step which can affect the accuracy and behavior of a simulation. Instead of incorporating the time step as a variable (often denoted as <code>dt</code>), I am choosing to to assume that every cycle through <code>draw()</code>represents a time step of one. While this assumption may not be the most accurate, it allows me to focus on the key principles of the simulation, and I will examine the impact of different time steps in greater detail in Chapter 6 when I cover other physics libraries.</p>
<div data-type="exercise">
<h3 id="exercise-21">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 id="24-factoring-in-mass">2.4 Factoring in 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>While smaller time steps is not something I plan to address here, I can and should expand on the concept of variable mass 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 to start is as easy as adding a <code>this.mass</code> 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 id="units-of-measurement">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. Sometimes you do want to take real-world units into consideration. However, in this chapter, Im going to stick with units of measurement 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”, the aforementioned time step).</p>
<p>In the case of mass, there isnt any unit of measurement to use. How much mass is in any given pixel? You might enjoy inventing your own p5.js unit of mass to associate with those values, like “10 moogs” or “10 yurkles.”</p>
<p>For demonstration purposes, Ill tie mass to pixels (the larger a circles diameter, the larger the mass). 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, Ill also note that mass should be tied to the formula for area of a circle: <span data-type="equation">\pi r^2</span> (more about <span data-type="equation">\pi</span> and circles in Chapter 3!)</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? I need to divide force by mass to apply 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, imagine youre the computer. Object <code>m1</code> receives the wind force—(1,0)—divides it by <code>mass</code> (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>Now you move 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)! Remember that 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 the 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 the force in its original state—(1,0). And so I must protect the original vector and make a copy of it before dividing by mass. Fortunately, the <code>p5.Vector</code> class has a convenient method for making a copy. <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();
//{!1} Divide the copy by mass!
f.div(this.mass);
this.acceleration.add(f);
}</pre>
<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>
<div data-type="exercise">
<h3 id="exercise-22">Exercise 2.2</h3>
<p>Theres another way you could write the <code>applyForce()</code> function, using the static method <code>div()</code> instead of <code>copy()</code>. Rewrite <code>applyForce()</code> using the static method. For help with this exercise, review static methods in <a href="/vectors#19-static-vs-non-static-functions">Chapter 1</a>.</p>
<pre class="codesplit" data-code-language="javascript">applyForce(force) {
let f = _______._______(_______,_______);
this.acceleration.add(f);
}</pre>
</div>
<h2 id="25-creating-forces">2.5 Creating Forces</h2><a data-type="indexterm" data-primary="forces" data-secondary="creating"></a>
<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 physical 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 an object <code>mover</code>, the code would read:</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 a <code>Mover</code> object (which in turn will apply it to its own acceleration). To finish off this example, Ill add one more force, gravity (pointing down), and only engage the wind force when the mouse is pressed.</p>
<div data-type="example">
<h3 id="example-21-forces">Example 2.1: Forces</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/4IRI8BEVE" data-example-path="examples/02_forces/example_2_1_forces"><img src="examples/02_forces/example_2_1_forces/screenshot.png"></div>
<figcaption>Click mouse to apply wind force.</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, youll probably want to do 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 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, 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);
}
show() {
stroke(0);
fill(175);
//{!1} Scaling the size according to mass.
circle(this.position.x, this.position.y, this.mass * 16);
}
// Somewhat arbitrarily, I have decided that an object bounces when it hits the edges ofthe canvas.
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 am doing so as a quic 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 written, I can create more than one <code>Mover</code> object.</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 <em>variable</em> mass that start at <em>variable</em> positions. A nice way to accomplish this is with constructor arguments.</p>
<pre class="codesplit" data-code-language="javascript">Mover(x, y, mass) {
//{!2} Now setting these variables with arguments
this.mass = mass;
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 the <code>x</code>, <code>y</code>, and <code>mass</code> 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 canvas, ones that start on the right, and everywhere in between.</p>
<pre class="codesplit" data-code-language="javascript">// A large Mover on the left side of the canvas
let moverA = new Mover(100, 30, 10);
// A smaller Mover on the right side of the canvas
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, and so on) and an array most likely makes more sense to manage large numbers of <code>Mover</code> objects. This is just a demonstration of the basics to get started. Ill introduce other techniques throughout this chapter and arrays will be covered in greater detail in Chapter 4.</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 id="example-22-forces-acting-on-two-objects">Example 2.2: Forces acting on two objects</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/ePLfo-OGu" data-example-path="examples/02_forces/example_2_2_forces_acting_on_two_objects"><img src="examples/02_forces/example_2_2_forces_acting_on_two_objects/screenshot.png"></div>
<figcaption>Click mouse to apply wind force.</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.show();
moverA.checkEdges();
moverB.update();
moverB.show();
moverB.checkEdges();
}</pre>
<p>When you run this code, notice how the small circle reaches the bottom of the window faster than the larger one. This is because of the 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 Earths gravitational pull! Ill get into why and how to correct this in the next section.</p>
<div data-type="exercise">
<h3 id="exercise-23">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, so that the closer it is, the stronger the force?</p>
</div>
<div data-type="exercise">
<h3 id="exercise-24">Exercise 2.4</h3>
<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>
</div>
<div data-type="exercise">
<h3 id="exercise-24-1">Exercise 2.4</h3>
<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>
</div>
<p></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?” Thats when modeling forces, instead of just making them up, enters the picture.</p><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>Consider again example 2.3. 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 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 gravity force by mass.</p>
<div data-type="example">
<h3 id="example-23-gravity-scaled-by-mass">Example 2.3: Gravity scaled by mass</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/0RiwMFOQ7" data-example-path="examples/02_forces/example_2_3_gravity_scaled_by_mass"><img src="examples/02_forces/example_2_3_gravity_scaled_by_mass/screenshot.png"></div>
<figcaption>Click mouse to apply wind force.</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. (Ive also included a solution to exercise 2.x in this example with the addition of a <code>radius</code> variable in the <code>Mover</code> class.)</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>
<h2 id="26-modeling-a-force">2.6 Modeling a Force</h2>
<p>Open up any high school physics textbook and you will find some diagrams and formulas describing different forces—gravity, electromagnetism, friction, tension, elasticity, and more. In this chapter Im going to look at two forces—friction and gravitational attraction—and consider how to model them with p5.js. The point Id like to make here is not that friction and gravity are fundamental forces that you always need in your simulations. Rather, I want to demonstrate these two forces as case studies for the following process:</p>
<ol>
<li>Understanding the concept behind a force</li>
<li>Deconstructing the forces formula into two parts:
<ol>
<li>How do you compute the forces direction?</li>
<li>How do you compute the forces magnitude?</li>
</ol>
</li>
<li>Translating that formula into p5.js code that calculates a vector to be passed through the <code>Mover</code>'s <code>applyForce()</code> function.</li>
</ol>
<p>If you can follow these steps with the 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 id="parsing-formulas">Parsing Formulas</h3>
<p>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 that formula because its just a few characters and symbols. 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 <a href="/introduction#i4-a-normal-distribution-of-random-numbers">Introduction</a>.</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 <span data-type="equation">\hat{v}</span>, 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: -, <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>
<h3 id="friction">Friction</h3><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>Figure 2.3 shows the formula for friction.</p>
<figure>
<img src="images/02_forces/02_forces_3.png" alt="Figure 2.3 Friction is a force that points in the opposite direction of the sleds velocity when sliding in contact with the hill.">
<figcaption>Figure 2.3 Friction is a force that points in the opposite direction of the sleds velocity when sliding in contact with the hill.</figcaption>
</figure><a data-type="indexterm" data-primary="friction" data-secondary="determining direction" data-tertiary="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. Figure 2.3 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 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 <span data-type="equation">\mu * N</span>. The Greek letter <em>mu</em> (<span data-type="equation">\mu</span>, 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 to scale the strength of the friction.</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. <span data-type="equation">N</span> 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, gravitational attraction is associated with mass, and so a lightweight sports car would experience less friction than a massive tractor trailer truck. In Figure 2.3, 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 complex 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. And so:</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>This 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 arbitrary, but logical, decision to apply friction when the circle comes into contact with the bottom of the canvas, which I can detect 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 elastic 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 resistance, which I will cover in the next section), but a quick way to simulate an inelastic collision is to reduce 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 id="example-24-including-friction">Example 2.4: Including friction</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/I4wC4aXd-E" data-example-path="examples/02_forces/example_2_4_including_friction"><img src="examples/02_forces/example_2_4_including_friction/screenshot.png"></div>
<figcaption>Click mouse to apply wind force.</figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">function draw() {
background(0);
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);
}
//{!1} Calling the new bounceEdges() function
mover.bounceEdges();
mover.update();
mover.display();
}</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 id="exercise-2x">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?</p>
</div>
<div data-type="exercise">
<h3 id="exercise-2x-1">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>
<h3 id="air-and-fluid-resistance">Air and Fluid Resistance</h3>
<figure>
<img src="images/02_forces/02_forces_4.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>air resistance</em>. While the result is ultimately the same as our previous friction examples (the object slows down), the calculation of a drag force and how it behaves is 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 to compute and pass into the <code>applyForce()</code> method.</li>
<li>- 1/2 is a constant: -0.5. While its an important factor to scale the force, its not terribly relevant here, as I will be making up values for other scaling constants. 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>, another constant that refers to the density of the liquid. Ill choose to ignore this at the moment and consider it 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>. (Ill note this assumes the liquid or gas is stationary and not moving, if you drop an object into a flowing river youd have to also take the relative speed of the water into account!)</li>
<li><span data-type="equation">A</span> refers to the frontal surface area of the object that is pushing through the liquid or gas. Consider a flat sheet of paper falling through the air and compare it to a sharp pencil pointed straight down. The pencil will experience less drag. Again, this is a constant and to keep things simple, I will consider all objects to have a spherical shape 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 my simulation, I can reduce the formula to:</p>
<figure>
<img src="images/02_forces/02_forces_5.png" alt="Figure 2.5: My simplified formula for a drag force">
<figcaption>Figure 2.5: My simplified formula for a drag force</figcaption>
</figure>
<p>While Ive written the formula above with <span data-type="equation">C_d</span> as the lone constant representing the “coefficient of drag”, I can also think of it as all of the constants combined ( <span data-type="equation">-1/2</span>, <span data-type="equation">\rho</span>, <span data-type="equation">A</span>). A more sophisticated simulation might consider them separately and could be considered an exercise for the reader.</p>
<p>The code follows:</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. 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 include <code>show()</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;
}
show() {
noStroke();
fill(175);
rect(this.x, this.y, this.w, this.h);
}
}</pre>
<p>The sketch also includes a variable <code>liquid</code>variable initialized in <code>setup()</code>.</p>
<pre class="codesplit" data-code-language="javascript">let liquid;
function setup() {
//{!1} Initialize a Liquid object. Note I'm choosing a coefficient is low (0.1), for a weaker effect. Try a stronger one!
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 the Liquid contains the Mover, 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 calculates and applies the appropriate 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 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 id="example-25-fluid-resistance">Example 2.5: Fluid Resistance</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/FknzcAaVh" data-example-path="examples/02_forces/example_2_5_fluid_resistance"><img src="examples/02_forces/example_2_5_fluid_resistance/screenshot.png"></div>
<figcaption>Click mouse to reset.</figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">let movers = [];
let liquid;
function setup() {
createCanvas(640, 360);
// 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.show();
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].show();
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 id="exercise-25">Exercise 2.5</h3>
<p>You might notice that if you set the coefficient of drag too high in the previous example, the circles may <em>bounce</em> off of the liquid! This is due to the inaccuracy of the large time steps that I mentioned earlier in this chapter. A drag force will cause an object to stop but never to reverse direction. How can you use the vector <code>limit()</code> function to correct this issue? You might also try dropping the objects from variables heights. How does this affect the drag as they hit the liquid?</p>
</div>
<div data-type="exercise">
<h3 id="exercise-26">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 id="exercise-27">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 id="29-gravitational-attraction">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_6.png" alt=" Figure 2.6">
<figcaption>Figure 2.6</figcaption>
</figure>
<p>Probably the most famous force of all is gravitational attraction. 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. In fact, 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><span data-type="equation">F_g</span> refers to the gravitational force, the vector to compute and pass into the <code>applyForce()</code> function.</li>
<li><span data-type="equation">G</span> 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 scale the forces in the world making them stronger or weaker. Just setting it equal to one and ignoring it isnt such a terrible choice either.</li>
<li><span data-type="equation">m1</span> and <span data-type="equation">m2</span> 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—<span data-type="equation">G</span>, <span data-type="equation">m1</span>, <span data-type="equation">m2</span>—the bigger its value, the stronger the force. Big mass, big force. Big <span data-type="equation">G</span>, 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_7.png" alt="Figure 2.7">
<figcaption>Figure 2.7</figcaption>
</figure>
<p>Remember in <a href="/vectors#110-interactivity-with-acceleration">Chapter 1</a>, 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 calculate 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>Here, the direction of the attraction force that object 1 exerts on object 2 then 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 class="half-width-right">
<img src="images/02_forces/02_forces_8.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.
let 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 gravitational attraction), 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 class="half-width-right">
<img src="images/02_forces/02_forces_9.png" alt="Figure 2.9: One mover and one attractor. The mover experiences a gravitational force towards the attractor.">
<figcaption>Figure 2.9: One mover and one attractor. The mover experiences a gravitational force towards the attractor.</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 a basic <code>Attractor</code> class—giving it a position and a mass, along with a function to draw 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;
}
show() {
stroke(0);
fill(175, 200);
circle(this.position.x, this.position.y, this.mass * 2);
}
}</pre>
<p>And in the sketch, I can add a variable to hold an object instance of the <code>Attractor</code>.</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} Draw Attractor object.
attractor.show();
mover.update();
mover.show();
}</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>
<table>
<thead>
<tr>
<th>Task</th>
<th>Function</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. A function that receives both an Attractor and a Mover:</td>
<td><code>attraction(attractor, mover);</code></td>
</tr>
<tr>
<td>2. A function in the Attractor class that receives a Mover:</td>
<td><code>attractor.attract(mover);</code></td>
</tr>
<tr>
<td>3. A function in the Mover class that receives an Attractor:</td>
<td><code>mover.attractedTo(attractor);</code></td>
</tr>
<tr>
<td>4. A function in the Attractor class that receives a Mover and returns a <code>p5.Vector</code>, which is the attraction force. That attraction force is then passed into the Mover's <code>applyForce()</code> function:</td>
<td>
<code>let force = attractor.attract(mover);</code>
<code>mover.applyForce(force);</code>
</td>
</tr>
</tbody>
</table>
<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 I prefer 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 examples 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 <code>p5.Vector</code>, 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.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, it is practical to 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 id="example-26-attraction">Example 2.6: Attraction</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/16sblEvax" data-example-path="examples/02_forces/example_2_6_attraction"><img src="examples/02_forces/example_2_6_attraction/screenshot.png"></div>
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">//{!2} A Mover and an Attractor
let mover;
let attractor;
// Gravitational constant (for global scaling)
let G = 1.0;
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.
let 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;
}
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;
}
show() {
stroke(0);
fill(175, 200);
circle(this.position.x, this.position.y, this.mass * 2);
}
}</pre>
<div data-type="exercise">
<h3 id="exercise-2x-adapt-the-attractor-example-to-use-map-the-mass-of-the-attractor-and-mover-to-the-area-of-the-circle-hint-since-the-area-of-a-circle-is-pi-r2-try-taking-the-square-root-of-mass">Exercise 2.x Adapt the Attractor example to use map the mass of the Attractor and Mover to the area of the circle (hint: since the area of a circle is \pi r^2 try taking the square root of mass!)</h3>
<pre class="codesplit" data-code-language="javascript">circle(this.position.x, this.position.y, ______(this.mass) * 2);</pre>
</div>
<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 id="example-27-attraction-with-many-movers">Example 2.7: Attraction with many Movers</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/LSXJ6-VziJ" data-example-path="examples/02_forces/example_2_7_attraction_with_many_movers"><img src="examples/02_forces/example_2_7_attraction_with_many_movers/screenshot.png"></div>
<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.show();
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].show();
}
}</pre>
<div data-type="exercise">
<h3 id="exercise-28">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 <a href="http://processing.org/exhibition/works/metropop/">Metropop Denim project by Clayton Cubitt and Tom Carden</a> for an example.</p>
</div>
<div data-type="exercise">
<h3 id="exercise-29">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 id="210-everything-attracts-or-repels-everything">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].show();
}
}</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 draw.” 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 draw.”</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].show();
}</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) {
let 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 id="example-28-mutual-attraction">Example 2.8: Mutual attraction</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/uT9VpVvCO" data-example-path="examples/02_forces/example_2_8_mutual_attraction"><img src="examples/02_forces/example_2_8_mutual_attraction/screenshot.png"></div>
<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 id="exercise-210">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 id="the-ecosystem-project-1">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>
<figure>
<img src="images/02_forces/02_forces_10.png" alt="">
<figcaption></figcaption>
</figure>
<p></p>
</div>
</section>