mirror of
https://github.com/nature-of-code/noc-book-2
synced 2024-11-16 07:47:48 +01:00
Merge pull request #880 from nature-of-code/notion-update-docs
[Notion] Update docs
This commit is contained in:
commit
0dcaf97ecd
18 changed files with 147 additions and 147 deletions
|
@ -1,4 +1,4 @@
|
|||
<section data-type="page">
|
||||
<section data-type="page" id="section-dedication">
|
||||
<h1 id="dedication">Dedication</h1>
|
||||
<p>For my grandmother, Bella Manel Greenfield (October 13, 1915–April 3, 2010) </p>
|
||||
<figure>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="page">
|
||||
<section data-type="page" id="section-acknowledgements">
|
||||
<h1 id="acknowledgments">Acknowledgments</h1>
|
||||
<blockquote data-type="epigraph">
|
||||
<p><em>The world around us moves in complicated and wonderful ways. We spend the earlier parts of our lives learning about our environment through perception and interaction. We expect the physical world around us to behave consistently with our perceptual memory, e.g., if we drop a rock, it will fall due to gravity, if a gust of wind blows, lighter objects will be tossed by the wind further. This class focuses on understanding, simulating, and incorporating motion-based elements of our physical world into the digital worlds that we create. Our hope is to create intuitive, rich, and more satisfying experiences by drawing from the perceptual memories of our users.</em></p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="page">
|
||||
<section data-type="page" id="section-introduction">
|
||||
<h1 id="introduction">Introduction</h1>
|
||||
<p>Over a decade ago, I self-published <em>The Nature of Code</em>, an online resource and print book exploring the unpredictable evolutionary and emergent properties of nature in software via the creative coding framework Processing. It’s the understatement of the century to say that much has changed in the world of technology and creative media since then, and so here I am again, with a new and rebooted version of this book built around JavaScript and the p5.js library. The book has a few new coding tricks this time, but it’s the same old nature—birds still flap their wings, and apples still fall on our heads.</p>
|
||||
<h2 id="what-is-this-book">What Is This Book?</h2>
|
||||
|
@ -22,7 +22,7 @@
|
|||
<p>Are you reading this book on a Kindle? Printed paper? On your laptop in PDF form? On a tablet showing an animated HTML5 version? Are you strapped to a chair, absorbing the content directly into your brain via a series of electrodes, tubes, and cartridges?</p>
|
||||
<p>My dream has always been to write this book in one single format (in this case, a collection of Notion documents) and then, after pressing a magic button (<code>npm run build</code>), out comes the book in any and all formats you might want—PDF, HTML5, printed hard copy, Kindle, and so on. This was largely made possible by the <a href="https://github.com/magicbookproject">Magic Book project</a>, which is an open source framework for self-publishing originally developed at ITP by Rune Madsen and Steve Klise. Everything has been designed and styled using CSS—no manual typesetting or layout.</p>
|
||||
<p>The reality of putting this book together isn’t quite so clean as that, and the story of how it happened is long. If you’re interested in learning more, make sure to read the book’s acknowledgments, and then go hire the people I’ve thanked to help you publish a book! <a href="https://github.com/nature-of-code">I’ll also include more details in the associated GitHub repository</a>.</p>
|
||||
<p>The bottom line is that no matter what format you’re reading it in, the material is all the same. The only difference will be in how you experience the code examples—more on that in <a href="#how-to-read-the-code">“How to Read the Code”</a>.</p>
|
||||
<p>The bottom line is that no matter what format you’re reading it in, the material is all the same. The only difference will be in how you experience the code examples—more on that in <a href="#how-to-read-the-code" class="page-reference">“How to Read the Code”</a>.</p>
|
||||
<h3 id="the-coding-train-connection">The Coding Train Connection</h3>
|
||||
<p>Personally, I still love an assembled amalgamation of cellulose pulp, meticulously bound together with a resilient spine, upon which pigmented compounds have been artfully deployed to convey words and ideas. Yet, ever since 2012, when I impulsively recorded my very first video lesson about programming in my office at ITP, I’ve discovered the tremendous value and joy in conveying ideas and lessons through moving pictures.</p>
|
||||
<p>All this is to say, I have a YouTube channel called <a href="https://www.youtube.com/thecodingtrain">the Coding Train</a>. I mentioned it earlier when discussing options for learning the prerequisite material for this book, and if you continue reading, you’ll find I continue to reference related videos. I might allude to one I made about a related algorithm or alternative technique for a particular coding example, or suggest a series on a tangential concept that could provide additional context to a topic I’m exploring.</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="chapter">
|
||||
<section data-type="chapter" id="section-random">
|
||||
<h1 id="chapter-0-randomness">Chapter 0. Randomness</h1>
|
||||
<div class="chapter-opening-quote">
|
||||
<blockquote data-type="epigraph">
|
||||
|
@ -546,7 +546,7 @@ function draw() {
|
|||
//{!1} Use <code>map()</code> to customize the range of Perlin noise.
|
||||
let x = map(n, 0, 1, 0, width);
|
||||
ellipse(x, 180, 16, 16);
|
||||
//{!1} Move forward in "time".
|
||||
//{!1} Move forward in time.
|
||||
t += 0.01;
|
||||
}</pre>
|
||||
<p>The same logic can be applied to the random walker, assigning both its x- and y-values according to Perlin noise. This creates a smoother, more organic random walk.</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="chapter">
|
||||
<section data-type="chapter" id="section-vectors">
|
||||
<h1 id="chapter-1-vectors">Chapter 1. Vectors</h1>
|
||||
<div class="chapter-opening-quote">
|
||||
<blockquote data-type="epigraph">
|
||||
|
@ -308,7 +308,7 @@ circle(position.x, position.y, 48);</pre>
|
|||
<p>It may not always be obvious when to directly access an object’s properties versus when to reference the object as a whole or use one of its methods. The goal of this chapter (and most of this book) is to help you distinguish between these scenarios by providing a variety of examples and use cases.</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-11">Exercise 1.1</h3>
|
||||
<p>Take one of the walker examples from <a href="/random#">Chapter 0</a> and convert it to use vectors.</p>
|
||||
<p>Take one of the walker examples from <a href="/random#section-random">Chapter 0</a> and convert it to use vectors.</p>
|
||||
</div>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-12">Exercise 1.2</h3>
|
||||
|
@ -674,7 +674,7 @@ function draw() {
|
|||
<li>Add the velocity to the position.</li>
|
||||
<li>Draw the object at the position.</li>
|
||||
</ol>
|
||||
<p>In the bouncing ball example, all this code happened within <code>setup()</code> and <code>draw()</code>. What I want to do now is move toward encapsulating all the logic for an object’s motion inside a <strong>class</strong>. This way, I can create a foundation for programming moving objects that I can easily reuse again and again. (See “<a href="/random#the-random-walker-class">The Random Walker Class”</a> for a brief review of OOP basics.)</p>
|
||||
<p>In the bouncing ball example, all this code happened within <code>setup()</code> and <code>draw()</code>. What I want to do now is move toward encapsulating all the logic for an object’s motion inside a <strong>class</strong>. This way, I can create a foundation for programming moving objects that I can easily reuse again and again. (See “<a href="/random#the-random-walker-class" class="page-reference">The Random Walker Class”</a> for a brief review of OOP basics.)</p>
|
||||
<p>To start, I’m going to create a generic <code>Mover</code> class that will describe a shape moving around the canvas. For that, I must consider the following two questions:</p>
|
||||
<ol>
|
||||
<li>What data does a mover have?</li>
|
||||
|
@ -788,7 +788,7 @@ class Mover {
|
|||
}</pre>
|
||||
<p>If OOP is at all new to you, one aspect here may seem a bit strange. I spent the beginning of this chapter discussing the <code>p5.Vector</code> class, and this class is the template for making the <code>position</code> object and the <code>velocity</code> object. So what are those objects doing inside yet another object, the <code>Mover</code> object?</p>
|
||||
<p>In fact, this is just about the most normal thing ever. An object is something that holds data (and functionality). That data can be numbers, or it can be other objects (arrays too)! You’ll see this over and over again in this book. In Chapter 4, for example, I’ll write a class to describe a system of particles. That <code>ParticleSystem</code> object will include a list of <code>Particle</code> objects . . . and each <code>Particle</code> object will have as its data several <code>p5.Vector</code> objects!</p>
|
||||
<p>You may have also noticed in the <code>Mover</code> class that I’m setting the initial position and velocity directly within the constructor, without using any arguments. While this approach keeps the code simple for now, I’ll explore the benefits of adding arguments to the constructor in <a href="/forces#">Chapter 2</a>.</p>
|
||||
<p>You may have also noticed in the <code>Mover</code> class that I’m setting the initial position and velocity directly within the constructor, without using any arguments. While this approach keeps the code simple for now, I’ll explore the benefits of adding arguments to the constructor in <a href="/forces#section-forces">Chapter 2</a>.</p>
|
||||
<p>At this point, you hopefully feel comfortable with two concepts: (1) what a vector is and (2) how to use vectors inside an object to keep track of its position and movement. This is an excellent first step and deserves a mild round of applause. Before standing ovations are in order, however, you need to make one more, somewhat bigger step forward. After all, watching the Motion 101 example is fairly boring. The circle never speeds up, never slows down, and never turns. For more sophisticated motion—the kind of motion that appears in the world around us—one more vector needs to be added to the class: <code>acceleration</code>.</p>
|
||||
<h2 id="acceleration">Acceleration</h2>
|
||||
<p><strong>Acceleration</strong> is the<em> </em>rate of change of velocity. Think about that definition for a moment. Is it a new concept? Not really. Earlier I defined velocity as<em> </em>the rate of change of position, so in essence I’m developing a trickle-down effect. Acceleration affects velocity, which in turn affects position. (To provide some brief foreshadowing, this point will become even more crucial in the next chapter, when I show how forces like friction affect acceleration, which affects velocity, which affects position.) In code, this trickle-down effect reads like this:</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="chapter">
|
||||
<section data-type="chapter" id="section-forces">
|
||||
<h1 id="chapter-2-forces">Chapter 2. Forces</h1>
|
||||
<div class="chapter-opening-quote">
|
||||
<blockquote data-type="epigraph">
|
||||
|
@ -74,7 +74,7 @@
|
|||
</div>
|
||||
<p>In the world of p5.js, what is mass anyway? Aren’t we dealing with pixels? Let’s start simple and say that in a pretend pixel world, all objects have a mass equal to 1. Anything divided by 1 equals itself, and so, in this simple world, we have this:</p>
|
||||
<div data-type="equation">\vec{A} = \vec{F}</div>
|
||||
<p>I’ve effectively removed mass from the equation, making the acceleration of an object equal to force. This is great news. After all, <a href="/vectors#">Chapter 1</a> described acceleration as the key to controlling the movement of objects in a canvas. I said that the position changes according to the velocity, and the velocity according to acceleration. Acceleration seemed to be where it all began. Now you can see that <em>force</em> is truly where it all begins.</p>
|
||||
<p>I’ve effectively removed mass from the equation, making the acceleration of an object equal to force. This is great news. After all, <a href="/vectors#section-vectors">Chapter 1</a> described acceleration as the key to controlling the movement of objects in a canvas. I said that the position changes according to the velocity, and the velocity according to acceleration. Acceleration seemed to be where it all began. Now you can see that <em>force</em> is truly where it all begins.</p>
|
||||
<p>Let’s take the <code>Mover</code> class, with position, velocity, and acceleration:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">class Mover {
|
||||
constructor() {
|
||||
|
@ -143,7 +143,7 @@ mover.update();</pre>
|
|||
<h3 id="units-of-measurement">Units of Measurement</h3>
|
||||
<p>Now that I’m introducing mass, it’s 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. In this chapter, however, I’m 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, p5.js doesn’t have 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 pixeloids” or “10 yurkles.”</p>
|
||||
<p>For demonstration purposes, I’ll tie mass to pixels (the larger a circle’s diameter, the larger the mass). This will allow me to visualize the mass of an object, albeit inaccurately. In the real world, size doesn’t indicate mass. A small metal ball could have a much higher mass than a large balloon because of its higher density. And for two circular objects with equal density, I’ll also note that mass should be tied to the formula for the area of a circle: <span data-type="equation">\pi r^2</span>. (This will be addressed in Exercise 2.11, and I’ll say more about <span data-type="equation">\pi</span> and circles in <a href="/oscillation#">Chapter 3</a>.)</p>
|
||||
<p>For demonstration purposes, I’ll tie mass to pixels (the larger a circle’s diameter, the larger the mass). This will allow me to visualize the mass of an object, albeit inaccurately. In the real world, size doesn’t indicate mass. A small metal ball could have a much higher mass than a large balloon because of its higher density. And for two circular objects with equal density, I’ll also note that mass should be tied to the formula for the area of a circle: <span data-type="equation">\pi r^2</span>. (This will be addressed in Exercise 2.11, and I’ll say more about <span data-type="equation">\pi</span> and circles in <a href="/oscillation#section-oscillation">Chapter 3</a>.)</p>
|
||||
</div>
|
||||
<p>Mass is a scalar, not a vector, as it’s 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 it’s 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() {
|
||||
|
@ -197,7 +197,7 @@ moverB.applyForce(wind);</pre>
|
|||
<p>Let’s take a moment to recap what I’ve covered so far. I’ve defined what a force is (a vector), and I’ve shown how to apply a force to an object (divide it by mass and add it to the object’s acceleration vector). What’s 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>You could write <code>applyForce()</code> in another way, using the static method <code>div()</code> instead of <code>copy()</code>. Rewrite <code>applyForce()</code> by using the static method. For help with this exercise, review static methods in <a href="/vectors#static-vs-nonstatic-methods">“Static vs. Nonstatic Methods”</a>.</p>
|
||||
<p>You could write <code>applyForce()</code> in another way, using the static method <code>div()</code> instead of <code>copy()</code>. Rewrite <code>applyForce()</code> by using the static method. For help with this exercise, review static methods in <a href="/vectors#static-vs-nonstatic-methods" class="page-reference">“Static vs. Nonstatic Methods”</a>.</p>
|
||||
<pre class="codesplit" data-code-language="javascript">applyForce(force) {
|
||||
let f = <span class="blank">p5.Vector.div</span>(<span class="blank">force</span>, <span class="blank">this.mass</span>);
|
||||
this.acceleration.add(f);
|
||||
|
@ -228,7 +228,7 @@ if (mouseIsPressed) {
|
|||
mover.applyForce(wind);
|
||||
}</pre>
|
||||
<p>Now I have two forces, pointing in different directions and with different magnitudes, both applied to the object <code>mover</code>. I’m beginning to get somewhere. I’ve built a world, an environment with forces that act on objects!</p>
|
||||
<p>Let’s look at what happens now when I add a second object with a variable mass. To do this, you’ll probably want to do a quick review of OOP. Again, I’m 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 <a href="/introduction#the-coding-train-connection">“The Coding Train Connection”</a>). However, since the idea of creating a world filled with objects is fundamental to all the examples in this book, it’s worth taking a moment to walk through the steps of going from one object to many.</p>
|
||||
<p>Let’s look at what happens now when I add a second object with a variable mass. To do this, you’ll probably want to do a quick review of OOP. Again, I’m 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 <a href="/introduction#the-coding-train-connection" class="page-reference">“The Coding Train Connection”</a>). However, since the idea of creating a world filled with objects is fundamental to all the examples in this book, it’s 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 that it’s identical to the <code>Mover</code> class created in Chapter 1, with two additions, <code>mass</code> and a new <code>applyForce()</code> method:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">class Mover {
|
||||
constructor() {
|
||||
|
@ -279,7 +279,7 @@ if (mouseIsPressed) {
|
|||
}
|
||||
}</pre>
|
||||
<div class="avoid-break">
|
||||
<p>Now that the class is written, I can create more than one <code>Mover</code> object.</p>
|
||||
<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>
|
||||
</div>
|
||||
|
@ -371,7 +371,7 @@ moverB.applyForce(gravityB);</pre>
|
|||
<p>Making up forces will actually get you quite far—after all, I just made up a pretty good approximation of Earth’s gravity. Ultimately, the world of p5.js is an orchestra of pixels, and you’re the conductor, so whatever you deem appropriate to be a force, well by golly, that’s the force it should be! Nevertheless, there may come a time when you find yourself wondering, “But how does it all <em>really</em> work?” That’s when modeling forces, instead of just making them up, enters the picture.</p>
|
||||
<div data-type="note">
|
||||
<h3 id="parsing-formulas">Parsing Formulas</h3>
|
||||
<p>In a moment, I’m going to write out the formula for friction. This won’t be the first time you’ve seen a formula in this book; I just finished up the discussion of Newton’s second law, <span data-type="equation">\vec{F} = M \times \vec{A}</span> (or force equals mass times acceleration). You hopefully didn’t spend a lot of time worrying about that formula, because it’s just a few characters and symbols. Nevertheless, it’s a scary world out there. Just take a look at the equation for a normal distribution, which I covered (without presenting the formula) in <a href="/random#a-normal-distribution-of-random-numbers">“A Normal Distribution of Random Numbers”</a>:</p>
|
||||
<p>In a moment, I’m going to write out the formula for friction. This won’t be the first time you’ve seen a formula in this book; I just finished up the discussion of Newton’s second law, <span data-type="equation">\vec{F} = M \times \vec{A}</span> (or force equals mass times acceleration). You hopefully didn’t spend a lot of time worrying about that formula, because it’s just a few characters and symbols. Nevertheless, it’s a scary world out there. Just take a look at the equation for a normal distribution, which I covered (without presenting the formula) in <a href="/random#a-normal-distribution-of-random-numbers" class="page-reference">“A Normal Distribution of Random Numbers”</a>:</p>
|
||||
<div data-type="equation">\frac{1}{\sigma\sqrt{2\pi}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}</div>
|
||||
<p>Formulas are regularly written with many symbols (often with letters from the Greek alphabet). Here’s the formula for friction (as indicated by <span data-type="equation">\vec{f}</span>):</p>
|
||||
<div data-type="equation">\vec{f} = -\mu N \hat{v}</div>
|
||||
|
@ -924,7 +924,7 @@ function draw() {
|
|||
movers[i].show();
|
||||
}
|
||||
}</pre>
|
||||
<p>This is just a small taste of what’s possible with arrays of objects. Stay tuned for a more in-depth exploration of adding and removing multiple objects from the canvas in <a href="/particles#">Chapter 4</a>, which covers particle systems.</p>
|
||||
<p>This is just a small taste of what’s possible with arrays of objects. Stay tuned for a more in-depth exploration of adding and removing multiple objects from the canvas in <a href="/particles#section-particles">Chapter 4</a>, which covers particle systems.</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-212">Exercise 2.12</h3>
|
||||
<p>In Example 2.7, there’s a system (an 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?</p>
|
||||
|
@ -1061,7 +1061,7 @@ function draw() {
|
|||
}
|
||||
}</pre>
|
||||
<p>The nested loop solution in Example 2.9 leads to what’s called an <em>n</em>-squared algorithm, meaning the number of calculations is equal to the number of bodies squared. If I were to increase the number of bodies, the simulation would start to slow significantly because of the number of calculations required.</p>
|
||||
<p>In <a href="/autonomous-agents#">Chapter 5</a>, I’ll explore strategies for optimizing sketches like this one, with a particular focus on spatial subdivision algorithms. Spatial subdivision, in combination with the concept of quadtrees and an algorithm called Barnes-Hut, is particularly effective for improving efficiency in simulations such as the <em>n</em>-body one discussed here.</p>
|
||||
<p>In <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a>, I’ll explore strategies for optimizing sketches like this one, with a particular focus on spatial subdivision algorithms. Spatial subdivision, in combination with the concept of quadtrees and an algorithm called Barnes-Hut, is particularly effective for improving efficiency in simulations such as the <em>n</em>-body one discussed here.</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-215">Exercise 2.15</h3>
|
||||
<p>Change the attraction force in Example 2.9 to a repulsion force. Can you create an example in which all the <code>Body</code> objects are attracted to the mouse but repel one another? 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>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="chapter">
|
||||
<section data-type="chapter" id="section-oscillation">
|
||||
<h1 id="chapter-3-oscillation">Chapter 3. Oscillation</h1>
|
||||
<div class="chapter-opening-quote">
|
||||
<blockquote data-type="epigraph">
|
||||
|
@ -16,9 +16,9 @@
|
|||
<h3 id="gala-by-bridget-riley-1974-acrylic-on-canvas-1597--1597-cm"><em>Gala</em> by Bridget Riley, 1974; acrylic on canvas, 159.7 × 159.7 cm</h3>
|
||||
<p>Bridget Riley, a celebrated British artist, was a driving force behind the Op Art movement of the 1960s. Her work features geometric patterns that challenge the viewer’s perceptions and evoke feelings of movement or vibration. Her 1974 piece <em>Gala</em> showcases a series of curvilinear forms that ripple across the canvas, evoking the natural rhythm of the sine wave.</p>
|
||||
</div>
|
||||
<p>In <a href="/vectors#">Chapters 1</a> and <a href="/forces#">2</a>, I carefully worked out an object-oriented structure to animate a shape in a p5.js canvas, using a vector to represent position, velocity, and acceleration driven by forces in the environment. I could move straight from here into topics such as particle systems, steering forces, group behaviors, and more. However, doing so would mean skipping a fundamental aspect of motion in the natural world: <strong>oscillation</strong>, or the back-and-forth movement of an object around a central point or position.</p>
|
||||
<p>In <a href="/vectors#section-vectors">Chapters 1</a> and <a href="/forces#section-forces">2</a>, I carefully worked out an object-oriented structure to animate a shape in a p5.js canvas, using a vector to represent position, velocity, and acceleration driven by forces in the environment. I could move straight from here into topics such as particle systems, steering forces, group behaviors, and more. However, doing so would mean skipping a fundamental aspect of motion in the natural world: <strong>oscillation</strong>, or the back-and-forth movement of an object around a central point or position.</p>
|
||||
<p>To model oscillation, you need to understand a little bit about <strong>trigonometry</strong>, the mathematics of triangles. Learning some trig will give you new tools to generate patterns and create new motion behaviors in a p5.js sketch. You’ll learn to harness angular velocity and acceleration to spin objects as they move. You’ll be able to use the sine and cosine functions to model nice ease-in, ease-out wave patterns. You’ll also learn to calculate the more complex forces at play in situations that involve angles, such as a pendulum swinging or a box sliding down an incline.</p>
|
||||
<p>I’ll start with the basics of working with angles in p5.js, then cover several aspects of trigonometry. In the end, I’ll connect trigonometry with what you learned about forces in <a href="/forces#">Chapter 2</a>. This chapter’s content will pave the way for more sophisticated examples that require trig later in this book.</p>
|
||||
<p>I’ll start with the basics of working with angles in p5.js, then cover several aspects of trigonometry. In the end, I’ll connect trigonometry with what you learned about forces in <a href="/forces#section-forces">Chapter 2</a>. This chapter’s content will pave the way for more sophisticated examples that require trig later in this book.</p>
|
||||
<h2 id="angles">Angles</h2>
|
||||
<p>Before going any further, I need to make sure you understand how the concept of an <strong>angle</strong> fits into creative coding in p5.js. If you have experience with p5.js, you’ve undoubtedly encountered this issue while using the <code>rotate()</code> function to rotate and spin objects. You’re most likely to be familiar with the concept of an angle as measured in <strong>degrees</strong> (see Figure 3.1).</p>
|
||||
<figure>
|
||||
|
@ -63,7 +63,7 @@ rotate(angle);</pre>
|
|||
</div>
|
||||
<h2 id="angular-motion">Angular Motion</h2>
|
||||
<p>Another term for rotation is <strong>angular motion</strong>—that is, motion about an angle. Just as linear motion can be described in terms of velocity—the rate at which an object’s position changes over time—angular motion can be described in terms of <strong>angular velocity</strong>—the rate at which an object’s angle changes over time. By extension, <strong>angular acceleration</strong> describes changes in an object’s angular velocity.</p>
|
||||
<p>Luckily, you already have all the math you need to understand angular motion. Remember the stuff I dedicated almost all of <a href="/vectors#">Chapters 1</a> and <a href="/forces#">2</a> to explaining?</p>
|
||||
<p>Luckily, you already have all the math you need to understand angular motion. Remember the stuff I dedicated almost all of <a href="/vectors#section-vectors">Chapters 1</a> and <a href="/forces#section-forces">2</a> to explaining?</p>
|
||||
<div class="avoid-break">
|
||||
<div data-type="equation">\overrightarrow{\text{velocity}} = \overrightarrow{\text{velocity}} + \overrightarrow{\text{acceleration}}</div>
|
||||
<div data-type="equation">\overrightarrow{\text{position}} = \overrightarrow{\text{position}} + \overrightarrow{\text{velocity}}</div>
|
||||
|
@ -168,7 +168,7 @@ function draw() {
|
|||
</div>
|
||||
<p>At this point, if you were to actually go ahead and create a <code>Mover</code> object, you wouldn’t see it behave any differently. This is because the angular acceleration is initialized to zero (<code>this.angleAcceleration = 0;</code>). For the object to rotate, it needs a nonzero acceleration! Certainly, one option is to hardcode a number in the constructor:</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> this.angleAcceleration = 0.01;</pre>
|
||||
<p>You can produce a more interesting result, however, by dynamically assigning an angular acceleration in the <code>update()</code> method according to forces in the environment. This could be my cue to start researching the physics of angular acceleration based on the concepts of <a href="https://en.wikipedia.org/wiki/Torque">torque</a> and <a href="https://en.wikipedia.org/wiki/Moment_of_inertia">moment of inertia</a>, but at this stage, that level of simulation would be a bit of a rabbit hole. (I’ll cover modeling angular acceleration with a pendulum in more detail in <a href="#the-pendulum">“The Pendulum”</a>, as well as look at how third-party physics libraries realistically model rotational motion in <a href="/physics-libraries#">Chapter 6</a>.)</p>
|
||||
<p>You can produce a more interesting result, however, by dynamically assigning an angular acceleration in the <code>update()</code> method according to forces in the environment. This could be my cue to start researching the physics of angular acceleration based on the concepts of <a href="https://en.wikipedia.org/wiki/Torque">torque</a> and <a href="https://en.wikipedia.org/wiki/Moment_of_inertia">moment of inertia</a>, but at this stage, that level of simulation would be a bit of a rabbit hole. (I’ll cover modeling angular acceleration with a pendulum in more detail in <a href="#the-pendulum" class="page-reference">“The Pendulum”</a>, as well as look at how third-party physics libraries realistically model rotational motion in <a href="/physics-libraries#section-physics-libraries">Chapter 6</a>.)</p>
|
||||
<p>Instead, a quick-and-dirty solution that yields creative results will suffice. A reasonable approach is to calculate angular acceleration as a function of the object’s linear acceleration, its rate of change of velocity along a path vector, as opposed to its rotation. Here’s an example:</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> // Use the x-component of the object’s linear acceleration to calculate angular acceleration.
|
||||
this.angleAcceleration = this.acceleration.x;</pre>
|
||||
|
@ -487,7 +487,7 @@ function draw() {
|
|||
<p></p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-37">Exercise 3.7</h3>
|
||||
<p>Using the sine function, create a simulation of a weight (sometimes referred to as a <em>bob</em>) that hangs from a spring from the top of the window. Use the <code>map()</code> function to calculate the vertical position of the bob. In <a href="#spring-forces">“Spring Forces”</a>, I’ll demonstrate how to create this same simulation by modeling the forces of a spring according to Hooke’s law.</p>
|
||||
<p>Using the sine function, create a simulation of a weight (sometimes referred to as a <em>bob</em>) that hangs from a spring from the top of the window. Use the <code>map()</code> function to calculate the vertical position of the bob. In <a href="#spring-forces" class="page-reference">“Spring Forces”</a>, I’ll demonstrate how to create this same simulation by modeling the forces of a spring according to Hooke’s law.</p>
|
||||
</div>
|
||||
<h2 id="oscillation-with-angular-velocity">Oscillation with Angular Velocity</h2>
|
||||
<p>An understanding of oscillation, amplitude, and period (or frequency) can be essential in the course of simulating real-world behaviors. However, there’s a slightly easier way to implement the simple harmonic motion from Example 3.5, one that achieves the same result with fewer variables. Take one more look at the oscillation formula:</p>
|
||||
|
@ -495,7 +495,7 @@ function draw() {
|
|||
<p>Now I’ll rewrite it in a slightly different way:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">let x = amplitude * sin( <strong>some value that increments slowly</strong> );</pre>
|
||||
<p>If you care about precisely defining the period of oscillation in terms of frames of animation, you might need the formula as I first wrote it. If you don’t care about the exact period, however—for example, if you’ll be choosing it randomly—all you really need inside the <code>sin()</code> function is a value that increments slowly enough for the object’s motion to appear smooth from one frame to the next. Every time this value ticks past a multiple of <span data-type="equation">2\pi</span>, the object will have completed one cycle of oscillation.</p>
|
||||
<p>This technique mirrors what I did with Perlin noise in <a href="/random#">Chapter 0</a>. In that case, I incremented an offset variable (which I called <code>t</code> or <code>xoff</code>) to sample various outputs from the <code>noise()</code> function, creating a smooth transition of values. Now, I’m going to increment a value (I’ll call it <code>angle</code>) that’s fed into the <code>sin()</code> function. The difference is that the output from <code>sin()</code> is a smoothly repeating sine wave, without any randomness.</p>
|
||||
<p>This technique mirrors what I did with Perlin noise in <a href="/random#section-random">Chapter 0</a>. In that case, I incremented an offset variable (which I called <code>t</code> or <code>xoff</code>) to sample various outputs from the <code>noise()</code> function, creating a smooth transition of values. Now, I’m going to increment a value (I’ll call it <code>angle</code>) that’s fed into the <code>sin()</code> function. The difference is that the output from <code>sin()</code> is a smoothly repeating sine wave, without any randomness.</p>
|
||||
<p>You might be wondering why I refer to the incrementing value as <code>angle</code>, given that the object has no visible rotation. The term <em>angle</em> is used because the value is passed into the <code>sin()</code> function, and angles are the traditional inputs to trigonometric functions. With this in mind, I can reintroduce the concept of angular velocity (and acceleration) to rewrite the example to calculate the <code>x</code> position in terms of a changing angle. I’ll assume these global variables:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">let angle = 0;
|
||||
let angleVelocity = 0.05;</pre>
|
||||
|
@ -700,7 +700,7 @@ function draw() {
|
|||
<figcaption>Figure 3.14: A spring with an anchor and bob</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<p>Exploring the mathematics of triangles and waves has been lovely, but perhaps you’re starting to miss Newton’s laws of motion and vectors. After all, the core of this book is about simulating the physics of moving bodies. In <a href="#properties-of-oscillation">“Properties of Oscillation”</a>, I modeled simple harmonic motion by mapping a sine wave to a range of pixels on a canvas. Exercise 3.7 asked you to use this technique to create a simulation of a bob hanging from a spring with the <code>sin()</code> function. That kind of quick-and-dirty, one-line-of-code solution won’t do, however, if what you really want is a bob hanging from a spring that responds to other forces in the environment (wind, gravity, and so on). To achieve a simulation like that, you need to model the force of the spring by using vectors.</p>
|
||||
<p>Exploring the mathematics of triangles and waves has been lovely, but perhaps you’re starting to miss Newton’s laws of motion and vectors. After all, the core of this book is about simulating the physics of moving bodies. In <a href="#properties-of-oscillation" class="page-reference">“Properties of Oscillation”</a>, I modeled simple harmonic motion by mapping a sine wave to a range of pixels on a canvas. Exercise 3.7 asked you to use this technique to create a simulation of a bob hanging from a spring with the <code>sin()</code> function. That kind of quick-and-dirty, one-line-of-code solution won’t do, however, if what you really want is a bob hanging from a spring that responds to other forces in the environment (wind, gravity, and so on). To achieve a simulation like that, you need to model the force of the spring by using vectors.</p>
|
||||
<p>I’ll consider a spring to be a connection between a movable<em> </em>bob (or weight) and a fixed anchor point (see Figure 3.14).</p>
|
||||
<div class="half-width-right">
|
||||
<figure>
|
||||
|
@ -720,7 +720,7 @@ let bob = createVector(0, 120);
|
|||
let restLength = 100;</pre>
|
||||
<p>I’ll then use Hooke’s law to calculate the magnitude of the force. For that, I need <code>k</code> and <code>x</code>. Calculating <code>k</code> is easy; it’s just a constant, so I’ll make something up:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">let k = 0.1;</pre>
|
||||
<p>Finding <code>x</code> is perhaps a bit more difficult. I need to know the difference between the current length and the rest length. The rest length is defined as the variable <code>restLength</code>. What’s the current length? The distance between the anchor and the bob. And how can I calculate that distance? How about the magnitude of a vector that points from the anchor to the bob? (Note that this is exactly the same process I employed to find the distance between objects for the purposes of calculating gravitational attraction in <a href="/forces#">Chapter 2</a>.)</p>
|
||||
<p>Finding <code>x</code> is perhaps a bit more difficult. I need to know the difference between the current length and the rest length. The rest length is defined as the variable <code>restLength</code>. What’s the current length? The distance between the anchor and the bob. And how can I calculate that distance? How about the magnitude of a vector that points from the anchor to the bob? (Note that this is exactly the same process I employed to find the distance between objects for the purposes of calculating gravitational attraction in <a href="/forces#section-forces">Chapter 2</a>.)</p>
|
||||
<pre class="codesplit" data-code-language="javascript">//{!1} A vector pointing from the anchor to the bob gives you the current length of the spring.
|
||||
let dir = p5.Vector.sub(bob, anchor);
|
||||
let currentLength = dir.mag();
|
||||
|
@ -963,7 +963,7 @@ function draw() {
|
|||
<pre class="codesplit" data-code-language="javascript">this.r = 125;</pre>
|
||||
<p>I also know the bob’s current angle relative to the pivot: it’s stored in the variable <code>angle</code>. Between the arm length and the angle, what I have is a polar coordinate for the bob: <span data-type="equation">(r,\theta)</span>. What I really need is a Cartesian coordinate, but luckily I already know how to use sine and cosine to convert from polar to Cartesian. And so:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">this.bob = createVector(r * sin(this.angle), r * cos(this.angle));</pre>
|
||||
<p>Notice that I’m using <code>sin(this.angle)</code> for the <em>x</em> value and <code>cos(this.angle)</code> for the <em>y</em>. This is the opposite of what I showed you in <a href="#polar-vs-cartesian-coordinates">“Polar vs. Cartesian Coordinates”</a>. The reason is that I’m now looking for the top angle of a right triangle pointing down, as depicted in Figure 3.21. This angle lives between the y-axis and the hypotenuse, instead of between the x-axis and the hypotenuse, as you saw earlier in Figure 3.9.</p>
|
||||
<p>Notice that I’m using <code>sin(this.angle)</code> for the <em>x</em> value and <code>cos(this.angle)</code> for the <em>y</em>. This is the opposite of what I showed you in <a href="#polar-vs-cartesian-coordinates" class="page-reference">“Polar vs. Cartesian Coordinates”</a>. The reason is that I’m now looking for the top angle of a right triangle pointing down, as depicted in Figure 3.21. This angle lives between the y-axis and the hypotenuse, instead of between the x-axis and the hypotenuse, as you saw earlier in Figure 3.9.</p>
|
||||
<p>Right now, the value of <code>this.bob</code> is assuming that the pivot is at point (0, 0). To get the bob’s position relative to wherever the pivot <em>actually</em> happens to be, I can just add <code>pivot</code> to the <code>bob</code> vector:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">this.bob.add(this.pivot);</pre>
|
||||
<p>Now all that remains is the little matter of drawing a line and a circle (you should be more creative, of course):</p>
|
||||
|
@ -971,7 +971,7 @@ function draw() {
|
|||
fill(127);
|
||||
line(this.pivot.x, this.pivot.y, this.bob.x, this.bob.y);
|
||||
circle(this.bob.x, this.bob.y, 16);</pre>
|
||||
<p>Finally, a real-world pendulum is going to experience a certain amount of friction (at the pivot point) and air resistance. As it stands, the pendulum would swing forever with the given code. To make it more realistic, I can slow the pendulum with a damping trick. I say <em>trick</em> because rather than model the resistance forces with some degree of accuracy (as I did in <a href="/forces#">Chapter 2</a>), I can achieve a similar result simply by reducing the angular velocity by an arbitrary amount during each cycle. The following code reduces the velocity by 1 percent (or multiplies it by 0.99) for each frame of animation:</p>
|
||||
<p>Finally, a real-world pendulum is going to experience a certain amount of friction (at the pivot point) and air resistance. As it stands, the pendulum would swing forever with the given code. To make it more realistic, I can slow the pendulum with a damping trick. I say <em>trick</em> because rather than model the resistance forces with some degree of accuracy (as I did in <a href="/forces#section-forces">Chapter 2</a>), I can achieve a similar result simply by reducing the angular velocity by an arbitrary amount during each cycle. The following code reduces the velocity by 1 percent (or multiplies it by 0.99) for each frame of animation:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">this.angleVelocity *= 0.99;</pre>
|
||||
<p>Putting everything together, I have the following example (with the pendulum beginning at a 45-degree angle).</p>
|
||||
<div data-type="example">
|
||||
|
@ -1057,7 +1057,7 @@ class Pendulum {
|
|||
<div data-type="project">
|
||||
<h3 id="the-ecosystem-project-4">The Ecosystem Project</h3>
|
||||
<p>Take one of your creatures and incorporate oscillation into its motion. You can use the <code>Oscillator</code> class from Example 3.7 as a model. The <code>Oscillator</code> object, however, oscillates around a single point (the middle of the window). Try oscillating around a moving point.</p>
|
||||
<p>In other words, design a creature that moves around the screen according to position, velocity, and acceleration. But that creature isn’t just a static shape; it’s an oscillating body. Consider tying the speed of oscillation to the speed of motion. Think of a butterfly’s flapping wings or the legs of an insect. Can you make it appear as though the creature’s internal mechanics (oscillation) drive its locomotion? See the book’s website for an additional example combining attraction from <a href="/forces#">Chapter 2</a> with oscillation.</p>
|
||||
<p>In other words, design a creature that moves around the screen according to position, velocity, and acceleration. But that creature isn’t just a static shape; it’s an oscillating body. Consider tying the speed of oscillation to the speed of motion. Think of a butterfly’s flapping wings or the legs of an insect. Can you make it appear as though the creature’s internal mechanics (oscillation) drive its locomotion? See the book’s website for an additional example combining attraction from <a href="/forces#section-forces">Chapter 2</a> with oscillation.</p>
|
||||
<figure>
|
||||
<img src="images/03_oscillation/03_oscillation_21.png" alt="">
|
||||
<figcaption></figcaption>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="chapter">
|
||||
<section data-type="chapter" id="section-particles">
|
||||
<h1 id="chapter-4-particle-systems">Chapter 4. Particle Systems</h1>
|
||||
<div class="chapter-opening-quote">
|
||||
<blockquote data-type="epigraph">
|
||||
|
@ -44,7 +44,7 @@ function draw() {
|
|||
<p>No single particle is referenced in this code, and yet the result will be full of particles flying all over the canvas. This works because the details are hidden inside the <code>ParticleSystem</code> class, which holds references to lots of instances of the <code>Particle</code> class. Getting used to this technique of writing sketches with multiple classes, including classes that keep lists of instances of other classes, will prove useful as you get to later chapters in this book.</p>
|
||||
<p>Finally, working with particle systems is also an opportunity to tackle two other OOP techniques: inheritance and polymorphism. With the examples you’ve seen up until now, I’ve always used an array of a single type of object, like an array of movers or an array of oscillators. With inheritance and polymorphism, I’ll demonstrate a convenient way to use a single list to store objects of different types. This way, a particle system need not be a system of only one kind of particle.</p>
|
||||
<h2 id="a-single-particle">A Single Particle</h2>
|
||||
<p>Before I can get rolling on coding the particle system, I need to write a class to describe a single particle. The good news: I’ve done this already! The <code>Mover</code> class from <a href="/forces#">Chapter 2</a> serves as the perfect template. A particle is an independent body that moves about the canvas, so just like a mover, it has <code>position</code>, <code>velocity</code>, and <code>acceleration</code> variables; a constructor to initialize those variables; and methods to <code>show()</code> itself and <code>update()</code> its position.</p>
|
||||
<p>Before I can get rolling on coding the particle system, I need to write a class to describe a single particle. The good news: I’ve done this already! The <code>Mover</code> class from <a href="/forces#section-forces">Chapter 2</a> serves as the perfect template. A particle is an independent body that moves about the canvas, so just like a mover, it has <code>position</code>, <code>velocity</code>, and <code>acceleration</code> variables; a constructor to initialize those variables; and methods to <code>show()</code> itself and <code>update()</code> its position.</p>
|
||||
<div class="avoid-break">
|
||||
<pre class="codesplit" data-code-language="javascript">class Particle {
|
||||
// A <code>Particle</code> object is just another name for a mover. It has position, velocity, and acceleration.
|
||||
|
@ -440,7 +440,7 @@ function draw() {
|
|||
</div>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-44">Exercise 4.4</h3>
|
||||
<p>Building off <a href="/oscillation#">Chapter 3</a>’s <em>Asteroids</em> example, use a particle system to emit particles from the ship’s thrusters whenever a thrust force is applied. The particles’ initial velocity should be related to the ship’s current direction.</p>
|
||||
<p>Building off <a href="/oscillation#section-oscillation">Chapter 3</a>’s <em>Asteroids</em> example, use a particle system to emit particles from the ship’s thrusters whenever a thrust force is applied. The particles’ initial velocity should be related to the ship’s current direction.</p>
|
||||
</div>
|
||||
<h2 id="a-system-of-emitters">A System of Emitters</h2>
|
||||
<p>So far, I’ve described an individual particle and organized its code into a <code>Particle</code> class. I’ve also described a system of particles and organized the code into an <code>Emitter</code> class. This particle system is nothing more than a collection of independent <code>Particle</code> objects. But as an instance of the <code>Emitter</code> class, isn’t a particle system itself an object? If that’s the case (and it is), there’s no reason I couldn’t also build a collection of many particle emitters: a system of systems!</p>
|
||||
|
@ -511,7 +511,7 @@ function setup() {
|
|||
</div>
|
||||
<h2 id="inheritance-and-polymorphism">Inheritance and Polymorphism</h2>
|
||||
<p>Up to now, all the particles in my systems have been identical, with the same basic appearance and behaviors. Who says this has to be the case? By harnessing two fundamental OOP principles, inheritance and polymorphism, I can create particle systems with significantly more variety and interest.</p>
|
||||
<p>Perhaps you’ve encountered these two terms in your programming life before this book. For example, my beginner text, <em>Learning Processing</em>, has close to an entire chapter (Chapter 22) dedicated to them. Still, perhaps you’ve learned about inheritance and polymorphism only in the abstract and never had a reason to really use them. If that’s true, you’ve come to the right place. Without these techniques, your ability to program diverse particles and particle systems is extremely limited. (In <a href="/physics-libraries#">Chapter 6</a>, I’ll also demonstrate how understanding these topics will help you use physics libraries.)</p>
|
||||
<p>Perhaps you’ve encountered these two terms in your programming life before this book. For example, my beginner text, <em>Learning Processing</em>, has close to an entire chapter (Chapter 22) dedicated to them. Still, perhaps you’ve learned about inheritance and polymorphism only in the abstract and never had a reason to really use them. If that’s true, you’ve come to the right place. Without these techniques, your ability to program diverse particles and particle systems is extremely limited. (In <a href="/physics-libraries#section-physics-libraries">Chapter 6</a>, I’ll also demonstrate how understanding these topics will help you use physics libraries.)</p>
|
||||
<p>Imagine it’s a Saturday morning. You’ve just gone out for a lovely jog, had a delicious bowl of cereal, and are sitting quietly at your computer with a cup of warm chamomile tea. It’s your old friend so-and-so’s birthday, and you’ve decided you’d like to make a greeting card with p5.js. How about simulating some confetti? Purple confetti, pink confetti, star-shaped confetti, square confetti, fast confetti, fluttery confetti—all kinds of confetti, all with different appearances and different behaviors, exploding onto the screen all at once.</p>
|
||||
<p>What you have is clearly a particle system: a collection of individual pieces (particles) of confetti. You might be able to cleverly redesign the <code>Particle</code> class to have variables that store color, shape, behavior, and more. To create a variety of particles, you might initialize those variables with random values. But what if some of your particles are drastically different? It could become very messy to have all sorts of code for different ways of being a particle in the same class. Another option might be to do the following:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">class HappyConfetti {
|
||||
|
@ -745,7 +745,7 @@ for (let animal of kingdom) {
|
|||
}</pre>
|
||||
<p>This is polymorphism (from the Greek <em>polymorphos</em>,<em> </em>meaning “many forms”) in action. Although all the animals are grouped together in an array and processed in a single <code>for</code> loop, JavaScript can identify their true types and invoke the appropriate <code>eat()</code> method for each one. It’s that simple!</p>
|
||||
<h3 id="particles-with-inheritance-and-polymorphism">Particles with Inheritance and Polymorphism</h3>
|
||||
<p>Now that I’ve covered the theory and syntax behind inheritance and polymorphism, I’m ready to write a working example of them in p5.js, based on my <code>Particle</code> class. First, take another look at a basic <code>Particle</code> implementation, adapted from Example 4.1.</p>
|
||||
<p>Now that I’ve covered the theory and syntax behind inheritance and polymorphism, I’m ready to write a working example of them in p5.js, based on my <code>Particle</code> class. First, take another look at a basic <code>Particle</code> implementation, adapted from Example 4.1:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">class Particle {
|
||||
constructor(x, y) {
|
||||
this.acceleration = createVector(0, 0);
|
||||
|
@ -797,7 +797,7 @@ for (let animal of kingdom) {
|
|||
square(this.position.x, this.position.y, 12);
|
||||
}
|
||||
}</pre>
|
||||
<p>Let’s make this a bit more sophisticated. Say I want to have each <code>Confetti</code> particle rotate as it flies through the air. One option is to model angular velocity and acceleration, as described in <a href="/oscillation#">Chapter 3</a>. For ease, however, I’ll implement something less formal.</p>
|
||||
<p>Let’s make this a bit more sophisticated. Say I want to have each <code>Confetti</code> particle rotate as it flies through the air. One option is to model angular velocity and acceleration, as described in <a href="/oscillation#section-oscillation">Chapter 3</a>. For ease, however, I’ll implement something less formal.</p>
|
||||
<p>I know a particle has an x-position somewhere between 0 and the width of the canvas. What if I said that when the particle’s x-position is 0, its rotation should be 0; when its x-position is equal to the width, its rotation should be equal to <span data-type="equation">4\pi</span>? Does this ring a bell? As discussed in Chapter 0, whenever a value has one range that you want to map to another range, you can use the <code>map()</code> function:</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> let angle = map(this.position.x, 0, width, 0, TWO_PI * 2);</pre>
|
||||
<p>Here’s how this code fits into the <code>show()</code> method:</p>
|
||||
|
@ -1149,14 +1149,14 @@ class Repeller {
|
|||
return force;
|
||||
}
|
||||
}</pre>
|
||||
<p>Notice the addition of the <code>power</code> variable in the <code>Repeller</code> class, which controls the strength of the repulsion force exerted. This property becomes especially interesting when you have multiple attractors and repellers, each with different power values. For example, strong attractors and weak repellers might result in particles clustering around the attractors, while more powerful repellers might reveal patterns reminiscent of paths or channels between them. These are hints of what’s to come in <a href="/autonomous-agents#">Chapter 5</a>, where I’ll further explore the concept of a complex system.</p>
|
||||
<p>Notice the addition of the <code>power</code> variable in the <code>Repeller</code> class, which controls the strength of the repulsion force exerted. This property becomes especially interesting when you have multiple attractors and repellers, each with different power values. For example, strong attractors and weak repellers might result in particles clustering around the attractors, while more powerful repellers might reveal patterns reminiscent of paths or channels between them. These are hints of what’s to come in <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a>, where I’ll further explore the concept of a complex system.</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-49">Exercise 4.9</h3>
|
||||
<p>Expand Example 4.7 to include multiple repellers and attractors. How might you use inheritance and polymorphism to create separate <code>Repeller</code> and <code>Attractor</code> classes without duplicating code?</p>
|
||||
</div>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-410">Exercise 4.10</h3>
|
||||
<p>Create a particle system in which each particle responds to every other particle. (I’ll explain how to do this in detail in <a href="/autonomous-agents#">Chapter 5</a>.)</p>
|
||||
<p>Create a particle system in which each particle responds to every other particle. (I’ll explain how to do this in detail in <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a>.)</p>
|
||||
</div>
|
||||
<h2 id="image-textures-and-additive-blending">Image Textures and Additive Blending</h2>
|
||||
<p>Even though this book is almost exclusively focused on behaviors and algorithms rather than computer graphics and design, I don’t think I would be able to live with myself if I finished a discussion of particle systems without presenting an example of texturing each particle with an image. After all, the way you render a particle is a key piece of the puzzle in designing certain types of visual effects. For example, compare the two smoke simulations shown in Figure 4.7.</p>
|
||||
|
@ -1199,7 +1199,7 @@ class Repeller {
|
|||
tint(255, this.lifespan);
|
||||
image(img, this.position.x, this.position.y);
|
||||
}</pre>
|
||||
<p>This smoke example is also a nice excuse to revisit the Gaussian distributions from <a href="/random#a-normal-distribution-of-random-numbers">“A Normal Distribution of Random Numbers”</a>. Instead of launching the particles in a purely random direction, which produces a fountain-like effect, the result will appear more smokelike if the initial velocity vectors cluster mostly around a mean value, with a lower probability of outlying velocities. Using the <code>randomGaussian()</code> function, the particle velocities can be initialized as follows:</p>
|
||||
<p>This smoke example is also a nice excuse to revisit the Gaussian distributions from <a href="/random#a-normal-distribution-of-random-numbers" class="page-reference">“A Normal Distribution of Random Numbers”</a>. Instead of launching the particles in a purely random direction, which produces a fountain-like effect, the result will appear more smokelike if the initial velocity vectors cluster mostly around a mean value, with a lower probability of outlying velocities. Using the <code>randomGaussian()</code> function, the particle velocities can be initialized as follows:</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> let vx = randomGaussian(0, 0.3);
|
||||
let vy = randomGaussian(-1, 0.3);
|
||||
this.velocity = createVector(vx, vy);</pre>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="chapter">
|
||||
<section data-type="chapter" id="section-autonomous-agents">
|
||||
<h1 id="chapter-5-autonomous-agents">Chapter 5. Autonomous Agents</h1>
|
||||
<div class="chapter-opening-quote">
|
||||
<blockquote data-type="epigraph">
|
||||
|
@ -61,14 +61,14 @@
|
|||
<li><strong>Locomotion:</strong> For the most part, I’m going to ignore this third layer. In the case of fleeing from zombies, the locomotion could be described as “left foot, right foot, left foot, right foot, as fast as you can.” In a canvas, however, a rectangle, circle, or triangle’s actual movement across a window is irrelevant, given that the motion is all an illusion in the first place. This isn’t to say that you should ignore locomotion entirely, however. You’ll find great value in thinking about the locomotive design of your vehicle and how you choose to animate it. The examples in this chapter will remain visually bare; a good exercise would be to elaborate on the animation style. For example, could you add spinning wheels, oscillating paddles, or shuffling legs?</li>
|
||||
</ol>
|
||||
<p>Ultimately, the most important layer for you to consider is the first one, action selection. What are the elements of your system, and what are their goals? In this chapter, I’m going to cover a series of steering behaviors (that is, actions): seeking, fleeing, following a path, following a flow field, flocking with your neighbors, and so on. As I’ve said in other chapters, however, the point isn’t that you should use these exact behaviors in all your projects. Rather, the point is to show you <em>how</em> to model a steering behavior—<em>any</em> steering behavior—in code, and to provide a foundation for designing and developing your own vehicles with new and exciting goals and behaviors.</p>
|
||||
<p>What’s more, even though the examples in this chapter are highly literal (follow that pixel!), you should allow yourself to think more abstractly (like Braitenberg). What would it mean for your vehicle to have “love” as its goal or “fear” as its driving force? Finally (and I’ll address this in <a href="#combining-behaviors">“Combining Behaviors”</a>), you won’t get very far by developing simulations with only one action. Yes, the first example’s action will be to seek a target. But by being creative—by making these steering behaviors <em>your own</em>—it will all come down to mixing and matching multiple actions within the same vehicle. View the coming examples not as singular behaviors to be emulated, but as pieces of a larger puzzle that you’ll eventually assemble.</p>
|
||||
<p>What’s more, even though the examples in this chapter are highly literal (follow that pixel!), you should allow yourself to think more abstractly (like Braitenberg). What would it mean for your vehicle to have “love” as its goal or “fear” as its driving force? Finally (and I’ll address this in <a href="#combining-behaviors" class="page-reference">“Combining Behaviors”</a>), you won’t get very far by developing simulations with only one action. Yes, the first example’s action will be to seek a target. But by being creative—by making these steering behaviors <em>your own</em>—it will all come down to mixing and matching multiple actions within the same vehicle. View the coming examples not as singular behaviors to be emulated, but as pieces of a larger puzzle that you’ll eventually assemble.</p>
|
||||
<h3 id="the-steering-force">The Steering Force</h3>
|
||||
<p>What exactly is a steering force? To answer, consider the following scenario: a vehicle with a current velocity is seeking a target. For fun, let’s think of the vehicle as a bug-like creature that desires to savor a delicious strawberry, as in Figure 5.1.</p>
|
||||
<figure>
|
||||
<img src="images/05_steering/05_steering_2.png" alt="Figure 5.1: A vehicle with a velocity and a target">
|
||||
<figcaption>Figure 5.1: A vehicle with a velocity and a target</figcaption>
|
||||
</figure>
|
||||
<p>The vehicle’s goal and subsequent action is to seek the target. Thinking back to <a href="/forces#">Chapter 2</a>, you might begin by making the target an attractor and applying a gravitational force that pulls the vehicle to the target. This would be a perfectly reasonable solution, but conceptually it’s not what I’m looking for here.</p>
|
||||
<p>The vehicle’s goal and subsequent action is to seek the target. Thinking back to <a href="/forces#section-forces">Chapter 2</a>, you might begin by making the target an attractor and applying a gravitational force that pulls the vehicle to the target. This would be a perfectly reasonable solution, but conceptually it’s not what I’m looking for here.</p>
|
||||
<p>I don’t want to simply calculate a force that pushes the vehicle toward its target; rather, I want to ask the vehicle to make an intelligent decision to steer toward the target based on its perception of its own state (its speed and the direction in which it’s currently moving) and its environment (the location of the target). The vehicle should consider how it desires to move (a vector pointing to the target), compare that goal with how it’s currently moving (its velocity), and apply a force accordingly. That’s exactly what Reynolds’s steering force formula says:</p>
|
||||
<div data-type="equation">\text{steering force} = \text{desired velocity} - \text{current velocity}</div>
|
||||
<p>Or, as you might write in p5.js:</p>
|
||||
|
@ -87,7 +87,7 @@
|
|||
<img src="images/05_steering/05_steering_4.png" alt="Figure 5.3: The magnitude of the vehicle’s desired velocity is max speed.">
|
||||
<figcaption>Figure 5.3: The magnitude of the vehicle’s desired velocity is <em>max speed</em>.</figcaption>
|
||||
</figure>
|
||||
<p>The concept of maximum speed was introduced in <a href="/vectors#">Chapter 1</a> to ensure that a mover’s speed remained within a reasonable range. However, I didn’t always use it in the subsequent chapters. In Chapter 2, other forces such as friction and drag kept the speed in check, while in <a href="/oscillation#">Chapter 3</a>, oscillation was caused by opposing forces that kept the speed limited. In this chapter, maximum speed is a key parameter for controlling the behavior of a steering agent, so I’ll include it in all the examples.</p>
|
||||
<p>The concept of maximum speed was introduced in <a href="/vectors#section-vectors">Chapter 1</a> to ensure that a mover’s speed remained within a reasonable range. However, I didn’t always use it in the subsequent chapters. In Chapter 2, other forces such as friction and drag kept the speed in check, while in <a href="/oscillation#section-oscillation">Chapter 3</a>, oscillation was caused by opposing forces that kept the speed limited. In this chapter, maximum speed is a key parameter for controlling the behavior of a steering agent, so I’ll include it in all the examples.</p>
|
||||
<p>While I encourage you to consider how other forces such as friction and drag could be combined with steering behaviors, I’m going to focus only on steering forces for the time being. As such, I can include the concept of maximum speed as a limiting factor in the force calculation. First, I need to add a property to the <code>Vehicle</code> class setting the maximum speed:</p>
|
||||
<div class="snip-below">
|
||||
<pre class="codesplit" data-code-language="javascript">class Vehicle {
|
||||
|
@ -114,7 +114,7 @@ desired.setMag(this.maxspeed);</pre>
|
|||
// to the object’s acceleration.
|
||||
this.applyForce(steer);
|
||||
}</pre>
|
||||
<p>Notice that I finish the method by passing the steering force into <code>applyForce()</code>. This assumes that the code is built on top of the foundation I developed in <a href="/forces#">Chapter 2</a>.</p>
|
||||
<p>Notice that I finish the method by passing the steering force into <code>applyForce()</code>. This assumes that the code is built on top of the foundation I developed in <a href="/forces#" class="page-reference">Chapter 2</a>.</p>
|
||||
<p>To see why Reynolds’s steering formula works so well, take a look at Figure 5.4. It shows what the steering force looks like relative to the vehicle and target positions.</p>
|
||||
<figure>
|
||||
<img src="images/05_steering/05_steering_5.png" alt="Figure 5.4: The vehicle applies a steering force equal to its desired velocity minus its current velocity.">
|
||||
|
@ -151,7 +151,7 @@ desired.setMag(this.maxspeed);</pre>
|
|||
<img src="images/05_steering/05_steering_6.png" alt="Figure 5.5: The path for a stronger maximum force (left) versus a weaker one (right)">
|
||||
<figcaption>Figure 5.5: The path for a stronger maximum force (left) versus a weaker one (right)</figcaption>
|
||||
</figure>
|
||||
<p>Here’s the full <code>Vehicle</code> class, incorporating the rest of the elements from the <a href="/forces#">Chapter 2</a> <code>Mover</code> class.</p>
|
||||
<p>Here’s the full <code>Vehicle</code> class, incorporating the rest of the elements from the <a href="/forces#section-forces">Chapter 2</a> <code>Mover</code> class.</p>
|
||||
<div data-type="example">
|
||||
<h3 id="example-51-seeking-a-target">Example 5.1: Seeking a Target</h3>
|
||||
<figure>
|
||||
|
@ -209,7 +209,7 @@ desired.setMag(this.maxspeed);</pre>
|
|||
pop();
|
||||
}
|
||||
}</pre>
|
||||
<p>Note that, unlike the circles used to represent movers and particles in previous chapters, the <code>Vehicle</code> object is drawn as a triangle, defined as three custom vertices set with <code>beginShape()</code> and <code>endShape()</code>. This allows the vehicle to be represented in a way that indicates its direction, determined using the <code>heading()</code> method, as demonstrated in <a href="/oscillation#">Chapter 3</a>.</p>
|
||||
<p>Note that, unlike the circles used to represent movers and particles in previous chapters, the <code>Vehicle</code> object is drawn as a triangle, defined as three custom vertices set with <code>beginShape()</code> and <code>endShape()</code>. This allows the vehicle to be represented in a way that indicates its direction, determined using the <code>heading()</code> method, as demonstrated in <a href="/oscillation#section-oscillation">Chapter 3</a>.</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-51">Exercise 5.1</h3>
|
||||
<p>Implement a <em>fleeing</em> steering behavior (the desired velocity is the same as <em>seek</em>, but pointed in the opposite direction).</p>
|
||||
|
@ -307,7 +307,7 @@ desired.setMag(this.maxspeed);</pre>
|
|||
this.applyForce(steer);
|
||||
}</pre>
|
||||
</div>
|
||||
<p>The arrive behavior is a great demonstration of an autonomous agent’s perception of the environment—including its own state. This model differs from the inanimate forces of <a href="/forces#">Chapter 2</a>: a celestial body attracted to another body doesn’t know it is experiencing gravity, whereas a cheetah chasing its prey knows it’s chasing.</p>
|
||||
<p>The arrive behavior is a great demonstration of an autonomous agent’s perception of the environment—including its own state. This model differs from the inanimate forces of <a href="/forces#section-forces">Chapter 2</a>: a celestial body attracted to another body doesn’t know it is experiencing gravity, whereas a cheetah chasing its prey knows it’s chasing.</p>
|
||||
<p>The key is in the way the forces are calculated. For instance, in the gravitational attraction sketch (Example 2.6), the force always points directly from the object to the target—the exact direction of the desired velocity. Here, by contrast, the vehicle perceives its distance to the target and adjusts its desired speed accordingly, slowing as it gets closer. The force on the vehicle itself is therefore based not just on the desired velocity but also on the desired velocity <em>relative to its current velocity</em>. The vehicle accounts for its own state as part of its assessment of the environment.</p>
|
||||
<p>Put another way, the magic of Reynolds’s <em>desired minus velocity</em> equation is that it essentially makes the steering force a manifestation of the current velocity’s <em>error</em>: “I’m supposed to be going this fast in this direction, but I’m actually going this fast in another direction. My error is the difference between where I want to go and where I’m currently going.” Sometimes this can lead to seemingly unexpected results, as in Figure 5.10.</p>
|
||||
<figure>
|
||||
|
@ -460,7 +460,7 @@ for (let i = 0; i < this.cols; i++) {
|
|||
}
|
||||
xoff += 0.1;
|
||||
}</pre>
|
||||
<p>Now I’m getting somewhere. Calculating the direction of the vectors by using Perlin noise is a great way to simulate a variety of natural effects, such as irregular gusts of wind or the meandering path of a river. I’ll note, however, that this noise mapping generates a field that prefers flowing left. Since Perlin noise has a Gaussian-like distribution, angles near <span data-type="equation">\pi</span> are more likely to be selected. For Figure 5.16, I used a range of 0 to <span data-type="equation">4\pi</span> to counteract this tendency, similarly to the way I applied <span data-type="equation">4\pi</span> in <a href="/particles#">Chapter 4</a> to represent a range of angles for spinning confetti particles. Ultimately, of course, there’s no one correct way to calculate the vectors of a flow field; it’s up to you to decide what you’re looking to simulate.</p>
|
||||
<p>Now I’m getting somewhere. Calculating the direction of the vectors by using Perlin noise is a great way to simulate a variety of natural effects, such as irregular gusts of wind or the meandering path of a river. I’ll note, however, that this noise mapping generates a field that prefers flowing left. Since Perlin noise has a Gaussian-like distribution, angles near <span data-type="equation">\pi</span> are more likely to be selected. For Figure 5.16, I used a range of 0 to <span data-type="equation">4\pi</span> to counteract this tendency, similarly to the way I applied <span data-type="equation">4\pi</span> in <a href="/particles#section-particles">Chapter 4</a> to represent a range of angles for spinning confetti particles. Ultimately, of course, there’s no one correct way to calculate the vectors of a flow field; it’s up to you to decide what you’re looking to simulate.</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-56">Exercise 5.6</h3>
|
||||
<p>Write the code to calculate a flow field so that the vectors swirl in circles around the center of the canvas. </p>
|
||||
|
@ -544,7 +544,7 @@ let row = floor(this.position.y / this.resolution);</pre>
|
|||
}</pre>
|
||||
</div>
|
||||
<p>Notice that <code>lookup()</code> is a method of the <code>FlowField</code> class, rather than of <code>Vehicle</code>. While you certainly could place <code>lookup()</code> within the <code>Vehicle</code> class instead, from my perspective, placing it in <code>FlowField</code> aligns best with the OOP principle of encapsulation. The lookup task, which retrieves a vector based on a position from the flow field, is inherently tied to the data of the <code>FlowField</code> object.</p>
|
||||
<p>You may also notice some familiar elements from <a href="/particles#">Chapter 4</a>, such as the use of an array of vehicles. Although the vehicles here operate independently, this is a great first step toward thinking about the group behaviors that I’ll introduce later in this chapter.</p>
|
||||
<p>You may also notice some familiar elements from <a href="/particles#section-particles">Chapter 4</a>, such as the use of an array of vehicles. Although the vehicles here operate independently, this is a great first step toward thinking about the group behaviors that I’ll introduce later in this chapter.</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-57">Exercise 5.7</h3>
|
||||
<p>Adapt the flow-field example so the vectors change over time. (Hint: Try using the third dimension of Perlin noise!)</p>
|
||||
|
@ -555,9 +555,9 @@ let row = floor(this.position.y / this.resolution);</pre>
|
|||
</div>
|
||||
<h2 id="path-following">Path Following</h2>
|
||||
<p>The next steering behavior formulated by Reynolds that I’d like to explore is path following. But let me quickly clarify something first: the behavior here is path <em>following</em>, not path <em>finding</em>. Pathfinding refers to an algorithm that solves for the shortest distance between two points, often in a maze. With <strong>path following</strong>, a predefined route, or path, already exists, and the vehicle simply tries to follow it.</p>
|
||||
<p>In this section, I will work through the algorithm, including the corresponding mathematics and code. However, before doing so, it’s important to cover a key concept in vector math that I skipped over in <a href="/vectors#">Chapter 1</a>: <strong>the dot product</strong>. I haven’t needed it yet, but it’s necessary here and likely will prove quite useful for you beyond just this example.</p>
|
||||
<p>In this section, I will work through the algorithm, including the corresponding mathematics and code. However, before doing so, it’s important to cover a key concept in vector math that I skipped over in <a href="/vectors#section-vectors">Chapter 1</a>: <strong>the dot product</strong>. I haven’t needed it yet, but it’s necessary here and likely will prove quite useful for you beyond just this example.</p>
|
||||
<h3 id="the-dot-product">The Dot Product</h3>
|
||||
<p>Remember all the vector math covered in <a href="/vectors#">Chapter 1</a>? Add, subtract, multiply, and divide? Figure 5.17 has a recap of some of these operations.</p>
|
||||
<p>Remember all the vector math covered in <a href="/vectors#section-vectors">Chapter 1</a>? Add, subtract, multiply, and divide? Figure 5.17 has a recap of some of these operations.</p>
|
||||
<figure>
|
||||
<img src="images/05_steering/05_steering_19.png" alt="Figure 5.17: Adding vectors and multiplying a vector by a scalar">
|
||||
<figcaption>Figure 5.17: Adding vectors and multiplying a vector by a scalar</figcaption>
|
||||
|
@ -952,7 +952,7 @@ for (let i = 0; i < path.points.length - 1; i++) {
|
|||
<ul>
|
||||
<li>
|
||||
<strong>Nonlinearity:</strong> This aspect of complex systems is often casually referred to as the <em>butterfly effect</em>, coined by mathematician and meteorologist Edward Norton Lorenz, a pioneer in the study of chaos theory. In 1961, Lorenz was running a computer weather simulation for the second time and, perhaps to save a little time, typed in a starting value of 0.506 instead of 0.506127. The end result was completely different from the first result of the simulation.
|
||||
Stated more evocatively, the theory is that a single butterfly flapping its wings on the other side of the world could cause a massive weather shift and ruin your weekend at the beach. It’s called <em>nonlinear</em> because there isn’t a linear relationship between a change in initial conditions and a change in outcome. A small change in initial conditions can have a massive effect on the outcome. Nonlinear systems are a superset of chaotic systems. In <a href="/cellular-automata#">Chapter 7</a>, you’ll see how even in a system of many 0s and 1s, if you change just one bit, the result will be completely different.
|
||||
Stated more evocatively, the theory is that a single butterfly flapping its wings on the other side of the world could cause a massive weather shift and ruin your weekend at the beach. It’s called <em>nonlinear</em> because there isn’t a linear relationship between a change in initial conditions and a change in outcome. A small change in initial conditions can have a massive effect on the outcome. Nonlinear systems are a superset of chaotic systems. In <a href="/cellular-automata#section-cellular-automata">Chapter 7</a>, you’ll see how even in a system of many 0s and 1s, if you change just one bit, the result will be completely different.
|
||||
</li>
|
||||
<li><strong>Competition and cooperation:</strong> One ingredient that often makes a complex system tick is the presence of both competition and cooperation among the elements. The upcoming flocking system will have three rules: alignment, cohesion, and separation. Alignment and cohesion will ask the elements to “cooperate” by trying to stay together and move together. Separation, however, will ask the elements to “compete” for space. When the time comes, try taking out just the cooperation or just the competition, and you’ll see how the system loses its complexity. Competition and cooperation are found together in living complex systems, but not in nonliving complex systems like the weather.</li>
|
||||
<li>
|
||||
|
@ -963,7 +963,7 @@ for (let i = 0; i < path.points.length - 1; i++) {
|
|||
</ul>
|
||||
<p>Complexity will serve as a key theme for much of the remainder of the book. In this section, I’ll begin by introducing an additional feature to the <code>Vehicle</code> class: the ability to perceive neighboring vehicles. This enhancement will pave the way for a culminating example of a complex system in which the interplay of simple individual behaviors results in an emergent behavior: flocking<em>.</em></p>
|
||||
<h3 id="implementing-group-behaviors-or-lets-not-run-into-each-other">Implementing Group Behaviors (or: Let’s Not Run Into Each Other)</h3>
|
||||
<p>Managing a group of objects is certainly not a new concept. You’ve seen this before—in <a href="/particles#">Chapter 4</a>, where I developed the <code>Emitter</code> class to represent an overall particle system. There, I used an array to store a list of individual particles. I’ll start with the same technique here and store <code>Vehicle</code> objects in an array:</p>
|
||||
<p>Managing a group of objects is certainly not a new concept. You’ve seen this before—in <a href="/particles#section-particles">Chapter 4</a>, where I developed the <code>Emitter</code> class to represent an overall particle system. There, I used an array to store a list of individual particles. I’ll start with the same technique here and store <code>Vehicle</code> objects in an array:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">// Declare an array of <code>Vehicle</code> objects.
|
||||
let vehicles;
|
||||
|
||||
|
@ -1137,7 +1137,7 @@ function draw() {
|
|||
</div>
|
||||
<h3 id="combining-behaviors">Combining Behaviors</h3>
|
||||
<p>The most exciting and intriguing group behaviors come from mixing and matching multiple steering forces. After all, how could I even begin to simulate emergence in a complex system through a sketch that has only one rule?</p>
|
||||
<p>When multiple steering forces are at play, I need a mechanism for managing them all. You may be thinking, “This is nothing new. We juggle multiple forces all the time.” You would be right. In fact, this technique appeared as early as <a href="/forces#">Chapter 2</a>:</p>
|
||||
<p>When multiple steering forces are at play, I need a mechanism for managing them all. You may be thinking, “This is nothing new. We juggle multiple forces all the time.” You would be right. In fact, this technique appeared as early as <a href="/forces#section-forces">Chapter 2</a>:</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> let wind = createVector(0.001, 0);
|
||||
let gravity = createVector(0, 0.1);
|
||||
mover.applyForce(wind);
|
||||
|
@ -1293,7 +1293,7 @@ function draw() {
|
|||
</div>
|
||||
<p>Can you rewrite the <code>align()</code> method so that boids see only other boids that fall within a direct line of sight?</p>
|
||||
</div>
|
||||
<p>The code for cohesion is quite similar to that for alignment. The only difference is that instead of calculating the average <em>velocity</em> of the boid’s neighbors, I want to calculate the average <em>position</em> of the boid’s neighbors (and use that as a target to seek):</p>
|
||||
<p>The code for cohesion is quite similar to that for alignment. The only difference is that instead of calculating the average <em>velocity</em> of the boid’s neighbors, I want to calculate the average <em>position</em> of the boid’s neighbors (and use that as a target to seek).</p>
|
||||
<div class="avoid-break">
|
||||
<pre class="codesplit" data-code-language="javascript"> cohesion(boids) {
|
||||
let neighborDistance = 50;
|
||||
|
@ -1319,7 +1319,7 @@ function draw() {
|
|||
}
|
||||
}</pre>
|
||||
</div>
|
||||
<p>It’s also worth taking the time to write a class called <code>Flock</code> that manages the whole group of boids. It will be virtually identical to the <code>ParticleSystem</code> class from <a href="/particles#">Chapter 4</a>, with only one tiny change: when I call <code>run()</code> on each <code>Boid</code> object (as I did to each <code>Particle</code> object), I’ll pass in a reference to the entire array of boids:</p>
|
||||
<p>It’s also worth taking the time to write a class called <code>Flock</code> that manages the whole group of boids. It will be virtually identical to the <code>ParticleSystem</code> class from <a href="/particles#section-particles">Chapter 4</a>, with only one tiny change: when I call <code>run()</code> on each <code>Boid</code> object (as I did to each <code>Particle</code> object), I’ll pass in a reference to the entire array of boids:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">class Flock {
|
||||
constructor() {
|
||||
this.boids = [];
|
||||
|
@ -1363,7 +1363,7 @@ function draw() {
|
|||
background(255);
|
||||
flock.run();
|
||||
}</pre>
|
||||
<p>Just as with the particle systems from <a href="/particles#">Chapter 4</a>, you can see the elegance of OOP in simplifying the <code>setup()</code> and <code>draw()</code> functions.</p>
|
||||
<p>Just as with the particle systems from <a href="/particles#section-particles">Chapter 4</a>, you can see the elegance of OOP in simplifying the <code>setup()</code> and <code>draw()</code> functions.</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-516">Exercise 5.16</h3>
|
||||
<p>Combine flocking with other steering behaviors.</p>
|
||||
|
@ -1388,7 +1388,7 @@ function draw() {
|
|||
</div>
|
||||
<h2 id="algorithmic-efficiency-or-why-does-my-sketch-run-so-slowly">Algorithmic Efficiency (or: Why Does My Sketch Run So Slowly?)</h2>
|
||||
<p>Group behaviors are wonderful, but it’s with a heavy heart that I must admit that they can also be slow. In fact, the bigger the group, the slower the sketch can be. I’d love to hide this dark truth from you, because I’d like you to be happy and live a fulfilling and meaningful life, free from concerns about the efficiency of your code. But I’d also like to be able to sleep at night without worrying about your inevitable disappointment when you try to run your flocking simulation with too many boids.</p>
|
||||
<p>Usually, when I talk about p5.js sketches running slowly, it’s because drawing to the canvas can be slow—the more you draw, the slower your sketch runs. As you may recall from <a href="/particles#">Chapter 4</a>, switching to a different renderer like WebGL can sometimes alleviate this issue, allowing for faster drawing of larger particle systems. With something like a flocking simulation, however, the slowness derives from the algorithm. Computer scientists put this problem in terms of something called<strong> big </strong><span data-type="equation">O</span><strong> notation</strong>, where the <span data-type="equation">O</span> stands for <em>order</em>. This is shorthand for describing the efficiency of an algorithm: How many computational cycles does the algorithm require to complete?</p>
|
||||
<p>Usually, when I talk about p5.js sketches running slowly, it’s because drawing to the canvas can be slow—the more you draw, the slower your sketch runs. As you may recall from <a href="/particles#section-particles">Chapter 4</a>, switching to a different renderer like WebGL can sometimes alleviate this issue, allowing for faster drawing of larger particle systems. With something like a flocking simulation, however, the slowness derives from the algorithm. Computer scientists put this problem in terms of something called<strong> big </strong><span data-type="equation">O</span><strong> notation</strong>, where the <span data-type="equation">O</span> stands for <em>order</em>. This is shorthand for describing the efficiency of an algorithm: How many computational cycles does the algorithm require to complete?</p>
|
||||
<p>Consider a simple search problem. You have a basket containing 100 chocolate treats, only one of which is pure dark chocolate. That’s the one you want to eat. To find it, you pick the chocolates out of the basket one by one. You might be lucky and find it on the first try, but in the worst-case scenario, you have to check all 100 before you find the dark chocolate. To find one thing in 100, you have to check 100 things (or to find one thing in <span data-type="equation">N</span> things, you have to check <span data-type="equation">N</span> times). The big <span data-type="equation">O</span> notation here is <span data-type="equation">O(N)</span>. This, incidentally, is also the big <span data-type="equation">O</span> notation that describes a simple particle system. If you have <span data-type="equation">N</span> particles, you have to run and display those particles <span data-type="equation">N</span> times.</p>
|
||||
<p>Now, let’s think about a group behavior such as flocking. For every <code>Boid</code> object, you have to check the velocity and position of every other <code>Boid</code> object before you can calculate its steering force. Let’s say you have 100 boids. For boid 1, you need to check 100 boids; for boid 2, you need to check 100 boids; and so on. In all, for 100 boids, you need to perform 10,000 checks (<span data-type="equation">100 \times 100 = \text{10,000}</span>).</p>
|
||||
<p>You might be thinking, “No problem. Computers are fast. They can do 10,000 things pretty easily.” But what if there are 1,000 boids? Then you have this:</p>
|
||||
|
@ -1479,7 +1479,7 @@ for (let i = 0; i < grid.length; i++) {
|
|||
<figcaption></figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<p>The quadtree data structure is key to the Barnes-Hut algorithm, which I referenced briefly when building an <em>n</em>-body simulation in <a href="/forces#">Chapter 2</a>. This method uses a quadtree to approximate groups of bodies into a single one when calculating gravitational forces. This drastically reduces the number of calculations needed, allowing simulations with large numbers of bodies to run more efficiently. You can learn more about <a href="https://thecodingtrain.com/quadtree">building a quadtree and applying it to a flocking system as part of Coding Challenge #98 on the Coding Train website</a>.</p>
|
||||
<p>The quadtree data structure is key to the Barnes-Hut algorithm, which I referenced briefly when building an <em>n</em>-body simulation in <a href="/forces#section-forces">Chapter 2</a>. This method uses a quadtree to approximate groups of bodies into a single one when calculating gravitational forces. This drastically reduces the number of calculations needed, allowing simulations with large numbers of bodies to run more efficiently. You can learn more about <a href="https://thecodingtrain.com/quadtree">building a quadtree and applying it to a flocking system as part of Coding Challenge #98 on the Coding Train website</a>.</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-520">Exercise 5.20</h3>
|
||||
<p>Expand the bin-lattice spatial subdivision flocking sketch from Example 5.12 to use a quadtree.</p>
|
||||
|
@ -1499,7 +1499,7 @@ for (let i = 0; i < grid.length; i++) {
|
|||
return sqrt(x * x + y * y);
|
||||
}</pre>
|
||||
</div>
|
||||
<p>Magnitude requires the square-root operation. And so it should! After all, if you want the magnitude of a vector, you have to break out the Pythagorean theorem (we did this in <a href="/vectors#">Chapter 1</a>). However, if you could somehow skip taking the square root, your code would run faster.</p>
|
||||
<p>Magnitude requires the square-root operation. And so it should! After all, if you want the magnitude of a vector, you have to break out the Pythagorean theorem (we did this in <a href="/vectors#section-vectors">Chapter 1</a>). However, if you could somehow skip taking the square root, your code would run faster.</p>
|
||||
<p>Say you just want to know the <em>relative</em> magnitude of a vector <code>v</code>. For example, is the magnitude greater than 10?</p>
|
||||
<pre class="codesplit" data-code-language="javascript">if (v.mag() > 10) {
|
||||
/* Do something! */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="chapter">
|
||||
<section data-type="chapter" id="section-physics-libraries">
|
||||
<h1 id="chapter-6-physics-libraries">Chapter 6. Physics Libraries</h1>
|
||||
<div class="chapter-opening-quote">
|
||||
<blockquote data-type="epigraph">
|
||||
|
@ -28,7 +28,7 @@
|
|||
<p>These activities have yielded a set of motion simulations, allowing you to creatively define the physics of the worlds you build (whether realistic or fantastical). But, of course, you and I aren’t the first or only people to do this. The world of computer graphics and programming is full of prewritten code libraries dedicated to physics simulations.</p>
|
||||
<p>Just try searching <em>open source physics engine</em> and you could spend the rest of your day poring over a host of rich and complex codebases. This begs the question: If an existing code library takes care of physics simulation, why should you bother learning how to write any of the algorithms yourself? Here’s where the philosophy behind this book comes into play. While many libraries provide out-of-the-box physics to experiment with (super-awesome, sophisticated, and robust physics at that), there are several good reasons for learning the fundamentals from scratch before diving into such libraries.</p>
|
||||
<p>First, without an understanding of vectors, forces, and trigonometry, it’s easy to get lost just reading the documentation of a library, let alone using it. Second, even though a library may take care of the math behind the scenes, it won’t necessarily simplify your code. A great deal of overhead may be required in understanding how a library works and what it expects from you code-wise. Finally, as wonderful as a physics engine might be, if you look deep down into your heart, you’ll likely see that you seek to create worlds and visualizations that stretch the limits of the imagination. A library may be great, but it provides only a limited set of features. It’s important to know when to live within those limitations in the pursuit of a creative coding project and when those limits will prove to be confining.</p>
|
||||
<p>This chapter is dedicated to examining two open source physics libraries for JavaScript: <a href="https://brm.io/matter-js">Matter.js</a> and <a href="http://haptic-data.com/toxiclibsjs">Toxiclibs.js</a>. I don’t mean to imply that these are the only libraries you should use for any and all creative coding projects that could benefit from a physics engine (see <a href="#other-physics-libraries">“Other Physics Libraries”</a> for alternatives, and check the book’s website for ports of the chapter’s examples to other libraries). However, both libraries integrate nicely with p5.js and will allow me to demonstrate the fundamental concepts behind physics engines and how they relate to and build upon the material I’ve covered so far.</p>
|
||||
<p>This chapter is dedicated to examining two open source physics libraries for JavaScript: <a href="https://brm.io/matter-js">Matter.js</a> and <a href="http://haptic-data.com/toxiclibsjs">Toxiclibs.js</a>. I don’t mean to imply that these are the only libraries you should use for any and all creative coding projects that could benefit from a physics engine (see <a href="#other-physics-libraries" class="page-reference">“Other Physics Libraries”</a> for alternatives, and check the book’s website for ports of the chapter’s examples to other libraries). However, both libraries integrate nicely with p5.js and will allow me to demonstrate the fundamental concepts behind physics engines and how they relate to and build upon the material I’ve covered so far.</p>
|
||||
<p>Ultimately, the aim of this chapter isn’t to teach you the details of a specific physics library, but to provide you with a foundation for working with <em>any</em> physics library. The skills you acquire here will enable you to navigate and understand documentation, opening the door for you to expand your abilities with any library you choose.</p>
|
||||
<h2 id="why-use-a-physics-library">Why Use a Physics Library?</h2>
|
||||
<p>I’ve made the case for writing your own physics simulations (as you’ve learned to do in the previous chapters), but why use a physics library? After all, adding any external framework or library to a project introduces complexity and extra code. Is that additional overhead worth it? If you just want to simulate a circle falling down because of gravity, for example, do you really need to import an entire physics engine and learn its API? As the early chapters of this book hopefully demonstrated, probably not. Lots of scenarios like this are simple enough for you to get by writing the code yourself.</p>
|
||||
|
@ -71,7 +71,7 @@
|
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script></pre>
|
||||
<p>At the time of this writing, the most recent version of Matter.js is <code>0.19.0</code>, and that’s what I’ve referenced in this snippet. As Matter.js updates and new versions are released, it’s often a good idea to upgrade, but by referencing a specific version that you know works with your sketch, you don’t have to worry about new features of the library breaking your existing code.</p>
|
||||
<h2 id="matterjs-overview">Matter.js Overview</h2>
|
||||
<p>When you use Matter.js (or any physics engine) in p5.js, your code ends up looking a bit different. Here’s a pseudocode generalization of all the examples in <a href="/vectors#">Chapters 1</a> through <a href="/autonomous-agents#">5</a>:</p>
|
||||
<p>When you use Matter.js (or any physics engine) in p5.js, your code ends up looking a bit different. Here’s a pseudocode generalization of all the examples in <a href="/vectors#section-vectors">Chapters 1</a> through <a href="/autonomous-agents#section-autonomous-agents">5</a>:</p>
|
||||
<p><code><strong>setup()</strong></code></p>
|
||||
<ol>
|
||||
<li>Create all the objects in the world.</li>
|
||||
|
@ -102,7 +102,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
<ul>
|
||||
<li><strong>Bodies:</strong> The primary elements in the world, corresponding to the physical objects being simulated. A body has a position and a velocity. Sound familiar? It’s basically another version of the class I’ve been building throughout <a href="/vectors#">Chapters 1</a> through <a href="/autonomous-agents#">5</a>. It also has geometry to define its shape. It’s important to note that <em>body</em> is a generic term that physics engines use to describe a <em>thing</em> in the world (similarly to the term <em>particle</em>); it isn’t related to an anthropomorphic body.</li>
|
||||
<li><strong>Bodies:</strong> The primary elements in the world, corresponding to the physical objects being simulated. A body has a position and a velocity. Sound familiar? It’s basically another version of the class I’ve been building throughout <a href="/vectors#section-vectors">Chapters 1</a> through <a href="/autonomous-agents#section-autonomous-agents">5</a>. It also has geometry to define its shape. It’s important to note that <em>body</em> is a generic term that physics engines use to describe a <em>thing</em> in the world (similarly to the term <em>particle</em>); it isn’t related to an anthropomorphic body.</li>
|
||||
<li><strong>Composite:</strong> A container that allows for the creation of complex entities (made up of multiple bodies). The world itself is an example of a composite, and every body created has to be added to the world.</li>
|
||||
<li><strong>Constraints:</strong> Act as connections between bodies.</li>
|
||||
</ul>
|
||||
|
@ -247,7 +247,7 @@ function setup() {
|
|||
<h3 id="object-destructuring">Object Destructuring</h3>
|
||||
<p><strong>Object destructuring</strong> in JavaScript is a technique for extracting properties from an object and assigning them to variables. In the case of Matter.js, the <code>Matter</code> object contains the <code>Engine</code> property. Normally, an alias for this property can be set with <code>let Engine = Matter.Engine</code>, but with destructuring, the alias can be created more concisely:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">const { Engine } = Matter;</pre>
|
||||
<p>Hold on. Did you catch that I snuck in a <code>const</code> here? I know I said back in <a href="/random#">Chapter 0</a> that I would use only <code>let</code> for variable declarations throughout this book. However, working with an external library is a really good time to dip your toe in the <code>const</code> waters. In JavaScript, <code>const</code> is used for declaring variables whose values should never be reassigned after initialization. In this case, I want to protect myself from accidentally overwriting the <code>Engine</code> variable later in the code, which would likely break everything!</p>
|
||||
<p>Hold on. Did you catch that I snuck in a <code>const</code> here? I know I said back in <a href="/random#section-random">Chapter 0</a> that I would use only <code>let</code> for variable declarations throughout this book. However, working with an external library is a really good time to dip your toe in the <code>const</code> waters. In JavaScript, <code>const</code> is used for declaring variables whose values should never be reassigned after initialization. In this case, I want to protect myself from accidentally overwriting the <code>Engine</code> variable later in the code, which would likely break everything!</p>
|
||||
<p>With that out of the way, let’s look at how the destructuring syntax really shines when you need to create aliases to multiple properties of the same object:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">// Use object destructuring to extract aliases for <code>Engine</code> and <code>Vector</code>.
|
||||
const { Engine, Vector } = Matter;</pre>
|
||||
|
@ -311,7 +311,7 @@ Render.run(render);</pre>
|
|||
<p>One more critical order of business remains: physics engines must be told to step forward in time. Since I’m using the built-in renderer, I can also use the built-in runner, which runs the engine at a default frame rate of 60 frames per second. The runner is also customizable, but the details aren’t terribly important since the goal here is to move toward using p5.js’s <code>draw()</code> loop instead (coming in the next section):</p>
|
||||
<pre class="codesplit" data-code-language="javascript">// Run the engine!
|
||||
Runner.run(engine);</pre>
|
||||
<p>Here’s the Matter.js code all together, with an added <code>ground</code> object—another rectangular body. Note the use of the <code>{ isStatic: true }</code> option in the creation of the ground body to ensure that it remains in a fixed position. I’ll cover more details about static bodies in <a href="#static-matterjs-bodies">“Static Matter.js Bodies”</a>.</p>
|
||||
<p>Here’s the Matter.js code all together, with an added <code>ground</code> object—another rectangular body. Note the use of the <code>{ isStatic: true }</code> option in the creation of the ground body to ensure that it remains in a fixed position. I’ll cover more details about static bodies in <a href="#static-matterjs-bodies" class="page-reference">“Static Matter.js Bodies”</a>.</p>
|
||||
<div data-type="example">
|
||||
<h3 id="example-61-matterjs-default-render-and-runner">Example 6.1: Matter.js Default Render and Runner</h3>
|
||||
<figure>
|
||||
|
@ -376,7 +376,7 @@ function setup() {
|
|||
square(this.x, this.y, this.w);
|
||||
}
|
||||
}</pre>
|
||||
<p>Now I’ll write a <em>sketch.js</em> file that creates a new <code>Box</code> whenever the mouse is clicked and stores all the <code>Box</code> objects in an array. (This is the same approach I took in the particle system examples from <a href="/particles#">Chapter 4</a>.)</p>
|
||||
<p>Now I’ll write a <em>sketch.js</em> file that creates a new <code>Box</code> whenever the mouse is clicked and stores all the <code>Box</code> objects in an array. (This is the same approach I took in the particle system examples from <a href="/particles#section-particles">Chapter 4</a>.)</p>
|
||||
<div data-type="example">
|
||||
<h3 id="example-62-a-comfortable-and-cozy-p5js-sketch-that-needs-a-little-matterjs">Example 6.2: A Comfortable and Cozy p5.js Sketch That Needs a Little Matter.js</h3>
|
||||
<figure>
|
||||
|
@ -469,7 +469,7 @@ function setup() {
|
|||
square(0, 0, this.w);
|
||||
pop();
|
||||
}</pre>
|
||||
<p>It’s important to note here that if you delete a <code>Box</code> object from the <code>boxes</code> array—perhaps when it moves outside the boundaries of the canvas or reaches the end of its life span, as demonstrated in <a href="/particles#">Chapter 4</a>—you must also explicitly remove the body associated with that <code>Box</code> object from the Matter.js world. This can be done with a <code>removeBody()</code> method on the <code>Box</code> class:</p>
|
||||
<p>It’s important to note here that if you delete a <code>Box</code> object from the <code>boxes</code> array—perhaps when it moves outside the boundaries of the canvas or reaches the end of its life span, as demonstrated in <a href="/particles#section-particles">Chapter 4</a>—you must also explicitly remove the body associated with that <code>Box</code> object from the Matter.js world. This can be done with a <code>removeBody()</code> method on the <code>Box</code> class:</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> // This function removes a body from the Matter.js world.
|
||||
removeBody() {
|
||||
Composite.remove(engine.world, this.body);
|
||||
|
@ -484,7 +484,7 @@ function setup() {
|
|||
</figure>
|
||||
</div>
|
||||
<h2 id="static-matterjs-bodies">Static Matter.js Bodies</h2>
|
||||
<p>In the example I just created, the <code>Box</code> objects appear at the mouse position and fall downward because of the default gravity force. What if I want to add immovable boundaries to the world that will block the path of the falling <code>Box</code> objects? Matter.js makes this easy with the <code>isStatic</code> property.</p>
|
||||
<p>In the example I just created, the <code>Box</code> objects appear at the mouse position and fall downward because of the default gravity force. What if I want to add immovable boundaries to the world that will block the path of the falling <code>Box</code> objects? Matter.js makes this easy with the <code>isStatic</code> property:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">// Create a fixed (static) boundary body.
|
||||
let options = { isStatic: true };
|
||||
let boundary = Bodies.rectangle(x, y, w, h, options);</pre>
|
||||
|
@ -682,7 +682,7 @@ let part2 = Bodies.circle(x + offset, y, r);</pre>
|
|||
<figcaption>Figure 6.10: A constraint is a connection between two bodies at an anchor point for each body.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<p>A <strong>distance constraint</strong> is a connection of fixed length between two bodies, similar to a spring force connecting two shapes in <a href="/oscillation#">Chapter 3</a>. The constraint is attached to each body at a specified <strong>anchor</strong>, a point relative to the body’s center (see Figure 6.10). Depending on the constraint’s stiffness property, the “fixed” length can exhibit variability, much as a spring can be more or less rigid.</p>
|
||||
<p>A <strong>distance constraint</strong> is a connection of fixed length between two bodies, similar to a spring force connecting two shapes in <a href="/oscillation#section-oscillation">Chapter 3</a>. The constraint is attached to each body at a specified <strong>anchor</strong>, a point relative to the body’s center (see Figure 6.10). Depending on the constraint’s stiffness property, the “fixed” length can exhibit variability, much as a spring can be more or less rigid.</p>
|
||||
<p>Defining a constraint uses a similar methodology as creating bodies, only you need to have two bodies ready to go. Let’s assume that two <code>Particle</code> objects each store a reference to a Matter.js body in a property called <code>body</code>. I’ll call them <code>particleA</code> and <code>particleB</code>:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">let particleA = new Particle();
|
||||
let particleB = new Particle();</pre>
|
||||
|
@ -711,7 +711,7 @@ let particleB = new Particle();</pre>
|
|||
<pre class="codesplit" data-code-language="javascript">let constraint = Constraint.create(options);
|
||||
//{!1} Don’t forget to add the constraint to the world!
|
||||
Composite.add(engine.world, constraint);</pre>
|
||||
<p>I can include a constraint to a class to encapsulate and manage the relationships among multiple bodies. Here’s an example of a class that represents a swinging pendulum (mirroring Example 3.11 from <a href="/oscillation#">Chapter 3</a>).</p>
|
||||
<p>I can include a constraint to a class to encapsulate and manage the relationships among multiple bodies. Here’s an example of a class that represents a swinging pendulum (mirroring Example 3.11 from <a href="/oscillation#section-oscillation">Chapter 3</a>).</p>
|
||||
<div data-type="example">
|
||||
<h3 id="example-66-matterjs-pendulum">Example 6.6: Matter.js Pendulum</h3>
|
||||
<figure>
|
||||
|
@ -883,19 +883,19 @@ Composite.add(engine.world, mouseConstraint);</pre>
|
|||
</div>
|
||||
<p>In this example, you’ll see that the <code>stiffness</code> property of the constraint is set to <code>0.7</code>, giving a bit of elasticity to the imaginary mouse string. Other properties such as <code>angularStiffness</code> and <code>damping</code> can also influence the mouse’s interaction. What happens if you adjust these values?</p>
|
||||
<h2 id="adding-more-forces">Adding More Forces</h2>
|
||||
<p>In <a href="/forces#">Chapter 2</a>, I covered how to build an environment with multiple forces at play. An object might respond to gravitational attraction, wind, air resistance, and so on. Clearly, forces are at work in Matter.js as rectangles and circles spin and fly around the screen! But so far, I’ve demonstrated how to manipulate only a single global force: gravity.</p>
|
||||
<p>In <a href="/forces#section-forces">Chapter 2</a>, I covered how to build an environment with multiple forces at play. An object might respond to gravitational attraction, wind, air resistance, and so on. Clearly, forces are at work in Matter.js as rectangles and circles spin and fly around the screen! But so far, I’ve demonstrated how to manipulate only a single global force: gravity.</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> let engine = Engine.create();
|
||||
// Change the engine’s gravity to point horizontally.
|
||||
engine.gravity.x = 1;
|
||||
engine.gravity.y = 0;</pre>
|
||||
<p>If I want to use any of the <a href="/forces#">Chapter 2</a> techniques with Matter.js, I need look no further than the trusty <code>applyForce()</code> method. In <a href="/forces#">Chapter 2</a>, I wrote this method as part of the <code>Mover</code> class. It received a vector, divided it by mass, and accumulated it into the mover’s acceleration. With Matter.js, the same method exists, so I no longer need to write all the details myself! I can call it with the static <code>Body.applyForce()</code>. Here’s what that looks like in what’s now the <code>Box</code> class:</p>
|
||||
<p>If I want to use any of the <a href="/forces#section-forces">Chapter 2</a> techniques with Matter.js, I need look no further than the trusty <code>applyForce()</code> method. In <a href="/forces#section-forces">Chapter 2</a>, I wrote this method as part of the <code>Mover</code> class. It received a vector, divided it by mass, and accumulated it into the mover’s acceleration. With Matter.js, the same method exists, so I no longer need to write all the details myself! I can call it with the static <code>Body.applyForce()</code>. Here’s what that looks like in what’s now the <code>Box</code> class:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">class Box {
|
||||
applyForce(force) {
|
||||
//{!1} Call <code>Body</code>’s <code>applyForce()</code>.
|
||||
Body.applyForce(this.body, this.body.position, force);
|
||||
}
|
||||
}</pre>
|
||||
<p>Here, the <code>Box</code> class’s <code>applyForce()</code> method receives a force vector and simply passes it along to Matter.js’s <code>applyForce()</code> method to apply it to the corresponding body. The key difference with this approach is that Matter.js is a more sophisticated engine than the examples from <a href="/forces#">Chapter 2</a>. The earlier examples assumed that the force was always applied at the mover’s center. Here, I’ve specified the exact position on the body where the force is applied. In this case, I’ve just applied it to the center as before by asking the body for its position, but this could be adjusted—for example, a force pushing at the edge of a box, causing it to spin across the canvas, much like dice tumbling when thrown.</p>
|
||||
<p>Here, the <code>Box</code> class’s <code>applyForce()</code> method receives a force vector and simply passes it along to Matter.js’s <code>applyForce()</code> method to apply it to the corresponding body. The key difference with this approach is that Matter.js is a more sophisticated engine than the examples from <a href="/forces#section-forces">Chapter 2</a>. The earlier examples assumed that the force was always applied at the mover’s center. Here, I’ve specified the exact position on the body where the force is applied. In this case, I’ve just applied it to the center as before by asking the body for its position, but this could be adjusted—for example, a force pushing at the edge of a box, causing it to spin across the canvas, much like dice tumbling when thrown.</p>
|
||||
<p>How can I bring forces into a Matter.js-driven sketch? Say I want to use a gravitational attraction force. Remember the code from Example 2.6 in the <code>Attractor</code> class?</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> attract(mover) {
|
||||
let force = p5.Vector.sub(this.position, mover.position);
|
||||
|
@ -937,7 +937,7 @@ Composite.add(engine.world, mouseConstraint);</pre>
|
|||
return force;
|
||||
}
|
||||
}</pre>
|
||||
<p>In addition to writing a custom <code>attract()</code> method for Example 6.9, two other key elements are required for the sketch to behave more like the example from <a href="/forces#">Chapter 2</a>. First, remember that a Matter.js <code>Engine</code> has a default gravity pointing down. I need to disable it in <code>setup()</code> with a <code>(0, 0)</code> vector:</p>
|
||||
<p>In addition to writing a custom <code>attract()</code> method for Example 6.9, two other key elements are required for the sketch to behave more like the example from <a href="/forces#section-forces">Chapter 2</a>. First, remember that a Matter.js <code>Engine</code> has a default gravity pointing down. I need to disable it in <code>setup()</code> with a <code>(0, 0)</code> vector:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">engine = Engine.create();
|
||||
//{!1} Disable the default gravity.
|
||||
engine.gravity = Vector.create(0, 0);</pre>
|
||||
|
@ -962,7 +962,7 @@ engine.gravity = Vector.create(0, 0);</pre>
|
|||
</div>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-68">Exercise 6.8</h3>
|
||||
<p>Convert any of the steering behavior examples from <a href="/autonomous-agents#">Chapter 5</a> to Matter.js. What does flocking look like with collisions?</p>
|
||||
<p>Convert any of the steering behavior examples from <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a> to Matter.js. What does flocking look like with collisions?</p>
|
||||
</div>
|
||||
<h2 id="collision-events">Collision Events</h2>
|
||||
<p>This book isn’t called <em>The Nature of Matter.js</em>, so I’m not going to cover every possible feature of the Matter.js library. At this point, I’ve gone over the basics of creating bodies and constraints, and shown you some of what the library can do. With the skills you’ve gained, hopefully the learning process will be considerably less painful when it comes time to use an aspect of Matter.js that I haven’t addressed here. Before moving on, however, one more feature of the library is worth covering: collision events.</p>
|
||||
|
@ -1261,7 +1261,7 @@ function setup() {
|
|||
circle(this.particle.x, this.particle.y, this.r * 2);
|
||||
}
|
||||
}</pre>
|
||||
<p>Looking over this code, you might first notice that drawing the particle is as simple as grabbing the <code>x</code> and <code>y</code> properties and using them with <code>circle()</code>. Second, you might notice that this <code>Particle</code> class doesn’t do much beyond storing a reference to a <code>VerletParticle2D</code> object. This hints at something important. Think back to the discussion of inheritance in <a href="/particles#">Chapter 4</a>, and then ask yourself: What is a <code>Particle</code> object other than an augmented <code>VerletParticle2D</code> object? Why bother making two objects—a <code>Particle</code> and a <code>VerletParticle2D</code>—for every one particle in the world, when I could simply extend the <code>VerletParticle2D</code> class to include the extra code needed to draw the particle?</p>
|
||||
<p>Looking over this code, you might first notice that drawing the particle is as simple as grabbing the <code>x</code> and <code>y</code> properties and using them with <code>circle()</code>. Second, you might notice that this <code>Particle</code> class doesn’t do much beyond storing a reference to a <code>VerletParticle2D</code> object. This hints at something important. Think back to the discussion of inheritance in <a href="/particles#section-particles">Chapter 4</a>, and then ask yourself: What is a <code>Particle</code> object other than an augmented <code>VerletParticle2D</code> object? Why bother making two objects—a <code>Particle</code> and a <code>VerletParticle2D</code>—for every one particle in the world, when I could simply extend the <code>VerletParticle2D</code> class to include the extra code needed to draw the particle?</p>
|
||||
<pre class="codesplit" data-code-language="javascript">class Particle extends VerletParticle2D {
|
||||
constructor(x, y, r) {
|
||||
//{!1} Call <code>super()</code> with <code>(x, y)</code> so the object is initialized properly.
|
||||
|
@ -1311,7 +1311,7 @@ let spring = new VerletSpring2D(particle1, particle2, length, strength);</pre>
|
|||
particle1.y = mouseY;
|
||||
particle1.unlock();
|
||||
}</pre>
|
||||
<p>And with that, I’m ready to put all these elements together in a simple sketch with two particles connected by a spring. One particle is permanently locked in place, and the other can be moved by dragging the mouse. This example is virtually identical to Example 3.11 from <a href="/oscillation#">Chapter 3</a>.</p>
|
||||
<p>And with that, I’m ready to put all these elements together in a simple sketch with two particles connected by a spring. One particle is permanently locked in place, and the other can be moved by dragging the mouse. This example is virtually identical to Example 3.11 from <a href="/oscillation#section-oscillation">Chapter 3</a>.</p>
|
||||
<div data-type="example">
|
||||
<h3 id="example-611-simple-spring-with-toxiclibsjs">Example 6.11: Simple Spring with Toxiclibs.js</h3>
|
||||
<figure>
|
||||
|
@ -1708,7 +1708,7 @@ let behavior = new AttractionBehavior(particle, distance, strength);</pre>
|
|||
circle(this.x, this.y, this.r * 2);
|
||||
}
|
||||
}</pre>
|
||||
<p>I can now remake the attraction example from <a href="/forces#">Chapter 2</a> with a single <code>Attractor</code> object that exerts an attraction behavior anywhere on the canvas. Even though the attractor is centered, I’m using a distance threshold of the full <code>width</code> to account for any movement of the attractor, and for particles located outside the canvas boundaries.</p>
|
||||
<p>I can now remake the attraction example from <a href="/forces#section-forces">Chapter 2</a> with a single <code>Attractor</code> object that exerts an attraction behavior anywhere on the canvas. Even though the attractor is centered, I’m using a distance threshold of the full <code>width</code> to account for any movement of the attractor, and for particles located outside the canvas boundaries.</p>
|
||||
<div data-type="example">
|
||||
<h3 id="example-615-attraction-and-repulsion-behaviors">Example 6.15: Attraction (and Repulsion) Behaviors</h3>
|
||||
<figure>
|
||||
|
@ -1733,7 +1733,7 @@ let behavior = new AttractionBehavior(particle, distance, strength);</pre>
|
|||
circle(this.x, this.y, this.r * 2);
|
||||
}
|
||||
}</pre>
|
||||
<p>Just as discussed in <a href="/autonomous-agents#spatial-subdivisions">“Spatial Subdivisions”</a>, Toxiclibs.js projects with large numbers of particles interacting with one another can run very slowly because of the <span data-type="equation">N^2</span> nature of the algorithm (every particle checking every other particle). To speed up the simulation, you could use the manual <code>addForce()</code> method in conjunction with a binning algorithm. Keep in mind, this would also require you to manually calculate the attraction force, as the built-in <code>AttractionBehavior</code> would no longer apply.</p>
|
||||
<p>Just as discussed in <a href="/autonomous-agents#spatial-subdivisions" class="page-reference">“Spatial Subdivisions”</a>, Toxiclibs.js projects with large numbers of particles interacting with one another can run very slowly because of the <span data-type="equation">N^2</span> nature of the algorithm (every particle checking every other particle). To speed up the simulation, you could use the manual <code>addForce()</code> method in conjunction with a binning algorithm. Keep in mind, this would also require you to manually calculate the attraction force, as the built-in <code>AttractionBehavior</code> would no longer apply.</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-614">Exercise 6.14</h3>
|
||||
<p>Use <code>AttractionBehavior</code> in conjunction with spring forces.</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="chapter">
|
||||
<section data-type="chapter" id="section-cellular-automata">
|
||||
<h1 id="chapter-7-cellular-automata">Chapter 7. Cellular Automata</h1>
|
||||
<div class="chapter-opening-quote">
|
||||
<blockquote data-type="epigraph">
|
||||
|
@ -17,7 +17,7 @@
|
|||
<h3 id="kente-cloth-photo-by-zsm">Kente cloth (photo by ZSM)</h3>
|
||||
<p>Originating from the Akan people of Ghana, kente cloth is a woven fabric celebrated for its vibrant colors and intricate patterns. Woven in narrow strips, each design is unique, and when joined, the strips form a tapestry of complex and emergent patterns that tell a story or carry a message. The image shows three typical Ewe kente stripes, highlighting the diverse weaving traditions that reflect the rich cultural tapestry of Ghana.</p>
|
||||
</div>
|
||||
<p>In <a href="/autonomous-agents#">Chapter 5</a>, I defined a complex system as a network of elements with short-range relationships, operating in parallel, that exhibit emergent behavior. I created a flocking simulation to demonstrate how a complex system adds up to more than the sum of its parts. In this chapter, I’m going to turn to developing other complex systems known as cellular automata.</p>
|
||||
<p>In <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a>, I defined a complex system as a network of elements with short-range relationships, operating in parallel, that exhibit emergent behavior. I created a flocking simulation to demonstrate how a complex system adds up to more than the sum of its parts. In this chapter, I’m going to turn to developing other complex systems known as cellular automata.</p>
|
||||
<p>In some respects, this shift may seem like a step backward. No longer will the individual elements of my systems be members of a physics world, driven by forces and vectors to move around the canvas. Instead, I’ll build systems out of the simplest digital element possible: a single bit. This bit is called a <strong>cell</strong>, and its value (0 or 1) is called its <strong>state</strong>. Working with such simple elements will help reveal how complex systems operate, and will offer an opportunity to elaborate on some programming techniques that apply to code-based projects. Building cellular automata will also set the stage for the rest of the book, where I’ll increasingly focus on systems and algorithms rather than vectors and motion—albeit systems and algorithms that I can and will apply to moving bodies.</p>
|
||||
<h2 id="what-is-a-cellular-automaton">What Is a Cellular Automaton?</h2>
|
||||
<p>A <strong>cellular automaton</strong> (<strong>cellular automata</strong> plural, or <strong>CA</strong> for short) is a model of a system of cell objects with the following characteristics:</p>
|
||||
|
@ -103,7 +103,7 @@
|
|||
<img src="images/07_ca/07_ca_13.png" alt="Figure 7.12: Translating a grid of 0s and 1s to white and black squares">
|
||||
<figcaption>Figure 7.12: Translating a grid of 0s and 1s to white and black squares</figcaption>
|
||||
</figure>
|
||||
<p>The low-resolution shape that emerges in Figure 7.12 is the <strong>Sierpiński triangle</strong>. Named after the Polish mathematician Wacław Sierpiński, it’s a famous example of a <strong>fractal</strong>. I’ll examine fractals more closely in <a href="/fractals#">Chapter 8</a>, but briefly, they’re patterns in which the same shapes repeat themselves at different scales. To give you a better sense of this, Figure 7.13 shows the CA over several more generations and with a wider grid size.</p>
|
||||
<p>The low-resolution shape that emerges in Figure 7.12 is the <strong>Sierpiński triangle</strong>. Named after the Polish mathematician Wacław Sierpiński, it’s a famous example of a <strong>fractal</strong>. I’ll examine fractals more closely in <a href="/fractals#section-fractals">Chapter 8</a>, but briefly, they’re patterns in which the same shapes repeat themselves at different scales. To give you a better sense of this, Figure 7.13 shows the CA over several more generations and with a wider grid size.</p>
|
||||
<figure>
|
||||
<img src="images/07_ca/07_ca_14.png" alt="Figure 7.13: Wolfram elementary CA ">
|
||||
<figcaption>Figure 7.13: Wolfram elementary CA </figcaption>
|
||||
|
@ -423,13 +423,13 @@ function rules(a, b, c) {
|
|||
<img src="images/07_ca/07_ca_26.png" alt="Figure 7.25: Rule 110 ">
|
||||
<figcaption>Figure 7.25: Rule 110 </figcaption>
|
||||
</figure>
|
||||
<p>In <a href="/autonomous-agents#">Chapter 5</a>, I introduced the concept of a complex system and used flocking to demonstrate how simple rules can result in emergent behaviors. Class 4 CAs remarkably exhibit the characteristics of complex systems and are the key to simulating phenomena such as forest fires, traffic patterns, and the spread of diseases. Research and applications of CA consistently emphasize the importance of class 4 as the bridge between CA and nature.</p>
|
||||
<p>In <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a>, I introduced the concept of a complex system and used flocking to demonstrate how simple rules can result in emergent behaviors. Class 4 CAs remarkably exhibit the characteristics of complex systems and are the key to simulating phenomena such as forest fires, traffic patterns, and the spread of diseases. Research and applications of CA consistently emphasize the importance of class 4 as the bridge between CA and nature.</p>
|
||||
<h2 id="the-game-of-life">The Game of Life</h2>
|
||||
<p>The next step is to move from a 1D CA to a 2D one: the Game of Life. This will introduce additional complexity—each cell will have a bigger neighborhood—but with the complexity comes a wider range of possible applications. After all, most of what happens in computer graphics lives in two dimensions, and this chapter demonstrates how to apply CA thinking to a 2D p5.js canvas.</p>
|
||||
<p>In 1970, Martin Gardner wrote a <em>Scientific American</em> article that documented mathematician John Conway’s new Game of Life, describing it as <em>recreational mathematics</em>: “To play life you must have a fairly large checkerboard and a plentiful supply of flat counters of two colors. It is possible to work with pencil and graph paper but it is much easier, particularly for beginners, to use counters and a board.”</p>
|
||||
<p>The Game of Life has become something of a computational cliché, as myriad projects display the game on LEDs, screens, projection surfaces, and so on. But practicing building the system with code is still valuable for a few reasons.</p>
|
||||
<p>For one, the Game of Life provides a good opportunity to practice skills with 2D arrays, nested loops, and more. Perhaps more important, however, this CA’s core principles are tied directly to a core goal of this book: simulating the natural world with code. The Game of Life algorithm and technical implementation will provide you with the inspiration and foundation to build simulations that exhibit the characteristics and behaviors of biological systems of reproduction.</p>
|
||||
<p>Unlike von Neumann, who created an extraordinarily complex system of states and rules, Conway wanted to achieve a similar lifelike result with the simplest set of rules possible. Gardner outlined Conway’s goals as follows.</p>
|
||||
<p>Unlike von Neumann, who created an extraordinarily complex system of states and rules, Conway wanted to achieve a similar lifelike result with the simplest set of rules possible. Gardner outlined Conway’s goals as follows:</p>
|
||||
<ol>
|
||||
<li><em>There should be no initial pattern for which there is a simple proof that the population can grow without limit.</em></li>
|
||||
<li><em>There should be initial patterns that apparently do grow without limit.</em></li>
|
||||
|
@ -743,7 +743,7 @@ board = next;</pre>
|
|||
<p>Create a CA in which each pixel is a cell and the pixel’s color is its state.</p>
|
||||
</div>
|
||||
<h3 id="historical">Historical</h3>
|
||||
<p>In the object-oriented Game of Life example, I used two variables to keep track of a cell’s current and previous states. What if you use an array to keep track of a cell’s state history over a longer period? This relates to the idea of a <em>complex adaptive system</em>, one that has the ability to change its rules over time by learning from its history. (Stay tuned for more on this concept in <a href="/genetic-algorithms#">Chapters 9</a> and <a href="/neural-networks#">10</a>.)</p>
|
||||
<p>In the object-oriented Game of Life example, I used two variables to keep track of a cell’s current and previous states. What if you use an array to keep track of a cell’s state history over a longer period? This relates to the idea of a <em>complex adaptive system</em>, one that has the ability to change its rules over time by learning from its history. (Stay tuned for more on this concept in <a href="/genetic-algorithms#section-genetic-algorithms">Chapters 9</a> and <a href="/neural-networks#section-neural-networks">10</a>.)</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-712">Exercise 7.12</h3>
|
||||
<p>Visualize the Game of Life by coloring each cell according to the amount of time it has been alive or dead. Can you also use the cell’s history to inform the rules?</p>
|
||||
|
@ -755,7 +755,7 @@ board = next;</pre>
|
|||
<p>Use CA rules in a flocking system. What if each boid has a state (that perhaps informs its steering behaviors), and its neighborhood changes from frame to frame as it moves closer to or farther from other boids?</p>
|
||||
</div>
|
||||
<h3 id="nesting"><strong>Nesting</strong></h3>
|
||||
<p>As discussed in <a href="/autonomous-agents#">Chapter 5</a>, a feature of complex systems is that they can be nested. A city is a complex system of people, a person is a complex system of organs, an organ is a complex system of cells, and so on. How could this be applied to a CA?</p>
|
||||
<p>As discussed in <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a>, a feature of complex systems is that they can be nested. A city is a complex system of people, a person is a complex system of organs, an organ is a complex system of cells, and so on. How could this be applied to a CA?</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-714">Exercise 7.14</h3>
|
||||
<p>Design a CA in which each cell is a smaller CA.</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="chapter">
|
||||
<section data-type="chapter" id="section-fractals">
|
||||
<h1 id="chapter-8-fractals">Chapter 8. Fractals</h1>
|
||||
<div class="chapter-opening-quote">
|
||||
<div class="chapter-opening-quote-long">
|
||||
|
@ -294,7 +294,7 @@ function drawCircles(x, y, radius) {
|
|||
line(this.start.x, this.start.y, this.end.x, this.end.y);
|
||||
}
|
||||
}</pre>
|
||||
<p>Now that I have the <code>KochLine</code> class, I can get started on <code>setup()</code> and <code>draw()</code>. I’ll need a data structure to keep track of what will eventually become many <code>KochLine</code> objects, and a JavaScript array will do just fine (see <a href="/particles#">Chapter 4</a> for a review of arrays):</p>
|
||||
<p>Now that I have the <code>KochLine</code> class, I can get started on <code>setup()</code> and <code>draw()</code>. I’ll need a data structure to keep track of what will eventually become many <code>KochLine</code> objects, and a JavaScript array will do just fine (see <a href="/particles#section-particles">Chapter 4</a> for a review of arrays):</p>
|
||||
<pre class="codesplit" data-code-language="javascript">let segments = [];</pre>
|
||||
<p>In <code>setup()</code>, I’ll want to add the first line segment to the array, a line that stretches from 0 to the width of the canvas:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">function setup() {
|
||||
|
@ -314,7 +314,7 @@ function drawCircles(x, y, radius) {
|
|||
}
|
||||
}</pre>
|
||||
<p>This is my foundation for the sketch. I have a <code>KochLine</code> class that keeps track of a line from point <code>start</code> to point <code>end</code>, and I have an array that keeps track of all the <code>KochLine</code> objects. Given these elements, how and where should I apply the Koch rules and the principles of recursion?</p>
|
||||
<p>Remember the Game of Life cellular automaton from <a href="/cellular-automata#">Chapter 7</a>? In that simulation, I always kept track of two generations: current and next. When I was finished calculating the next generation, <em>next</em> became <em>current</em>, and I moved on to computing the new next generation. I’m going to apply a similar technique here. I have a <code>segments</code> array listing the current set of line segments (at the start of the program, there’s only one). Now I need a second array (I’ll call it <code>next</code>), where I can place all the new <code>KochLine</code> objects generated from applying the Koch rules. For every single <code>KochLine</code> in the current array, four new line segments will be added to <code>next</code>. When I’m done, the <code>next</code> array becomes the new <code>segments</code> array (see Figure 8.13).</p>
|
||||
<p>Remember the Game of Life cellular automaton from <a href="/cellular-automata#section-cellular-automata">Chapter 7</a>? In that simulation, I always kept track of two generations: current and next. When I was finished calculating the next generation, <em>next</em> became <em>current</em>, and I moved on to computing the new next generation. I’m going to apply a similar technique here. I have a <code>segments</code> array listing the current set of line segments (at the start of the program, there’s only one). Now I need a second array (I’ll call it <code>next</code>), where I can place all the new <code>KochLine</code> objects generated from applying the Koch rules. For every single <code>KochLine</code> in the current array, four new line segments will be added to <code>next</code>. When I’m done, the <code>next</code> array becomes the new <code>segments</code> array (see Figure 8.13).</p>
|
||||
<figure>
|
||||
<img src="images/08_fractals/08_fractals_15.png" alt="Figure 8.13: The next generation of the fractal is calculated from the current generation. Then next becomes the new current in the transition from one generation to another.">
|
||||
<figcaption>Figure 8.13: The next generation of the fractal is calculated from the current generation. Then <em>next</em> becomes the new <em>current</em> in the transition from one generation to another.</figcaption>
|
||||
|
@ -335,12 +335,12 @@ function drawCircles(x, y, radius) {
|
|||
segments = next;
|
||||
}</pre>
|
||||
<p>By calling <code>generate()</code> over and over, the Koch curve rules will be recursively applied to the existing set of <code>KochLine</code> segments. But, of course, I’ve skipped over the real work of the function: How do I actually break one line segment into four as described by the rules? I need a way to calculate the start and end points of each line.</p>
|
||||
<p>Because the <code>KochLine</code> class uses <code>p5.Vector</code> objects to store the start and end points, this is a wonderful opportunity to practice all that vector math from <a href="/vectors#">Chapter 1</a>, along with some trigonometry from <a href="/oscillation#">Chapter 3</a>. First, I should establish the scope of the problem: How many points do I need to compute for each <code>KochLine</code> object? Figure 8.14 shows the answer.</p>
|
||||
<p>Because the <code>KochLine</code> class uses <code>p5.Vector</code> objects to store the start and end points, this is a wonderful opportunity to practice all that vector math from <a href="/vectors#section-vectors">Chapter 1</a>, along with some trigonometry from <a href="/oscillation#section-oscillation">Chapter 3</a>. First, I should establish the scope of the problem: How many points do I need to compute for each <code>KochLine</code> object? Figure 8.14 shows the answer.</p>
|
||||
<figure>
|
||||
<img src="images/08_fractals/08_fractals_16.png" alt="Figure 8.14: Two points become five points.">
|
||||
<figcaption>Figure 8.14: Two points become five points.</figcaption>
|
||||
</figure>
|
||||
<p>As the figure illustrates, I need to turn the two points (<em>start</em>, <em>end</em>) into five (<em>a</em>, <em>b</em>, <em>c</em>, <em>d</em>, <em>e</em>) to generate the four new line segments (<em>a</em>→<em>b</em>, <em>b</em>→<em>c</em>, <em>c</em>→<em>d</em>, <em>d</em>→<em>e</em>):</p>
|
||||
<p>As the figure illustrates, I need to turn the two points (<em>start</em>, <em>end</em>) into five (<em>a</em>, <em>b</em>, <em>c</em>, <em>d</em>, <em>e</em>) to generate the four new line segments (<em>a </em>→ <em>b</em>, <em>b </em>→ <em>c</em>, <em>c </em>→ <em>d</em>, <em>d </em>→ <em>e</em>):</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> next.add(new KochLine(a, b));
|
||||
next.add(new KochLine(b, c));
|
||||
next.add(new KochLine(c, d));
|
||||
|
@ -362,7 +362,7 @@ function drawCircles(x, y, radius) {
|
|||
<p>Wait, let’s take a look at this one line of code a little bit more closely:</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> // This is object destructuring, but for an array!
|
||||
let [a, b, c, d, e] = segment.kochPoints();</pre>
|
||||
<p>As you may recall, in <a href="/physics-libraries#">Chapter 6</a> I explained <strong>object destructuring</strong> as a means of extracting properties from an object and assigning them to individual variables. Guess what? You can do the same with arrays! Here, as long as the <code>kochPoints()</code> method returns an array of five elements, I can conveniently unpack and assign them, each to its respective variables: <code>a</code>, <code>b</code>, <code>c</code>, <code>d</code>, and <code>e</code>. It’s a lovely way to handle multiple return values. Just as with objects, <strong>array destructuring</strong> keeps the code neat and tidy.</p>
|
||||
<p>As you may recall, in <a href="/physics-libraries#section-physics-libraries">Chapter 6</a> I explained <strong>object destructuring</strong> as a means of extracting properties from an object and assigning them to individual variables. Guess what? You can do the same with arrays! Here, as long as the <code>kochPoints()</code> method returns an array of five elements, I can conveniently unpack and assign them, each to its respective variables: <code>a</code>, <code>b</code>, <code>c</code>, <code>d</code>, and <code>e</code>. It’s a lovely way to handle multiple return values. Just as with objects, <strong>array destructuring</strong> keeps the code neat and tidy.</p>
|
||||
<p>Now I just need to write a new <code>kochPoints()</code> method in the <code>KochLine</code> class that returns an array of <code>p5.Vector</code> objects representing the points <em>a</em> through <em>e</em> in Figure 8.15. I’ll knock off <em>a</em> and <em>e</em> first, which are the easiest—they’re just copies of the <code>start</code> and <code>end</code> points of the original line:</p>
|
||||
<div class="snip-below">
|
||||
<pre class="codesplit" data-code-language="javascript"> kochPoints() {
|
||||
|
@ -447,7 +447,7 @@ function setup() {
|
|||
</div>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-85">Exercise 8.5</h3>
|
||||
<p>Use recursion to draw the Sierpiński triangle (as seen in <a href="/cellular-automata#">Chapter 7</a>’s Wolfram elementary CA).</p>
|
||||
<p>Use recursion to draw the Sierpiński triangle (as seen in <a href="/cellular-automata#section-cellular-automata">Chapter 7</a>’s Wolfram elementary CA).</p>
|
||||
<figure>
|
||||
<img src="images/08_fractals/08_fractals_19.png" alt=" ">
|
||||
<figcaption> </figcaption>
|
||||
|
@ -463,7 +463,7 @@ function setup() {
|
|||
<figcaption>Figure 8.17: Each generation of a fractal tree, following the given production rules. The final tree is several generations later.</figcaption>
|
||||
</figure>
|
||||
<p>Once again, I have a nice fractal with a recursive definition: a branch is a line with two branches connected to it. What makes this fractal a bit more difficult than the previous ones is the use of the word <em>rotate</em> in the fractal’s rules. Each new branch must rotate relative to the previous branch, which is rotated relative to all its previous branches. Luckily, p5.js has a mechanism to keep track of rotations: <strong>transformations</strong>.</p>
|
||||
<p>I touched on transformations in <a href="/oscillation#">Chapter 3</a>. They’re a set of functions, such as <code>translate()</code>, <code>rotate()</code>, <code>scale()</code>, <code>push()</code>, and <code>pop()</code>, that allow you to change the position, orientation, and scale of shapes in your sketch. The <code>translate()</code> function moves the coordinate system, <code>rotate()</code> rotates it, and <code>push()</code> and <code>pop()</code> help save and restore the current transformation state. If you aren’t familiar with these functions, I have a <a href="https://thecodingtrain.com/transformations">set of videos on transformations in p5.js available at the Coding Train website</a>.</p>
|
||||
<p>I touched on transformations in <a href="/oscillation#section-oscillation">Chapter 3</a>. They’re a set of functions, such as <code>translate()</code>, <code>rotate()</code>, <code>scale()</code>, <code>push()</code>, and <code>pop()</code>, that allow you to change the position, orientation, and scale of shapes in your sketch. The <code>translate()</code> function moves the coordinate system, <code>rotate()</code> rotates it, and <code>push()</code> and <code>pop()</code> help save and restore the current transformation state. If you aren’t familiar with these functions, I have a <a href="https://thecodingtrain.com/transformations">set of videos on transformations in p5.js available at the Coding Train website</a>.</p>
|
||||
<p>I’ll begin by drawing a single branch, the trunk of the tree. Since I’m going to be using the <code>rotate()</code> function, I need to make sure I’m continuously translating along the branches while drawing. Remember, when you rotate in p5.js, you’re always rotating around the origin, or point (0, 0), so here the origin must always be translated to the start of the next branch being drawn (equivalent to the end of the previous branch). Since the trunk starts at the bottom of the window, I first have to translate to that spot:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">translate(width / 2, height);</pre>
|
||||
<p>Then I can draw the trunk as a line upward:</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="chapter">
|
||||
<section data-type="chapter" id="section-genetic-algorithms">
|
||||
<h1 id="chapter-9-evolutionary-computing">Chapter 9. Evolutionary Computing</h1>
|
||||
<div class="chapter-opening-quote">
|
||||
<blockquote data-type="epigraph">
|
||||
|
@ -28,7 +28,7 @@
|
|||
<ul>
|
||||
<li><strong>Traditional genetic algorithm:</strong> I’ll begin with the traditional, textbook GA. This algorithm was developed to solve problems in computer science for which the solution space is so vast that a <em>brute-force</em> algorithm would take too long. Here’s an example: I’m thinking of a number between one and one billion. How long will it take you to guess it? With a brute-force approach, you’d have to check every possible solution. Is it one? Is it two? Is it three? Is it four? . . . Luck plays a factor here (maybe I happened to pick five!), but on average, you would end up spending years counting up from one before hitting the correct answer. However, what if I could tell you whether your answer was good or bad? Warm or cold? Very warm? Hot? Ice frigid? If you could evaluate how close (or <em>fit</em>) your guesses are, you could start picking numbers accordingly and arrive at the answer more quickly. Your answer would <em>evolve</em>.</li>
|
||||
<li><strong>Interactive selection: </strong>After<strong> </strong>exploring the traditional computer science version, I’ll examine other applications of GAs in the visual arts. <em>Interactive selection</em> refers to the process of evolving something (often a computer-generated image) through user interaction. Let’s say you walk into a museum gallery and see 10 paintings. With interactive selection, you might pick your favorites and allow an algorithmic process to generate (or <em>evolve</em>) new paintings based on your preferences.</li>
|
||||
<li><strong>Ecosystem simulation:</strong> The traditional computer science GA and interactive selection technique are what you’ll likely find if you search online or read a textbook about artificial intelligence. But as you’ll soon see, they don’t really simulate the process of evolution as it happens in the physical world. In this chapter, I’ll also explore techniques for simulating evolution in an ecosystem of artificial creatures. How can the objects that move about a canvas meet each other, mate, and pass their genes on to a new generation? This could apply directly to the Ecosystem Project outlined at the end of each chapter. It will also be particularly relevant as I explore neuroevolution in <a href="/neuroevolution#">Chapter 11</a>.</li>
|
||||
<li><strong>Ecosystem simulation:</strong> The traditional computer science GA and interactive selection technique are what you’ll likely find if you search online or read a textbook about artificial intelligence. But as you’ll soon see, they don’t really simulate the process of evolution as it happens in the physical world. In this chapter, I’ll also explore techniques for simulating evolution in an ecosystem of artificial creatures. How can the objects that move about a canvas meet each other, mate, and pass their genes on to a new generation? This could apply directly to the Ecosystem Project outlined at the end of each chapter. It will also be particularly relevant as I explore neuroevolution in <a href="/neuroevolution#section-neuroevolution">Chapter 11</a>.</li>
|
||||
</ul>
|
||||
<h2 id="why-use-genetic-algorithms">Why Use Genetic Algorithms?</h2>
|
||||
<p>To help illustrate the utility of the traditional GA, I’m going to start with cats. No, not just your everyday feline friends. I’m going to start with some purr-fect cats that paw-sess a talent for typing, with the goal of producing the complete works of Shakespeare (Figure 9.1).</p>
|
||||
|
@ -38,7 +38,7 @@
|
|||
</figure>
|
||||
<p>This is my meow-velous<em> </em>twist on the <strong>infinite monkey theorem</strong>,<strong> </strong>which<strong> </strong>is stated as follows: a monkey hitting keys randomly on a typewriter will eventually type the complete works of Shakespeare, given an infinite amount of time. It’s only a theory because in practice the number of possible combinations of letters and words makes the likelihood of the monkey <em>actually</em> typing Shakespeare minuscule. To put it in perspective, even if the monkey had started typing at the beginning of the universe, the probability that by now it would have produced just <em>Hamlet</em>, to say nothing of the <em>entire</em> <em>works</em> of Shakespeare, is still absurdly unlikely.</p>
|
||||
<p>Consider a cat named Clawdius. Clawdius types on a reduced typewriter containing only 27 characters: the 26 English letters plus the spacebar. The probability of Clawdius hitting any given key is 1 in 27.</p>
|
||||
<p>Next, consider the phrase “to be or not to be that is the question” (for simplicity, I’m ignoring capitalization and punctuation). The phrase is 39 characters long, including spaces. If Clawdius starts typing, the chance he’ll get the first character right is 1 in 27. Since the probability he’ll get the second character right is also 1 in 27, he has a 1 in 729 (<span data-type="equation">27 \times 27</span>) chance of landing the first two characters in correct order. (This follows directly from our discussion of probability in <a href="/random#">Chapter 0</a>.) Therefore, the probability that Clawdius will type the full phrase is 1 in 27 multiplied by itself 39 times, or <span data-type="equation">(1/27)^{39}</span>. That equals a probability of . . .</p>
|
||||
<p>Next, consider the phrase “to be or not to be that is the question” (for simplicity, I’m ignoring capitalization and punctuation). The phrase is 39 characters long, including spaces. If Clawdius starts typing, the chance he’ll get the first character right is 1 in 27. Since the probability he’ll get the second character right is also 1 in 27, he has a 1 in 729 (<span data-type="equation">27 \times 27</span>) chance of landing the first two characters in correct order. (This follows directly from our discussion of probability in <a href="/random#section-random">Chapter 0</a>.) Therefore, the probability that Clawdius will type the full phrase is 1 in 27 multiplied by itself 39 times, or <span data-type="equation">(1/27)^{39}</span>. That equals a probability of . . .</p>
|
||||
<div data-type="equation">1 \text{ in } \text{66,555,937,033,867,822,607,895,549,241,096,482,953,017,615,834,735,226,163}</div>
|
||||
<p>Needless to say, even hitting just this one phrase, let alone an entire play, let alone all 38 Shakespeare plays (yes, even <em>The Two Noble Kinsmen</em>) is highly unlikely. Even if Clawdius were a computer simulation and could type a million random phrases per second, for Clawdius to have a 99 percent probability of eventually getting just the one phrase right, he would have to type for 9,719,096,182,010,563,073,125,591,133,903,305,625,605,017 years. (For comparison, the universe is estimated to be a mere 13,750,000,000 years old.)</p>
|
||||
<p>The point of all these unfathomably large numbers isn’t to give you a headache, but to demonstrate that a brute-force algorithm (typing every possible random phrase) isn’t a reasonable strategy for arriving randomly at “to be or not to be that is the question.” Enter GAs, which start with random phrases and swiftly find the solution through simulated evolution, leaving plenty of time for Clawdius to savor a cozy catnap.</p>
|
||||
|
@ -80,7 +80,7 @@ console.log(s);</pre>
|
|||
</table>
|
||||
<p>Sure, these phrases have variety, but try to mix and match the characters every which way and you’ll never get <em>cat</em>. There isn’t <em>enough</em> variety here to evolve the optimal solution. However, if I had a population of thousands of phrases, all generated randomly, chances are that at least one phrase would have a <em>c</em> as the first character, one would have an <em>a</em> as the second, and one a <em>t</em> as the third. A large population will most likely provide enough variety to generate the desired phrase. (In step 3 of the algorithm, I’ll also demonstrate another mechanism to introduce more variation in case there isn’t enough in the first place.) Step 1 can therefore be described as follows:</p>
|
||||
<p><span class="highlight">Create a population of randomly generated elements.</span></p>
|
||||
<p><em>Element</em> is perhaps a better, more general-purpose term than <em>creature</em>. But what is the element? As you move through the examples in this chapter, you’ll see several scenarios; you might have a population of images or a population of vehicles à la <a href="/autonomous-agents#">Chapter 5</a>. The part that’s new in this chapter is that each element, each member of the population, has <em>virtual DNA</em>, a set of properties (you could also call them <em>genes</em>) that describe how a given element looks or behaves. For the typing cats, for example, the DNA could be a string of characters. With this in mind, I can be even more specific and describe step 1 of the GA as follows:</p>
|
||||
<p><em>Element</em> is perhaps a better, more general-purpose term than <em>creature</em>. But what is the element? As you move through the examples in this chapter, you’ll see several scenarios; you might have a population of images or a population of vehicles à la <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a>. The part that’s new in this chapter is that each element, each member of the population, has <em>virtual DNA</em>, a set of properties (you could also call them <em>genes</em>) that describe how a given element looks or behaves. For the typing cats, for example, the DNA could be a string of characters. With this in mind, I can be even more specific and describe step 1 of the GA as follows:</p>
|
||||
<p><span class="highlight">Create a population of <em>N</em> elements, each with randomly generated DNA.</span></p>
|
||||
<p>The field of genetics makes an important distinction between the concepts of genotype and phenotype. The actual genetic code—the particular sequence of molecules in the DNA—is an organism’s <strong>genotype</strong>. This is what gets passed down from generation to generation. The <strong>phenotype</strong>, by contrast, is the <em>expression</em> of that data—this cat will be big, that cat will be small, that other cat will be a particularly fast and effective typist.</p>
|
||||
<p>The genotype/phenotype distinction is key to creatively using GAs. What are the objects in your world? How will you design the genotype for those objects—the data structure to store each object’s properties, and the values those properties take on? And how will you use that information to design the phenotype? That is, what do <em>you</em> want these variables to actually express?</p>
|
||||
|
@ -418,7 +418,7 @@ function setup() {
|
|||
}
|
||||
}</pre>
|
||||
<p>Once the fitness scores have been computed, the next step is to build the <em>mating pool</em> for the reproduction process. The mating pool is a data structure from which two parents are repeatedly selected. Recalling the description of the selection process, the goal is to pick parents with probabilities calculated according to fitness. The members of the population with the highest fitness scores should be the most likely to be selected; those with the lowest scores, the least likely.</p>
|
||||
<p>In <a href="/random#">Chapter 0</a>, I covered the basics of probability and generating a custom distribution of random numbers. I’m going to use the same techniques here to assign a probability to each member of the population, picking parents by spinning the wheel of fortune. Revisiting Figure 9.2, your mind might immediately go back to <a href="/oscillation#">Chapter 3</a> and contemplate coding a simulation of an actual spinning wheel. As fun as this might be (and you should make one!), it’s quite unnecessary.</p>
|
||||
<p>In <a href="/random#section-random">Chapter 0</a>, I covered the basics of probability and generating a custom distribution of random numbers. I’m going to use the same techniques here to assign a probability to each member of the population, picking parents by spinning the wheel of fortune. Revisiting Figure 9.2, your mind might immediately go back to <a href="/oscillation#section-oscillation">Chapter 3</a> and contemplate coding a simulation of an actual spinning wheel. As fun as this might be (and you should make one!), it’s quite unnecessary.</p>
|
||||
<div class="half-width-right">
|
||||
<figure>
|
||||
<img src="images/09_ga/09_ga_8.png" alt="Figure 9.7: A bucket full of letters A, B, C, D, and E. The higher the fitness, the more instances of the letter in the bucket.">
|
||||
|
@ -444,7 +444,7 @@ function setup() {
|
|||
<pre class="codesplit" data-code-language="javascript"> let parentA = random(matingPool);
|
||||
let parentB = random(matingPool);</pre>
|
||||
</div>
|
||||
<p>This method of building a mating pool and choosing parents from it works, but it isn’t the only way to perform selection. Other, more memory-efficient techniques don’t require an additional array full of multiple references to each element. For example, think back to the discussion of nonuniform distributions of random numbers in <a href="/random#">Chapter 0</a>. There, I implemented the accept-reject method. If applied here, the approach would be to randomly pick an element from the original <code>population</code> array, and then pick a second, qualifying random number to check against the element’s fitness value. If the fitness is less than the qualifying number, start again and pick a new element. Keep going until two parents are deemed fit enough.</p>
|
||||
<p>This method of building a mating pool and choosing parents from it works, but it isn’t the only way to perform selection. Other, more memory-efficient techniques don’t require an additional array full of multiple references to each element. For example, think back to the discussion of nonuniform distributions of random numbers in <a href="/random#section-random">Chapter 0</a>. There, I implemented the accept-reject method. If applied here, the approach would be to randomly pick an element from the original <code>population</code> array, and then pick a second, qualifying random number to check against the element’s fitness value. If the fitness is less than the qualifying number, start again and pick a new element. Keep going until two parents are deemed fit enough.</p>
|
||||
<p>Yet another excellent alternative is worth exploring that similarly capitalizes on the principle of fitness-proportionate selection. To understand how it works, imagine a relay race in which each member of the population runs a given distance tied to its fitness. The higher the fitness, the farther they run. Let’s also assume that the fitness values have been normalized to all add up to 1 (just as with the wheel of fortune). The first step is to pick a <em>starting line—</em>a random distance from the finish. This distance is a random number from 0 to 1. (You’ll see in a moment that the finish line<em> </em>is assumed to be at 0.)</p>
|
||||
<pre class="codesplit" data-code-language="javascript">let start = random(1);</pre>
|
||||
<p>Then the relay race begins at the starting line with the first member of the population:</p>
|
||||
|
@ -479,7 +479,7 @@ while (start > 0) {
|
|||
<p>Depending on the specific requirements and constraints of your application of GAs, one approach might prove more suitable than the other. I’ll alternate between them in the examples outlined in this chapter.</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-92">Exercise 9.2</h3>
|
||||
<p>Revisit the accept-reject algorithm from <a href="/random#">Chapter 0</a> and rewrite the <code>weightedSelection()</code> function to use accept-reject instead. Like the relay race method, this technique can also end up being computationally intensive, since several potential parents may be rejected as unfit before one is finally chosen.</p>
|
||||
<p>Revisit the accept-reject algorithm from <a href="/random#section-random">Chapter 0</a> and rewrite the <code>weightedSelection()</code> function to use accept-reject instead. Like the relay race method, this technique can also end up being computationally intensive, since several potential parents may be rejected as unfit before one is finally chosen.</p>
|
||||
</div>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-93">Exercise 9.3</h3>
|
||||
|
@ -984,7 +984,7 @@ let populationSize = 150;</pre>
|
|||
<p>In this section, I’m going to evolve my own simplified smart rockets, inspired by Thorp’s. When I get to the end of the section, I’ll leave implementing some of Thorp’s additional advanced features as an exercise.</p>
|
||||
<p>My rockets will have only one thruster, which will be able to fire in any direction with any strength for every frame of animation. This isn’t particularly realistic, but it will make building out the example a little easier. (You can always make the rocket and its thrusters more advanced and realistic later.)</p>
|
||||
<h3 id="developing-the-rockets">Developing the Rockets</h3>
|
||||
<p>To implement my evolving smart rockets, I’ll start by taking the <code>Mover</code> class from <a href="/forces#">Chapter 2</a> and renaming it <code>Rocket</code>:</p>
|
||||
<p>To implement my evolving smart rockets, I’ll start by taking the <code>Mover</code> class from <a href="/forces#section-forces">Chapter 2</a> and renaming it <code>Rocket</code>:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">class Rocket {
|
||||
constructor(x, y) {
|
||||
// A rocket has three vectors: position, velocity, and acceleration.
|
||||
|
@ -1059,7 +1059,7 @@ let populationSize = 150;</pre>
|
|||
</div>
|
||||
<figcaption>Figure 9.11: Vectors created with random <em>x</em> and <em>y</em> values (left) and using <code>p5.Vector.random2D()</code> (right)</figcaption>
|
||||
</figure>
|
||||
<p>As you may recall from <a href="/oscillation#">Chapter 3</a>, a better choice is to pick a random angle and create a vector of length 1 from that angle. This produces results that form a circle (see the right of Figure 9.11) and can be achieved with polar-to-Cartesian conversion or the trusty <code>p5.Vector.random2D()</code> method:</p>
|
||||
<p>As you may recall from <a href="/oscillation#section-oscillation">Chapter 3</a>, a better choice is to pick a random angle and create a vector of length 1 from that angle. This produces results that form a circle (see the right of Figure 9.11) and can be achieved with polar-to-Cartesian conversion or the trusty <code>p5.Vector.random2D()</code> method:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">for (let i = 0; i < length; i++) {
|
||||
//{!1} A random unit vector
|
||||
this.genes[i] = p5.Vector.random2D();
|
||||
|
@ -1463,7 +1463,7 @@ function nextGeneration() {
|
|||
</div>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-914">Exercise 9.14</h3>
|
||||
<p>Another of Karl Sims’s seminal works in the field of GAs is “Evolved Virtual Creatures.” In this project, a population of digital creatures in a simulated physics environment is evaluated for their ability to perform tasks, such as swimming, running, jumping, following, and competing for a green cube. The project uses a node-based genotype: the creature’s DNA isn’t a linear list of vectors or numbers, but a map of nodes (much like the soft-body simulation in <a href="/physics-libraries#">Chapter 6</a>). The phenotype is the creature’s body itself, a network of limbs connected with muscles.</p>
|
||||
<p>Another of Karl Sims’s seminal works in the field of GAs is “Evolved Virtual Creatures.” In this project, a population of digital creatures in a simulated physics environment is evaluated for their ability to perform tasks, such as swimming, running, jumping, following, and competing for a green cube. The project uses a node-based genotype: the creature’s DNA isn’t a linear list of vectors or numbers, but a map of nodes (much like the soft-body simulation in <a href="/physics-libraries#section-physics-libraries">Chapter 6</a>). The phenotype is the creature’s body itself, a network of limbs connected with muscles.</p>
|
||||
<div class="half-width-right">
|
||||
<figure>
|
||||
<img src="images/09_ga/09_ga_15.png" alt="">
|
||||
|
@ -1517,7 +1517,7 @@ function nextGeneration() {
|
|||
}
|
||||
}</pre>
|
||||
</div>
|
||||
<p>So far, I’m just rehashing the particle systems from <a href="/particles#">Chapter 4</a>. I have an entity called <code>Bloop</code> that moves around the canvas, and a class called <code>World</code> that manages a variable quantity of these entities. To turn this into a system that evolves, I need to add two additional features to my world:</p>
|
||||
<p>So far, I’m just rehashing the particle systems from <a href="/particles#section-particles">Chapter 4</a>. I have an entity called <code>Bloop</code> that moves around the canvas, and a class called <code>World</code> that manages a variable quantity of these entities. To turn this into a system that evolves, I need to add two additional features to my world:</p>
|
||||
<ul>
|
||||
<li><strong>Bloops die.</strong></li>
|
||||
<li><strong>Bloops are born.</strong></li>
|
||||
|
@ -1544,7 +1544,7 @@ function nextGeneration() {
|
|||
}</pre>
|
||||
<p>This is a good first step, but I haven’t really achieved anything. After all, if all bloops start with 100 health points and lose health at the same rate, then all bloops will live for the exact same amount of time and die together. If every single bloop lives the same amount of time, each one has an equal chance of reproducing, and therefore no evolutionary change will occur.</p>
|
||||
<p>You can achieve variable life spans in several ways with a more sophisticated world. One approach is to introduce predators that eat bloops. Faster bloops would be more likely to escape being eaten, leading to the evolution of increasingly faster bloops. Another option is to introduce food. When a bloop eats food, its health points increase, extending its life.</p>
|
||||
<p>Let’s assume I have an array of vector positions called <code>food</code>. I could test each bloop’s proximity to each food position. If the bloop is close enough, it eats the food (which is then removed from the world) and increases its health:</p>
|
||||
<p>Let’s assume I have an array of vector positions called <code>food</code>. I could test each bloop’s proximity to each food position. If the bloop is close enough, it eats the food (which is then removed from the world) and increases its health.</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> eat(food) {
|
||||
// Check all the food vectors.
|
||||
for (let i = food.length - 1; i >= 0; i--) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="chapter">
|
||||
<section data-type="chapter" id="section-neural-networks">
|
||||
<h1 id="chapter-10-neural-networks">Chapter 10. Neural Networks</h1>
|
||||
<div class="chapter-opening-quote">
|
||||
<blockquote data-type="epigraph">
|
||||
|
@ -27,7 +27,7 @@
|
|||
<figcaption>Figure 10.1: A neuron with dendrites and an axon connected to another neuron</figcaption>
|
||||
</figure>
|
||||
<p>Fortunately, as you’ve seen throughout this book, developing engaging animated systems with code doesn’t require scientific rigor or accuracy. Designing a smart rocket isn’t rocket science, and neither is designing an artificial neural network brain science. It’s enough to simply be inspired by the <em>idea</em> of brain function.</p>
|
||||
<p>In this chapter, I’ll begin with a conceptual overview of the properties and features of neural networks and build the simplest possible example of one, a network that consists of a single neuron. I’ll then introduce you to more complex neural networks by using the ml5.js library. This will serve as a foundation for <a href="/neuroevolution#">Chapter 11</a>, the grand finale of this book, where I’ll combine GAs with neural networks for physics simulation.</p>
|
||||
<p>In this chapter, I’ll begin with a conceptual overview of the properties and features of neural networks and build the simplest possible example of one, a network that consists of a single neuron. I’ll then introduce you to more complex neural networks by using the ml5.js library. This will serve as a foundation for <a href="/neuroevolution#section-neuroevolution">Chapter 11</a>, the grand finale of this book, where I’ll combine GAs with neural networks for physics simulation.</p>
|
||||
<h2 id="introducing-artificial-neural-networks">Introducing Artificial Neural Networks</h2>
|
||||
<p>Computer scientists have long been inspired by the human brain. In 1943, Warren S. McCulloch, a neuroscientist, and Walter Pitts, a logician, developed the first conceptual model of an artificial neural network. In their paper “A Logical Calculus of the Ideas Immanent in Nervous Activity,” they describe a <strong>neuron </strong>as a single computational cell living in a network of cells that receives inputs, processes those inputs, and generates an output.</p>
|
||||
<p>Their work, and the work of many scientists and researchers who followed, wasn’t meant to accurately describe how the biological brain works. Rather, an <em>artificial</em> neural network (hereafter referred to as just a <em>neural network</em>) was intended as a computational model based on the brain, designed to solve certain kinds of problems that were traditionally difficult for computers.</p>
|
||||
|
@ -56,14 +56,14 @@
|
|||
<ul>
|
||||
<li><strong>Supervised learning:</strong> Essentially, this strategy involves a teacher that’s smarter than the network itself. Take the case of facial recognition. The teacher shows the network a bunch of faces, and the teacher already knows the name associated with each face. The network makes its guesses; then the teacher provides the network with the actual names. The network can compare its answers to the known correct ones and make adjustments according to its errors. The neural networks in this chapter follow this model.</li>
|
||||
<li><strong>Unsupervised learning:</strong> This technique is required when you don’t have an example dataset with known answers. Instead, the network works on its own to uncover hidden patterns in the data. An application of this is clustering: a set of elements is divided into groups according to an unknown pattern. I won’t be showing any instances of unsupervised learning, as the strategy is less relevant to the book’s examples.</li>
|
||||
<li><strong>R</strong><strong>einforcement learning:</strong> This strategy is built on observation: a learning agent makes decisions and looks to its environment for the results. It’s rewarded for good decisions and penalized for bad decisions, such that it learns to make better decisions over time. I’ll discuss this strategy in more detail in <a href="/neuroevolution#">Chapter 11</a>.</li>
|
||||
<li><strong>R</strong><strong>einforcement learning:</strong> This strategy is built on observation: a learning agent makes decisions and looks to its environment for the results. It’s rewarded for good decisions and penalized for bad decisions, such that it learns to make better decisions over time. I’ll discuss this strategy in more detail in <a href="/neuroevolution#section-neuroevolution">Chapter 11</a>.</li>
|
||||
</ul>
|
||||
<p>The ability of a neural network to learn, to make adjustments to its structure over time, is what makes it so useful in the field of <strong>machine learning</strong>. This term can be traced back to the 1959 paper “Some Studies in Machine Learning Using the Game of Checkers,” in which computer scientist Arthur Lee Samuel outlines a “self-learning” program for playing checkers. The concept of an algorithm enabling a computer to learn without explicit programming is the foundation of machine learning.</p>
|
||||
<p>Think about what you’ve been doing throughout this book: coding! In traditional programming, a computer program takes inputs and, based on the rules you’ve provided, produces outputs. Machine learning, however, turns this approach upside down. Instead of you writing the rules, the system is given example inputs and outputs, and generates the rules itself! Many algorithms can be used to implement machine learning, and a neural network is just one of them.</p>
|
||||
<p>Machine learning is part of the broad, sweeping field of <strong>artificial intelligence (AI)</strong>, although the terms are sometimes used interchangeably. In their thoughtful and friendly primer <em>A People’s Guide to AI</em>, Mimi Onuoha and Diana Nucera (aka Mother Cyborg) define AI as “the theory and development of computer systems able to perform tasks that normally require human intelligence.” Machine learning algorithms are one approach to these tasks, but not all AI systems feature a self-learning component.</p>
|
||||
<h3 id="machine-learning-libraries">Machine Learning Libraries</h3>
|
||||
<p>Today, leveraging machine learning in creative coding and interactive media isn’t only feasible but increasingly common, thanks to third-party libraries that handle a lot of the neural network implementation details under the hood. While the vast majority of machine learning development and research is done in Python, the world of web development has seen the emergence of powerful JavaScript-based tools. Two libraries of note are TensorFlow.js and ml5.js.</p>
|
||||
<p>TensorFlow.js<strong> </strong>is an open source library that lets you define, train, and run neural networks directly in the browser using JavaScript, without the need to install or configure complex environments. It’s part of the TensorFlow ecosystem, which is maintained and developed by Google. TensorFlow.js is a powerful tool, but its low-level operations and highly technical API can be intimidating to beginners. Enter ml5.js, a library built on top of TensorFlow.js and designed specifically for use with p5.js. Its goal is to be beginner friendly and make machine learning approachable for a broad audience of artists, creative coders, and students. I’ll demonstrate how to use ml5.js in <a href="#machine-learning-with-ml5js">“Machine Learning with ml5.js”</a>.</p>
|
||||
<p>TensorFlow.js<strong> </strong>is an open source library that lets you define, train, and run neural networks directly in the browser using JavaScript, without the need to install or configure complex environments. It’s part of the TensorFlow ecosystem, which is maintained and developed by Google. TensorFlow.js is a powerful tool, but its low-level operations and highly technical API can be intimidating to beginners. Enter ml5.js, a library built on top of TensorFlow.js and designed specifically for use with p5.js. Its goal is to be beginner friendly and make machine learning approachable for a broad audience of artists, creative coders, and students. I’ll demonstrate how to use ml5.js in <a href="#machine-learning-with-ml5js" class="page-reference">“Machine Learning with ml5.js”</a>.</p>
|
||||
<p>A benefit of libraries like TensorFlow.js and ml5.js is that you can use them to run pretrained models. A machine learning <strong>model</strong> is a specific setup of neurons and connections, and a <strong>pretrained</strong> model is one that has already been prepared for a particular task. For example, popular pretrained models are used for classifying images, identifying body poses, recognizing facial landmarks or hand positions, and even analyzing the sentiment expressed in a text. You can use such a model as is or treat it as a starting point for additional learning (commonly referred to as <strong>transfer learning</strong>).</p>
|
||||
<p>Before I get to exploring the ml5.js library, however, I’d like to try my hand at building the simplest of all neural networks from scratch, using only p5.js, to illustrate how the concepts of neural networks and machine learning are implemented in code.</p>
|
||||
<h2 id="the-perceptron">The Perceptron</h2>
|
||||
|
@ -182,7 +182,7 @@ function activate(sum) {
|
|||
<img src="images/10_nn/10_nn_5.png" alt="Figure 10.4: A collection of points in 2D space divided by a line, representing plant categories according to their water and sunlight intake ">
|
||||
<figcaption>Figure 10.4: A collection of points in 2D space divided by a line, representing plant categories according to their water and sunlight intake</figcaption>
|
||||
</figure>
|
||||
<p>In truth, I don’t need a neural network—not even a simple perceptron—to tell me whether a point is above or below a line. I can see the answer for myself with my own eyes, or have my computer figure it out with simple algebra. But just like solving a problem with a known answer—“to be or not to be”—was a convenient first test for the GA in <a href="/genetic-algorithms#">Chapter 9</a>, training a perceptron to categorize points as being on one side of a line versus the other will be a valuable way to demonstrate the algorithm of the perceptron and verify that it’s working properly.</p>
|
||||
<p>In truth, I don’t need a neural network—not even a simple perceptron—to tell me whether a point is above or below a line. I can see the answer for myself with my own eyes, or have my computer figure it out with simple algebra. But just like solving a problem with a known answer—“to be or not to be”—was a convenient first test for the GA in <a href="/genetic-algorithms#section-genetic-algorithms">Chapter 9</a>, training a perceptron to categorize points as being on one side of a line versus the other will be a valuable way to demonstrate the algorithm of the perceptron and verify that it’s working properly.</p>
|
||||
<p>To solve this problem, I’ll give my perceptron two inputs: <span data-type="equation">x_0</span> is the x-coordinate of a point, representing a plant’s amount of sunlight, and <span data-type="equation">x_1</span> is the y-coordinate of that point, representing the plant’s amount of water. The perceptron then guesses the plant’s classification according to the sign of the weighted sum of these inputs. If the sum is positive, the perceptron outputs a +1, signifying a hydrophyte (above the line). If the sum is negative, it outputs a –1, signifying a xerophyte (below the line). Figure 10.5 shows this perceptron (note the shorthand of <span data-type="equation">w_0</span> and <span data-type="equation">w_1</span> for the weights).</p>
|
||||
<figure>
|
||||
<img src="images/10_nn/10_nn_6.png" alt="Figure 10.5: A perceptron with two inputs (x_0 and x_1), a weight for each input (w_0 and w_1), and a processing neuron that generates the output">
|
||||
|
@ -283,7 +283,7 @@ let guess = perceptron.feedForward(inputs);</pre>
|
|||
<p>This process can be packaged into a method on the <code>Perceptron</code> class, but before I can write it, I need to examine steps 3 and 4 in more detail. How do I define the perceptron’s error? And how should I adjust the weights according to this error?</p>
|
||||
<p>The perceptron’s error can be defined as the difference between the desired answer and its guess:</p>
|
||||
<div data-type="equation">\text{error} = \text{desired output} - \text{guess output}</div>
|
||||
<p>Does this formula look familiar? Think back to the formula for a vehicle’s steering force that I worked out in <a href="/autonomous-agents#">Chapter 5</a>:</p>
|
||||
<p>Does this formula look familiar? Think back to the formula for a vehicle’s steering force that I worked out in <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a>:</p>
|
||||
<div data-type="equation">\text{steering} = \text{desired velocity} - \text{current velocity}</div>
|
||||
<p>This is also a calculation of an error! The current velocity serves as a guess, and the error (the steering force) indicates how to adjust the velocity in the correct direction. Adjusting a vehicle’s velocity to follow a target is similar to adjusting the weights of a neural network toward the correct answer.</p>
|
||||
<p>For the perceptron, the output has only two possible values: +1 or –1. Therefore, only three errors are possible. If the perceptron guesses the correct answer, the guess equals the desired output and the error is 0. If the correct answer is –1 and the perceptron guessed +1, then the error is –2. If the correct answer is +1 and the perceptron guessed –1, then the error is +2. Here’s that process summarized in a table:</p>
|
||||
|
@ -423,10 +423,10 @@ if (y > yline) {
|
|||
//{!1} The answer becomes +1 if <code>y</code> is above the line.
|
||||
desired = 1;
|
||||
}</pre>
|
||||
<p>I can then make an input array to go with the <code>desired</code> output.</p>
|
||||
<p>I can then make an input array to go with the <code>desired</code> output:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">// Don’t forget to include the bias!
|
||||
let trainingInputs = [x, y, 1];</pre>
|
||||
<p>Assuming that I have a <code>perceptron</code> variable, I can train it by providing the inputs along with the desired answer.</p>
|
||||
<p>Assuming that I have a <code>perceptron</code> variable, I can train it by providing the inputs along with the desired answer:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">perceptron.train(trainingInputs, desired);</pre>
|
||||
<p>If I train the perceptron on a new random point (and its answer) for each cycle through <code>draw()</code>, it will gradually get better at classifying the points as above or below the line.</p>
|
||||
<div data-type="example">
|
||||
|
@ -540,9 +540,9 @@ function draw() {
|
|||
<p>The solution to optimizing the weights of a multilayered network is <strong>backpropagation</strong>. This process takes the error and feeds it backward through the network so it can adjust the weights of all the connections in proportion to how much they’ve contributed to the total error. The details of backpropagation are beyond the scope of this book. The algorithm uses a variety of activation functions (one classic example is the sigmoid function) as well as some calculus. If you’re interested in continuing down this road and learning more about how backpropagation works, you can find my <a href="https://thecodingtrain.com/neural-network">“Toy Neural Network” project at the Coding Train website with accompanying video tutorials</a>. They go through all the steps of solving XOR using a multilayered feed-forward network with backpropagation. For this chapter, however, I’d instead like to get some help and phone a friend.</p>
|
||||
<h2 id="machine-learning-with-ml5js">Machine Learning with ml5.js</h2>
|
||||
<p>That friend is ml5.js. This machine learning library can manage the details of complex processes like backpropagation so you and I don’t have to worry about them. As I mentioned earlier in the chapter, ml5.js aims to provide a friendly entry point for those who are new to machine learning and neural networks, while still harnessing the power of Google’s TensorFlow.js behind the scenes.</p>
|
||||
<p>To use ml5.js in a sketch, you must import it via a <code><script></code> element in your <em>index.html</em> file, much as you did with Matter.js and Toxiclibs.js in <a href="/physics-libraries#">Chapter 6</a>:</p>
|
||||
<p>To use ml5.js in a sketch, you must import it via a <code><script></code> element in your <em>index.html</em> file, much as you did with Matter.js and Toxiclibs.js in <a href="/physics-libraries#section-physics-libraries">Chapter 6</a>:</p>
|
||||
<pre class="codesplit" data-code-language="html"><script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script></pre>
|
||||
<p>My goal for the rest of this chapter is to introduce ml5.js by developing a system that can recognize mouse gestures. This will prepare you for <a href="/neuroevolution#">Chapter 11</a>, where I’ll add a neural network “brain” to an autonomous steering agent and tie machine learning back into the story of the book. First, however, I’d like to talk more generally through the steps of training a multilayered neural network model using supervised learning. Outlining these steps will highlight important decisions you’ll have to make before developing a learning model, introduce the syntax of the ml5.js library, and provide you with the context you’ll need before training your own machine learning models.</p>
|
||||
<p>My goal for the rest of this chapter is to introduce ml5.js by developing a system that can recognize mouse gestures. This will prepare you for <a href="/neuroevolution#section-neuroevolution">Chapter 11</a>, where I’ll add a neural network “brain” to an autonomous steering agent and tie machine learning back into the story of the book. First, however, I’d like to talk more generally through the steps of training a multilayered neural network model using supervised learning. Outlining these steps will highlight important decisions you’ll have to make before developing a learning model, introduce the syntax of the ml5.js library, and provide you with the context you’ll need before training your own machine learning models.</p>
|
||||
<h3 id="the-machine-learning-life-cycle">The Machine Learning Life Cycle</h3>
|
||||
<p>The life cycle of a machine learning model is typically broken into seven steps:</p>
|
||||
<ol>
|
||||
|
@ -742,10 +742,10 @@ let energyPredictor = ml5.neuralNetwork(options);</pre>
|
|||
<figcaption>Figure 10.20:<em> </em>A single mouse gesture as a vector between a start and end point</figcaption>
|
||||
</figure>
|
||||
<p>Each gesture could be recorded as a vector extending from the start to the end point of a mouse movement. The x- and y-components of the vector will be the model’s inputs. The model’s task could be to predict one of four possible labels for the gesture: <em>up</em>, <em>down</em>, <em>left</em>, or <em>right</em>. With a discrete set of possible outputs, this sounds like a classification problem. The four labels will be the model’s outputs.</p>
|
||||
<p>Much like some of the GA demonstrations in <a href="/genetic-algorithms#">Chapter 9</a>—and like the simple perceptron example earlier in this chapter—the problem I’m selecting here has a known solution and could be solved more easily and efficiently without a neural network. The direction of a vector can be classified with the <code>heading()</code> function and a series of <code>if</code> statements! However, by using this seemingly trivial scenario, I hope to explain the process of training a machine learning model in an understandable and friendly way. Additionally, this example will make it easy to check that the code is working as expected. When I’m done, I’ll provide some ideas about how to expand the classifier to a scenario that couldn’t use simple <code>if</code> statements.</p>
|
||||
<p>Much like some of the GA demonstrations in <a href="/genetic-algorithms#section-genetic-algorithms">Chapter 9</a>—and like the simple perceptron example earlier in this chapter—the problem I’m selecting here has a known solution and could be solved more easily and efficiently without a neural network. The direction of a vector can be classified with the <code>heading()</code> function and a series of <code>if</code> statements! However, by using this seemingly trivial scenario, I hope to explain the process of training a machine learning model in an understandable and friendly way. Additionally, this example will make it easy to check that the code is working as expected. When I’m done, I’ll provide some ideas about how to expand the classifier to a scenario that couldn’t use simple <code>if</code> statements.</p>
|
||||
<h3 id="collecting-and-preparing-the-data">Collecting and Preparing the Data</h3>
|
||||
<p>With the problem established, I can turn to steps 1 and 2: collecting and preparing the data. In the real world, these steps can be tedious, especially when the raw data you collect is messy and needs a lot of initial processing. You can think of this like having to organize, wash, and chop all your ingredients before you can start cooking a meal from scratch.</p>
|
||||
<p>For simplicity, I’d instead like to take the approach of ordering a machine learning “meal kit,” with the ingredients (data) already portioned and prepared. This way, I’ll get straight to the cooking itself, the process of training the model. After all, this is really just an appetizer for what will be the ultimate meal in <a href="/neuroevolution#">Chapter 11</a>, when I apply neural networks to steering agents.</p>
|
||||
<p>For simplicity, I’d instead like to take the approach of ordering a machine learning “meal kit,” with the ingredients (data) already portioned and prepared. This way, I’ll get straight to the cooking itself, the process of training the model. After all, this is really just an appetizer for what will be the ultimate meal in <a href="/neuroevolution#section-neuroevolution">Chapter 11</a>, when I apply neural networks to steering agents.</p>
|
||||
<p>With that in mind, I’ll handcode some example data and manually keep it normalized within a range of –1 and +1. I’ll organize the data into an array of objects, pairing the x- and y-components of a vector with a string label. I’m picking values that I feel clearly point in a specific direction and assigning the appropriate label—two examples per label:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">let data = [
|
||||
{ x: 0.99, y: 0.02, label: "right" },
|
||||
|
@ -814,7 +814,7 @@ classifier.train(options, finishedTraining);</pre>
|
|||
<p>The second argument to <code>train()</code> is optional, but it’s good to include one. It specifies a callback function that runs when the training process is complete—in this case, <code>finshedTraining()</code>. (See the “Callbacks” box for more on callback functions.) This is useful for knowing when you can proceed to the next steps in your code. Another optional callback, which I usually name <code>whileTraining()</code>, is triggered after each epoch. However, for my purposes, knowing when the training is done is plenty!</p>
|
||||
<div data-type="note">
|
||||
<h3 id="callbacks">Callbacks</h3>
|
||||
<p>A <strong>callback function</strong> in JavaScript is a function you don’t actually call yourself. Instead, you provide it as an argument to another function, intending for it to be <em>called back</em> automatically at a later time (typically associated with an event, like a mouse click). You’ve seen this before when working with Matter.js in <a href="/physics-libraries#">Chapter 6</a>, where you specified a function to call whenever a collision was detected.</p>
|
||||
<p>A <strong>callback function</strong> in JavaScript is a function you don’t actually call yourself. Instead, you provide it as an argument to another function, intending for it to be <em>called back</em> automatically at a later time (typically associated with an event, like a mouse click). You’ve seen this before when working with Matter.js in <a href="/physics-libraries#section-physics-libraries">Chapter 6</a>, where you specified a function to call whenever a collision was detected.</p>
|
||||
<p>Callbacks are needed for <strong>asynchronous</strong> operations, when you want your code to continue along with animating or doing other things while waiting for another task (like training a machine learning model) to finish. A classic example of this in p5.js is loading data into a sketch with <code>loadJSON()</code>.</p>
|
||||
<p>JavaScript also provides a more recent approach for handling asynchronous operations known as <strong>promises</strong>. With promises, you can use keywords like <code>async</code> and <code>await</code> to make your asynchronous code look more like traditional synchronous code. While ml5.js also supports this style, I’ll stick to using callbacks to stay aligned with p5.js style.</p>
|
||||
</div>
|
||||
|
@ -944,7 +944,7 @@ function gotResults(error, results) {
|
|||
<p>Incorporate machine learning into your ecosystem to enhance the behavior of creatures. How could classification or regression be applied?</p>
|
||||
<ul>
|
||||
<li>Can you classify the creatures of your ecosystem into multiple categories? What if you use an initial population as a training dataset, and as new creatures are born, the system classifies them according to their features? What are the inputs and outputs for your system?</li>
|
||||
<li>Can you use a regression to predict the life span of a creature based on its properties? Think about how size and speed affected the life span of the bloops from <a href="/genetic-algorithms#">Chapter 9</a>. Could you analyze how well the regression model’s predictions align with the actual outcomes?</li>
|
||||
<li>Can you use a regression to predict the life span of a creature based on its properties? Think about how size and speed affected the life span of the bloops from <a href="/genetic-algorithms#section-genetic-algorithms">Chapter 9</a>. Could you analyze how well the regression model’s predictions align with the actual outcomes?</li>
|
||||
</ul>
|
||||
<figure>
|
||||
<img src="images/10_nn/10_nn_25.png" alt="">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="chapter">
|
||||
<section data-type="chapter" id="section-neuroevolution">
|
||||
<h1 id="chapter-11-neuroevolution">Chapter 11. Neuroevolution</h1>
|
||||
<div class="chapter-opening-quote">
|
||||
<blockquote data-type="epigraph">
|
||||
|
@ -25,14 +25,14 @@
|
|||
<figcaption></figcaption>
|
||||
</figure>
|
||||
<p>Throughout this book, you’ve explored the fundamental principles of interactive physics simulations with p5.js, dived into the complexities of agent and other rule-based behaviors, and dipped your toe into the exciting realm of machine learning. You’ve become a natural!</p>
|
||||
<p>However, <a href="/neural-networks#">Chapter 10</a> merely scratched the surface of working with data and neural network–based machine learning—a vast landscape that would require countless sequels to this book to cover comprehensively. My goal was never to go deep into neural networks, but simply to establish the core concepts in preparation for a grand finale, where I find a way to integrate machine learning into the world of animated, interactive p5.js sketches and bring together as many of our new <em>Nature of Code</em> friends as possible for one last hurrah.</p>
|
||||
<p>The path forward passes through the field of <strong>neuroevolution</strong>, a style of machine learning that combines the GAs from <a href="/genetic-algorithms#">Chapter 9</a> with the neural networks from <a href="/neural-networks#">Chapter 10</a>. A neuroevolutionary system uses Darwinian principles to evolve the weights (and in some cases, the structure itself) of a neural network over generations of trial-and-error learning. In this chapter, I’ll demonstrate how to use neuroevolution with a familiar example from the world of gaming. I’ll then finish off by varying Craig Reynolds’s steering behaviors from <a href="/autonomous-agents#">Chapter 5</a> so that they are learned through neuroevolution.</p>
|
||||
<p>However, <a href="/neural-networks#section-neural-networks">Chapter 10</a> merely scratched the surface of working with data and neural network–based machine learning—a vast landscape that would require countless sequels to this book to cover comprehensively. My goal was never to go deep into neural networks, but simply to establish the core concepts in preparation for a grand finale, where I find a way to integrate machine learning into the world of animated, interactive p5.js sketches and bring together as many of our new <em>Nature of Code</em> friends as possible for one last hurrah.</p>
|
||||
<p>The path forward passes through the field of <strong>neuroevolution</strong>, a style of machine learning that combines the GAs from <a href="/genetic-algorithms#section-genetic-algorithms">Chapter 9</a> with the neural networks from <a href="/neural-networks#section-neural-networks">Chapter 10</a>. A neuroevolutionary system uses Darwinian principles to evolve the weights (and in some cases, the structure itself) of a neural network over generations of trial-and-error learning. In this chapter, I’ll demonstrate how to use neuroevolution with a familiar example from the world of gaming. I’ll then finish off by varying Craig Reynolds’s steering behaviors from <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a> so that they are learned through neuroevolution.</p>
|
||||
<h2 id="reinforcement-learning">Reinforcement Learning</h2>
|
||||
<p>Neuroevolution shares many similarities with another machine learning methodology that I briefly referenced in <a href="/neural-networks#">Chapter 10</a>, <strong>reinforcement learning</strong>, which incorporates machine learning into a simulated environment. A neural network–backed agent learns by interacting with the environment and receiving feedback about its decisions in the form of rewards or penalties. It’s a strategy built around observation.</p>
|
||||
<p>Neuroevolution shares many similarities with another machine learning methodology that I briefly referenced in <a href="/neural-networks#section-neural-networks">Chapter 10</a>, <strong>reinforcement learning</strong>, which incorporates machine learning into a simulated environment. A neural network–backed agent learns by interacting with the environment and receiving feedback about its decisions in the form of rewards or penalties. It’s a strategy built around observation.</p>
|
||||
<p>Think of a little mouse running through a maze. If it turns left, it gets a piece of cheese; if it turns right, it receives a little shock. (Don’t worry, this is just a pretend mouse.) Presumably, the mouse will learn over time to turn left. Its biological neural network makes a decision with an outcome (turn left or right) and observes its environment (yum or ouch). If the observation is negative, the network can adjust its weights in order to make a different decision the next time.</p>
|
||||
<p>In the real world, reinforcement learning is commonly used not for tormenting rodents but rather for developing robots. At time <em>t</em>, the robot performs a task and observes the results. Did it crash into a wall or fall off a table, or is it unharmed? As time goes on, the robot learns to interpret the signals from its environment in the optimal way to accomplish its tasks and avoid harm.</p>
|
||||
<p>Instead of a mouse or a robot, now think about any of the example objects from earlier in this book (walker, mover, particle, vehicle). Imagine embedding a neural network into one of these objects and using it to calculate a force or another action. The neural network could receive its inputs from the environment (such as distance to an obstacle) and output some kind of decision. Perhaps the network chooses from a set of discrete options (move left or right) or picks a set of continuous values (the magnitude and direction of a steering force).</p>
|
||||
<p>Is this starting to sound familiar? It’s no different from the way a neural network performed after training in the <a href="/neural-networks#">Chapter 10</a> examples, receiving inputs and predicting a classification or regression! Actually training one of these objects to make a good decision is where the reinforcement learning process diverges from the supervised learning approach. To better illustrate, let’s start with a hopefully easy-to-understand and possibly familiar scenario, the game <em>Flappy Bird</em> (see Figure 11.1).</p>
|
||||
<p>Is this starting to sound familiar? It’s no different from the way a neural network performed after training in the <a href="/neural-networks#section-neural-networks">Chapter 10</a> examples, receiving inputs and predicting a classification or regression! Actually training one of these objects to make a good decision is where the reinforcement learning process diverges from the supervised learning approach. To better illustrate, let’s start with a hopefully easy-to-understand and possibly familiar scenario, the game <em>Flappy Bird</em> (see Figure 11.1).</p>
|
||||
<p>The game is deceptively simple. You control a small bird that continually moves horizontally across the screen. With each tap or click, the bird flaps its wings and rises upward. The challenge? A series of vertical pipes spaced apart at irregular intervals emerge from the right. The pipes have gaps, and your primary objective is to navigate the bird safely through these gaps. If you hit a pipe, it’s game over. As you progress, the game’s speed increases, and the more pipes you navigate, the higher your score.</p>
|
||||
<figure>
|
||||
<img src="images/11_nn_ga/11_nn_ga_3.png" alt="Figure 11.1: The Flappy Bird game">
|
||||
|
@ -75,9 +75,9 @@
|
|||
task: "classification"
|
||||
};
|
||||
let birdBrain = ml5.neuralNetwork(options);</pre>
|
||||
<p>What next? If I were following the steps I laid out in <a href="/neural-networks#">Chapter 10</a>, I’d have to go back to steps 1 and 2 of the machine learning process: data collection and preparation. How exactly would that work here? One idea could be to scour the earth for the greatest <em>Flappy Bird</em> player of all time and record them playing for hours. I could log the input features for every moment of gameplay along with whether the player flapped or not. Feed all that data into the model, train it, and I can see the headlines already: “Artificial Intelligence Bot Defeats Flappy Bird.”</p>
|
||||
<p>What next? If I were following the steps I laid out in <a href="/neural-networks#section-neural-networks">Chapter 10</a>, I’d have to go back to steps 1 and 2 of the machine learning process: data collection and preparation. How exactly would that work here? One idea could be to scour the earth for the greatest <em>Flappy Bird</em> player of all time and record them playing for hours. I could log the input features for every moment of gameplay along with whether the player flapped or not. Feed all that data into the model, train it, and I can see the headlines already: “Artificial Intelligence Bot Defeats Flappy Bird.”</p>
|
||||
<p>But wait a second; has a computerized agent really learned to play <em>Flappy Bird</em> on its own, or has it simply learned to mirror the gameplay of a human? What if that human missed a key aspect of <em>Flappy Bird</em> strategy? The automated player would never discover it. Not to mention that collecting all that data would be incredibly tedious.</p>
|
||||
<p>The problem here is that I’ve reverted to a supervised learning scenario like the ones from <a href="/neural-networks#">Chapter 10</a>, but this is supposed to be a section about reinforcement learning. Unlike supervised learning, in which the correct answers are provided by a training dataset, the agent in reinforcement learning learns the answers—the optimal decisions—through trial and error by interacting with the environment and receiving feedback. In the case of <em>Flappy Bird</em>, the agent could receive a positive reward every time it successfully navigates a pipe, but a negative reward if it hits a pipe or the ground. The agent’s goal is to figure out which actions lead to the most cumulative rewards over time.</p>
|
||||
<p>The problem here is that I’ve reverted to a supervised learning scenario like the ones from <a href="/neural-networks#section-neural-networks">Chapter 10</a>, but this is supposed to be a section about reinforcement learning. Unlike supervised learning, in which the correct answers are provided by a training dataset, the agent in reinforcement learning learns the answers—the optimal decisions—through trial and error by interacting with the environment and receiving feedback. In the case of <em>Flappy Bird</em>, the agent could receive a positive reward every time it successfully navigates a pipe, but a negative reward if it hits a pipe or the ground. The agent’s goal is to figure out which actions lead to the most cumulative rewards over time.</p>
|
||||
<p>At the start, the <em>Flappy Bird</em> agent won’t know the best time to flap its wings, leading to many crashes. As it accrues more and more feedback from countless play-throughs, however, it will begin to refine its actions and develop the optimal strategy to navigate the pipes without crashing, maximizing its total reward. This process of <em>learning by doing</em> and optimizing based on feedback is the essence of reinforcement learning.</p>
|
||||
<p>As the chapter goes on, I’ll explore the principles I’m outlining here, but with a twist. Traditional techniques in reinforcement learning involve defining a strategy (called a <strong>policy</strong>) and a corresponding <strong>reward function</strong> to provide feedback for adjusting the policy. Instead of going down this road, however, I’m going to turn toward the star of this chapter, neuroevolution.</p>
|
||||
<h2 id="evolving-neural-networks-is-neat">Evolving Neural Networks Is NEAT!</h2>
|
||||
|
@ -276,8 +276,8 @@ function draw() {
|
|||
}
|
||||
}</pre>
|
||||
</div>
|
||||
<p>The neural network’s prediction is in the same format as the gesture classifier from <a href="/neural-networks#">Chapter 10</a>, and the decision can be made by checking the first element of the <code>results</code> array. If the output label is <code>"flap"</code>, then call <code>flap()</code>.</p>
|
||||
<p>Now that I’ve finished the <code>think()</code> method, the real challenge can begin: teaching the bird to win the game by consistently flapping its wings at the right moment. This is where the GA comes back into the picture. Recalling the discussion from <a href="/genetic-algorithms#">Chapter 9</a>, three key principles underpin Darwinian evolution: variation, selection, and heredity. I’ll revisit each of these principles in turn as I implement the steps of the GA in this new context of neural networks.</p>
|
||||
<p>The neural network’s prediction is in the same format as the gesture classifier from <a href="/neural-networks#section-neural-networks">Chapter 10</a>, and the decision can be made by checking the first element of the <code>results</code> array. If the output label is <code>"flap"</code>, then call <code>flap()</code>.</p>
|
||||
<p>Now that I’ve finished the <code>think()</code> method, the real challenge can begin: teaching the bird to win the game by consistently flapping its wings at the right moment. This is where the GA comes back into the picture. Recalling the discussion from <a href="/genetic-algorithms#section-genetic-algorithms">Chapter 9</a>, three key principles underpin Darwinian evolution: variation, selection, and heredity. I’ll revisit each of these principles in turn as I implement the steps of the GA in this new context of neural networks.</p>
|
||||
<h3 id="variation-a-flock-of-flappy-birds">Variation: A Flock of Flappy Birds</h3>
|
||||
<p>A single bird with a randomly initialized neural network isn’t likely to have any success at all. That lone bird will most likely jump incessantly and fly way off-screen, or sit perched at the bottom of the canvas awaiting collision after collision with the pipes. This erratic and nonsensical behavior is a reminder: a randomly initialized neural network lacks any knowledge or experience. The bird is essentially making wild guesses for its actions, so success is going to be rare.</p>
|
||||
<p>This is where the first key principle of GAs comes in: <strong>variation</strong>. The hope is that by introducing as many different neural network configurations as possible, a few might perform slightly better than the rest. The first step toward variation is to add an array of many birds (Figure 11.4).</p>
|
||||
|
@ -325,12 +325,12 @@ function draw() {
|
|||
//{!1} Is the bird alive or not?
|
||||
this.alive = true;
|
||||
}</pre>
|
||||
<p>I’ll assign the fitness a numeric value that increases by one every cycle through <code>draw()</code>, as long as the bird remains alive. The birds that survive longer should have a higher fitness value. This mechanism mirrors the reinforcement learning technique of rewarding good decisions. In reinforcement learning, however, an agent receives immediate feedback for every decision it makes, allowing it to adjust its policy accordingly. Here, the bird’s fitness is a cumulative measure of its overall success and will be applied only during the selection step of the GA:</p>
|
||||
<p>I’ll assign the fitness a numeric value that increases by one every cycle through <code>draw()</code>, as long as the bird remains alive. The birds that survive longer should have a higher fitness value. This mechanism mirrors the reinforcement learning technique of rewarding good decisions. In reinforcement learning, however, an agent receives immediate feedback for every decision it makes, allowing it to adjust its policy accordingly. Here, the bird’s fitness is a cumulative measure of its overall success and will be applied only during the selection step of the GA.</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> update() {
|
||||
//{!1} Increment the fitness each time through <code>update()</code>.
|
||||
this.fitness++;
|
||||
}</pre>
|
||||
<p>The <code>alive</code> property is a Boolean flag that’s initially set to <code>true</code>. When a bird collides with a pipe, this property is set to <code>false</code>. Only birds that are still alive are updated and drawn to the canvas:</p>
|
||||
<p>The <code>alive</code> property is a Boolean flag that’s initially set to <code>true</code>. When a bird collides with a pipe, this property is set to <code>false</code>. Only birds that are still alive are updated and drawn to the canvas.</p>
|
||||
<pre class="codesplit" data-code-language="javascript">function draw() {
|
||||
// There’s now an array of birds!
|
||||
for (let bird of birds) {
|
||||
|
@ -350,7 +350,7 @@ function draw() {
|
|||
}
|
||||
}
|
||||
}</pre>
|
||||
<p>In <a href="/genetic-algorithms#">Chapter 9</a>, I demonstrated two techniques for running an evolutionary simulation. In the smart rockets example, the population lived for a fixed amount of time each generation. The same approach could likely work here as well, but I want to allow the birds to accumulate the highest fitness value possible and not arbitrarily stop them based on a time limit. The second technique, demonstrated with the bloops example, eliminated the fitness score entirely and set a random probability for cloning any living creature. For <em>Flappy Bird</em>, this approach could become messy and risks overpopulation or all the birds dying out completely.</p>
|
||||
<p>In <a href="/genetic-algorithms#section-genetic-algorithms">Chapter 9</a>, I demonstrated two techniques for running an evolutionary simulation. In the smart rockets example, the population lived for a fixed amount of time each generation. The same approach could likely work here as well, but I want to allow the birds to accumulate the highest fitness value possible and not arbitrarily stop them based on a time limit. The second technique, demonstrated with the bloops example, eliminated the fitness score entirely and set a random probability for cloning any living creature. For <em>Flappy Bird</em>, this approach could become messy and risks overpopulation or all the birds dying out completely.</p>
|
||||
<p>I propose combining elements of both approaches. I’ll allow a generation to continue as long as at least one bird is still alive. When all the birds have died, I’ll select parents for the reproduction step and start anew. I’ll begin by writing a function to check whether all the birds have died:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">function allBirdsDead() {
|
||||
for (let bird of birds) {
|
||||
|
@ -389,7 +389,7 @@ function weightedSelection() {
|
|||
}</pre>
|
||||
<p>Once normalized, each bird’s fitness is equal to its probability of being selected.</p>
|
||||
<h3 id="heredity-baby-birds">Heredity: Baby Birds</h3>
|
||||
<p>Only one step is left in the GA—reproduction. In <a href="/genetic-algorithms#">Chapter 9</a>, I explored in great detail the two-step process for generating a child element: crossover and mutation. Crossover is where the third key principle of <strong>heredity</strong> arrives: the DNA from the two selected parents is combined to form the child’s DNA.</p>
|
||||
<p>Only one step is left in the GA—reproduction. In <a href="/genetic-algorithms#section-genetic-algorithms">Chapter 9</a>, I explored in great detail the two-step process for generating a child element: crossover and mutation. Crossover is where the third key principle of <strong>heredity</strong> arrives: the DNA from the two selected parents is combined to form the child’s DNA.</p>
|
||||
<p>At first glance, the idea of inventing a crossover algorithm for two neural networks might seem daunting, and yet it’s quite straightforward. Think of the individual “genes” of a bird’s brain as the weights within the neural network. Mixing two such brains boils down to creating a new neural network with each weight chosen by a virtual coin flip—the weight comes from either the first or the second parent:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">// Pick two parents and create a child with crossover.
|
||||
let parentA = weightedSelection();
|
||||
|
@ -459,15 +459,15 @@ function resetPipes() {
|
|||
<p>Note the addition of a new <code>resetPipes()</code> function. If I don’t remove the pipes before starting a new generation, the birds may immediately restart at a position colliding with a pipe, in which case even the best bird won’t have a chance to fly! The full online code for Example 11.2 also adjusts the behavior of the birds so that they die when they leave the canvas, either by crashing into the ground or soaring too high above the top.</p>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-112">Exercise 11.2</h3>
|
||||
<p>It takes a very long time for Example 11.2 to produce any results. Could you “speed up time” by skipping the drawing of every single frame of the game to reach an optimal bird faster? (A solution is presented in <a href="#speeding-up-time">“Speeding Up Time”</a>.) Additionally, could you add an overlay that displays information about the simulation’s status, such as the number of birds still in play, the current generation, and the life span of the best bird?</p>
|
||||
<p>It takes a very long time for Example 11.2 to produce any results. Could you “speed up time” by skipping the drawing of every single frame of the game to reach an optimal bird faster? (A solution is presented in <a href="#speeding-up-time" class="page-reference">“Speeding Up Time”</a>.) Additionally, could you add an overlay that displays information about the simulation’s status, such as the number of birds still in play, the current generation, and the life span of the best bird?</p>
|
||||
</div>
|
||||
<div data-type="exercise">
|
||||
<h3 id="exercise-113">Exercise 11.3</h3>
|
||||
<p>To avoid starting the neuroevolution process from scratch every time, try using ml5.js’s neural network <code>save()</code> and <code>load()</code> methods. How might you add a feature that saves the best bird model as well as an option to load a previously saved model?</p>
|
||||
</div>
|
||||
<h2 id="steering-the-neuroevolutionary-way">Steering the Neuroevolutionary Way</h2>
|
||||
<p>Having explored neuroevolution with <em>Flappy Bird</em>, I’d like to shift the focus back to the realm of simulation, specifically the steering agents introduced in <a href="/autonomous-agents#">Chapter 5</a>. What if, instead of me dictating the rules for an algorithm to calculate a steering force, a simulated creature could evolve its own strategy? Drawing inspiration from Reynolds’s aim of lifelike and improvisational behaviors, my goal isn’t to use neuroevolution to engineer the perfect creature that can flawlessly execute a task. Instead, I hope to create a captivating world of simulated life, where the quirks, nuances, and happy accidents of evolution unfold in the canvas.</p>
|
||||
<p>I’ll begin by adapting the smart rockets example from <a href="/genetic-algorithms#">Chapter 9</a>. In that example, the genes for each rocket were an array of vectors:</p>
|
||||
<p>Having explored neuroevolution with <em>Flappy Bird</em>, I’d like to shift the focus back to the realm of simulation, specifically the steering agents introduced in <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a>. What if, instead of me dictating the rules for an algorithm to calculate a steering force, a simulated creature could evolve its own strategy? Drawing inspiration from Reynolds’s aim of lifelike and improvisational behaviors, my goal isn’t to use neuroevolution to engineer the perfect creature that can flawlessly execute a task. Instead, I hope to create a captivating world of simulated life, where the quirks, nuances, and happy accidents of evolution unfold in the canvas.</p>
|
||||
<p>I’ll begin by adapting the smart rockets example from <a href="/genetic-algorithms#section-genetic-algorithms">Chapter 9</a>. In that example, the genes for each rocket were an array of vectors:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">this.genes = [];
|
||||
for (let i = 0; i < lifeSpan; i++) {
|
||||
//{!2} Each gene is a vector with random direction and magnitude.
|
||||
|
@ -498,7 +498,7 @@ for (let i = 0; i < lifeSpan; i++) {
|
|||
}</pre>
|
||||
<p>The neural network brain outputs two values: one for the angle of the vector and one for the magnitude. You might think to instead use these outputs for the vector’s x- and y-components. The default output range for an ml5.js neural network is from 0 to 1, however, and I want the forces to be capable of pointing in both positive and negative directions. Mapping the first output to an angle by multiplying it by <code>TWO_PI</code> offers the full range.</p>
|
||||
<p>You may have noticed that the code includes a variable called <code>inputs</code> that I have yet to declare or initialize. Defining the inputs to the neural network is where you, as the designer of the system, can be the most creative. You have to consider the nature of the environment and the simulated biology and capabilities of your creatures, and then decide which features are most important.</p>
|
||||
<p>As a first try, I’ll assign something basic for the inputs and see if it works. Since the smart rockets’ environment is static, with fixed obstacles and targets, what if the brain could learn and estimate a flow field to navigate toward its goal? As I demonstrated in <a href="/autonomous-agents#">Chapter 5</a>, a flow field receives a position and returns a vector, so the neural network can mirror this functionality and use the rocket’s current x- and y-position as input. I just have to normalize the values according to the canvas dimensions:</p>
|
||||
<p>As a first try, I’ll assign something basic for the inputs and see if it works. Since the smart rockets’ environment is static, with fixed obstacles and targets, what if the brain could learn and estimate a flow field to navigate toward its goal? As I demonstrated in <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a>, a flow field receives a position and returns a vector, so the neural network can mirror this functionality and use the rocket’s current x- and y-position as input. I just have to normalize the values according to the canvas dimensions:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">let inputs = [this.position.x / width, this.position.y / height];</pre>
|
||||
<p>That’s it! Virtually everything else from the original example can remain unchanged: the population, the fitness function, and the selection process.</p>
|
||||
<div data-type="example">
|
||||
|
@ -557,7 +557,7 @@ for (let i = 0; i < lifeSpan; i++) {
|
|||
circle(this.position.x, this.position.y, this.r * 2);
|
||||
}
|
||||
}</pre>
|
||||
<p>As the glow moves, the creature should take the glow’s position into account in its decision-making process, as an input to its brain. However, it isn’t sufficient to know only the light’s position; it’s the position relative to the creature’s own that’s key. A nice way to synthesize this information as an input feature is to calculate a vector that points from the creature to the glow. Essentially, I’m reinventing the <code>seek()</code> method from <a href="/autonomous-agents#">Chapter 5</a>, using a neural network to estimate the steering force:</p>
|
||||
<p>As the glow moves, the creature should take the glow’s position into account in its decision-making process, as an input to its brain. However, it isn’t sufficient to know only the light’s position; it’s the position relative to the creature’s own that’s key. A nice way to synthesize this information as an input feature is to calculate a vector that points from the creature to the glow. Essentially, I’m reinventing the <code>seek()</code> method from <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a>, using a neural network to estimate the steering force:</p>
|
||||
<div class="snip-below">
|
||||
<pre class="codesplit" data-code-language="javascript"> seek(target) {
|
||||
//{!1} Calculate a vector from the position to the target.
|
||||
|
@ -671,8 +671,8 @@ function draw() {
|
|||
}</pre>
|
||||
<p>It’s hard to believe, but this book has been a journey well over 10 years in the making. Thank you, dear reader, for sticking with it. I promise it’s not an infinite loop. However meandering it might have seemed, like a random walk, I’m finally using an arrival steering behavior to reach the final piece of the puzzle, an attempt to bring together all my past explorations in my own version of the Ecosystem Project.</p>
|
||||
<h2 id="a-neuroevolutionary-ecosystem">A Neuroevolutionary Ecosystem</h2>
|
||||
<p>A few elements in this chapter’s examples don’t quite fit with my dream of simulating a natural ecosystem. The first goes back to an issue I raised in <a href="/genetic-algorithms#">Chapter 9</a> with the introduction of the bloops. A system of creatures that all live and die together, starting completely over with each subsequent generation—that isn’t how the biological world works! I’d like to revisit this dilemma in this chapter’s context of neuroevolution.</p>
|
||||
<p>Second, and perhaps more important, a major flaw exists in the way I’m extracting features from a scene to train a model. The creatures in Example 11.4 are all-knowing. Sure, it’s reasonable to assume that a creature is aware of its own current velocity, but I’ve also allowed each creature to know the glow’s exact location, regardless of how far away it is or what might be blocking the creature’s vision or senses. This is a bridge too far. It flies in the face of one of the main tenets of autonomous agents I introduced in <a href="/autonomous-agents#">Chapter 5</a>: an agent should have a <em>limited</em> ability to perceive its environment.</p>
|
||||
<p>A few elements in this chapter’s examples don’t quite fit with my dream of simulating a natural ecosystem. The first goes back to an issue I raised in <a href="/genetic-algorithms#section-genetic-algorithms">Chapter 9</a> with the introduction of the bloops. A system of creatures that all live and die together, starting completely over with each subsequent generation—that isn’t how the biological world works! I’d like to revisit this dilemma in this chapter’s context of neuroevolution.</p>
|
||||
<p>Second, and perhaps more important, a major flaw exists in the way I’m extracting features from a scene to train a model. The creatures in Example 11.4 are all-knowing. Sure, it’s reasonable to assume that a creature is aware of its own current velocity, but I’ve also allowed each creature to know the glow’s exact location, regardless of how far away it is or what might be blocking the creature’s vision or senses. This is a bridge too far. It flies in the face of one of the main tenets of autonomous agents I introduced in <a href="/autonomous-agents#section-autonomous-agents">Chapter 5</a>: an agent should have a <em>limited</em> ability to perceive its environment.</p>
|
||||
<h3 id="sensing-the-environment">Sensing the Environment</h3>
|
||||
<p>A common approach to simulating how a real-world creature (or robot) would have a limited awareness of its surroundings is to attach <strong>sensors</strong> to an agent. Think back to that mouse in the maze from the beginning of the chapter (hopefully it’s been thriving on the cheese it’s been getting as a reward), and now imagine it has to navigate the maze in the dark. Its whiskers might act as proximity sensors to detect walls and turns. The mouse whiskers can’t see the entire maze, but only sense the immediate surroundings. Another example of sensors is a bat using echolocation to navigate, or a car on a winding road where the driver can see only what’s projected in front of the car’s headlights.</p>
|
||||
<p>I’d like to build on this idea of the whiskers (or more formally the <em>vibrissae</em>) found in mice, cats, and other mammals. In the real world, animals use their vibrissae to navigate and detect nearby objects, especially in dark or obscured environments (see Figure 11.5). How can I attach whisker-like sensors to my neuroevolutionary-seeking creatures?</p>
|
||||
|
@ -680,7 +680,7 @@ function draw() {
|
|||
<img src="images/11_nn_ga/11_nn_ga_7.png" alt="Figure 11.5: Clawdius the cat sensing his environment with his vibrissae">
|
||||
<figcaption>Figure 11.5: Clawdius the cat sensing his environment with his vibrissae</figcaption>
|
||||
</figure>
|
||||
<p>I’ll keep the generic class name <code>Creature</code> but think of them now as the amoeba-like bloops from <a href="/genetic-algorithms#">Chapter 9</a>, enhanced with whisker-like sensors that emanate from their center in all directions:</p>
|
||||
<p>I’ll keep the generic class name <code>Creature</code> but think of them now as the amoeba-like bloops from <a href="/genetic-algorithms#section-genetic-algorithms">Chapter 9</a>, enhanced with whisker-like sensors that emanate from their center in all directions:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">class Creature {
|
||||
constructor(x, y) {
|
||||
// The creature has a position and radius.
|
||||
|
@ -832,7 +832,7 @@ class Creature {
|
|||
force.setMag(magnitude);
|
||||
this.applyForce(force);
|
||||
}</pre>
|
||||
<p>The logical next step might be to incorporate all the usual parts of the GA, writing a fitness function (how much food did each creature eat?) and performing selection after a fixed generational time period. But this is a great opportunity to revisit the principles of a continuous ecosystem and aim for a more sophisticated environment and set of potential behaviors for the creatures themselves. Instead of a fixed life span cycle for each generation, I’ll bring back <a href="/genetic-algorithms#">Chapter 9</a>’s <code>health</code> score for each creature. For every cycle through <code>draw()</code> that a creature lives, its health deteriorates a little bit:</p>
|
||||
<p>The logical next step might be to incorporate all the usual parts of the GA, writing a fitness function (how much food did each creature eat?) and performing selection after a fixed generational time period. But this is a great opportunity to revisit the principles of a continuous ecosystem and aim for a more sophisticated environment and set of potential behaviors for the creatures themselves. Instead of a fixed life span cycle for each generation, I’ll bring back <a href="/genetic-algorithms#section-genetic-algorithms">Chapter 9</a>’s <code>health</code> score for each creature. For every cycle through <code>draw()</code> that a creature lives, its health deteriorates a little bit:</p>
|
||||
<div class="snip-below">
|
||||
<pre class="codesplit" data-code-language="javascript">class Creature {
|
||||
constructor() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="page">
|
||||
<section data-type="page" id="section-appendix-creature">
|
||||
<h1 id="appendix-creature-design">Appendix: Creature Design</h1>
|
||||
<p><em>This guide is by Zannah Marsh, who created all the illustrations you see in this book.</em></p>
|
||||
<p>If you aren’t sure how to start the creature design task for your Ecosystem Project, or if the thought of populating a multi-creature ecosystem feels daunting, don’t worry! You can start developing creatures by using a few visual building blocks, like basic shapes and lines, and reuse them for various results. This design task is similar to programming by reusing and repurposing code.</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section data-type="page">
|
||||
<section data-type="page" id="section-image-credits">
|
||||
<h1 id="image-credits">Image Credits</h1>
|
||||
<p>All emojis in the book are from OpenMoji, the open source emoji and icon project, and licensed under CC BY-SA 4.0.</p>
|
||||
<p><strong>Chapter 0:</strong> Pages 314–315 from <em>A Million Random Digits with 100,000 Normal Deviates</em>, RAND Corporation, MR-1418-RC, 2001. As of October 17, 2023: <a href="https://www.rand.org/pubs/monograph_reports/MR1418.html"><em>https://www.rand.org/pubs/monograph_reports/MR1418.html</em></a>.</p>
|
||||
|
|
|
@ -90,7 +90,7 @@ a {
|
|||
|
||||
// Internal links
|
||||
// for the ones include '#' but not at the end
|
||||
&:not([href^='http'])[href*='#']:not([href$='#'])::after {
|
||||
&.page-reference:not([href^='http'])[href*='#']:not([href$='#'])::after {
|
||||
content: ' on page '
|
||||
prince-script(
|
||||
format-number,
|
||||
|
|
Loading…
Reference in a new issue