noc-book-2/content/04_particles.html
2022-11-28 16:31:26 +00:00

1213 lines
No EOL
74 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

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

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

<section data-type="chapter">
<h1 id="chapter-4-particle-systems">Chapter 4. Particle Systems</h1>
<blockquote data-type="epigraph">
<p>“That is wise. Were I to invoke logic, however, logic clearly dictates that the needs of the many outweigh the needs of the few.”</p>
<p>— Spock</p>
</blockquote><a data-type="indexterm" data-primary="Lucasfilm Ltd."></a><a data-type="indexterm" data-primary="particle systems"></a><a data-type="indexterm" data-primary="Reeves" data-secondary="William T."></a><a data-type="indexterm" data-primary="Star Trek II: The Wrath of Khan (1982)"></a>
<p>In 1982, William T. Reeves, a researcher at Lucasfilm Ltd., was working on the film <em>Star Trek II: The Wrath of Khan</em>. Much of the movie revolves around the Genesis Device, a torpedo that when shot at a barren, lifeless planet has the ability to reorganize matter and create a habitable world for colonization. During the sequence, a wall of fire ripples over the planet while it is being “terraformed.” The term <strong><em>particle system</em></strong>, an incredibly common and useful technique in computer graphics, was coined in the creation of this particular effect.</p>
<blockquote data-type="epigraph">
<p>“A particle system is a collection of many many minute particles that together represent a fuzzy object. Over a period of time, particles are generated into a system, move and change from within the system, and die from the system.”</p>
<p>—William Reeves, "Particle Systems—A Technique for Modeling a Class of Fuzzy Objects," ACM Transactions on Graphics 2:2 (April 1983), 92.</p>
</blockquote>
<p>Since the early 1980s, particle systems have been used in countless video games, animations, digital art pieces, and installations to model various irregular types of natural phenomena, such as fire, smoke, waterfalls, fog, grass, bubbles, and so on.</p>
<p>This chapter is dedicated to looking at implementation strategies for coding a particle system. How do you organize your code? Where do you store information related to individual particles versus information related to the system as a whole? The examples Ill cover will focus on managing the data associated with a particle system. Theyll use simple shapes for the particles and apply only the most basic behaviors (such as gravity). However, by building on this framework and adding more interesting ways to render the particles and compute behaviors, you can achieve a variety of effects.</p>
<h2 id="41-why-you-need-particle-systems">4.1 Why You Need Particle Systems</h2><a data-type="indexterm" data-primary="particle systems" data-secondary="purpose of"></a>
<p>Ive defined a particle system to be a collection of independent objects, often represented by a simple shape or dot. Why does this matter? Certainly, the prospect of modeling some of the phenomena listed (explosions!) is attractive and potentially useful. But really, theres an even better reason to explore particle systems. If you want to get anywhere in this nature of code life, youre likely to find yourself developing systems of <em>many</em> thingsballs bouncing, birds flocking, ecosystems evolving, all sorts of things in plural.</p>
<p>Just about every chapter after this one is going to deal with a list of objects. Yes, Ive dipped my toe in the array waters in some of the first vector and forces examples. But now its time to go where no array has gone before.</p>
<p>First, Im going to want to deal with flexible quantities of elements. Some examples will have zero things, sometimes one thing, sometimes ten things, and sometimes ten thousand things. Second, Im going to want to take a more sophisticated object-oriented approach. Instead of writing a class to describe a single particle, Im also going to want to write a class that describes the collection of particles—the particle system itself. The goal here is to be able to write a sketch that looks like the following:</p>
<pre class="codesplit" data-code-language="javascript">// Ah, isnt this main program so simple and lovely?
let system;
function setup() {
createCanvas(640, 360);
system = new ParticleSystem();
}
function draw() {
background(255);
system.run();
}</pre>
<p>No single particle is ever referenced in the above code, yet the result will be full of particles flying all over the screen. Getting used to writing p5.js sketches with multiple classes, and classes that keep lists of instances of other classes, will prove very useful as you get to later chapters in this book.</p><a data-type="indexterm" data-primary="inheritance"></a><a data-type="indexterm" data-primary="object-oriented programming" data-secondary="inheritance"></a><a data-type="indexterm" data-primary="object-oriented programming" data-secondary="polymorphism"></a><a data-type="indexterm" data-primary="polymorphism"></a>
<p>Finally, working with particle systems is also a good excuse to tackle two other object-oriented programming techniques: inheritance and polymorphism. With the examples youve seen up until now, Ive always used an array of a single type of object, like "movers" or “oscillators.” With inheritance (and polymorphism), Ill demonstrate a convenient way to store a single list containing objects of different types. This way, a particle system need not only be a system of a single type of particle.</p>
<p>Though it may seem obvious to you, Id also like to point out that my examples are modeled after conventional implementations of particle systems, and thats where I will begin in this chapter. However, the fact that the particles in this chapter look or behave a certain way should not limit your imagination. Just because particle systems tend to look sparkly, fly forward, and fall with gravity doesnt mean that those are the characteristics yours should have.</p>
<p>The focus here is on how to keep track of a system of many elements. What those elements do and how those elements look is up to you.</p>
<h2 id="42-a-single-particle">4.2 A Single Particle</h2><a data-type="indexterm" data-primary="particle systems" data-secondary="particles in"></a><a data-type="indexterm" data-primary="particles"></a>
<p>Before I can get rolling on coding the system itself, I need to write the class to describe a single particle. The good news: Ive done this already! The <code>Mover</code> class from Chapter 2 serves as the perfect template. A particle is an independent body that moves about the canvas. It has <code>position</code>, <code>velocity</code>, and <code>acceleration</code>, a constructor to initialize those variables, and functions to <code>display()</code> itself and <code>update()</code> its position.</p>
<pre class="codesplit" data-code-language="javascript">class Particle {
//{!5} A “Particle” object is just another name for our “Mover.” It has position, velocity, and acceleration.
Particle(x, y) {
this.position = createVector(x, y);
this.acceleration = createVector();
this.velocity = createVector();
}
update() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
}
display() {
stroke(0);
fill(175);
circle(this.position.x, this.position.y, 8);
}
}</pre>
<p>This is about as simple as a particle can get. From here, I could take the particle in several directions. I could add the <code>applyForce()</code> function to affect the particles behavior (Ill do precisely this in a future example). I could also add variables to describe color and shape, or load a <code>p5.Image</code> to draw the particle. For now, however, Ill focus on adding just one additional detail: <strong><em>lifespan</em></strong>.</p><a data-type="indexterm" data-primary="emitter"></a><a data-type="indexterm" data-primary="particle systems" data-secondary="emitter"></a><a data-type="indexterm" data-primary="particle systems" data-secondary="lifespan of particles"></a><a data-type="indexterm" data-primary="particles" data-secondary="lifespan of"></a>
<p>Some particle systems involve something called an <strong><em>emitter</em></strong>. The emitter is the source of the particles and controls the initial settings for the particles: position, velocity, and more. An emitter might emit a single burst of particles, or a continuous stream of particles, or both. The new feature here is that a particle born at the emitter does not live forever. If it were to live forever, the p5.js sketch would eventually grind to a halt as the amount of particles increases to an unwieldy number over time. As new particles are born, old particles need to be removed. This creates the illusion of an infinite stream of particles, and the performance of the sketch does not suffer. There are many different ways to decide when a particle is ready to be removed. For example, it could come into contact with another object, or it could leave the canvas. For this first <code>Particle</code> class, Ill choose to add a <code>lifespan</code> variable that acts like a countdown timer. The timer will start at 255 and count down to 0, when the particle will be considered “dead.” The code for this in the <code>Particle</code> class as:</p>
<pre class="codesplit" data-code-language="javascript">class Particle {
constructor(x, y) {
this.position = createVector(x, y);
this.acceleration = createVector();
this.velocity = createVector();
//{!1 .bold} A new variable to keep track of how long the particle has been “alive”. We start at 255 and count down for convenience.
this.lifespan = 255;
}
update() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
//{!1 .bold} Lifespan decreases
this.lifespan -= 2.0;
}
display() {
//{!2 .bold} Since the life ranges from 255 to 0 it can be used also for alpha
stroke(0, this.lifespan);
fill(175, this.lifespan);
circle(this.position.x, this.position.y, 8);
}
}</pre>
<p>The reason I chose to start the lifespan at 255 and count down to 0 is for convenience. With those values, I can assign <code>lifespan</code> to act as the alpha transparency for the circle as well. When the particle is “dead” it will also have faded away.</p><a data-type="indexterm" data-primary="particle systems" data-secondary="dead particles" data-tertiary="checking for"></a><a data-type="indexterm" data-primary="particles" data-secondary="death" data-tertiary="checking for"></a>
<p>With the addition of the <code>lifespan</code> property, Ill also need one additional function—a function that can be queried (for a true or false answer) as to whether the particle is alive or dead. This will come in handy when writing the <code>ParticleSystem</code> class, whose task will be to manage the list of particles themselves. Writing this function is pretty easy; I just need to check and see if the value of <code>lifespan</code> is less than 0. If it is <code>return true</code>, if not <code>return false</code>.</p>
<pre class="codesplit" data-code-language="javascript"> isDead() {
//{!5} Is the particle still alive?
if (this.lifespan &#x3C; 0.0) {
return true;
} else {
return false;
}
}</pre>
<p>Or more simply, I can return the result of the boolean expression itself!</p>
<pre class="codesplit" data-code-language="javascript"> isDead() {
//{!1} Is the particle still alive?
return (this.lifespan &#x3C; 0.0);
}</pre><a data-type="indexterm" data-primary="particles" data-secondary="testing"></a>
<p>Before I get to the next step of making many particles, its worth taking a moment to make sure the particle works correctly and create a sketch with one single <code>Particle</code> object. Here is the full code below, with one small additiongiving the particle a random initial velocity as well as adding <code>applyForce()</code> (to simulate gravity).</p>
<div data-type="example">
<h3 id="example-41-a-single-particle">Example 4.1: A single particle</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/B1GoIg-ug"></div>
<figcaption></figcaption>
</figure>
<figure>
<img src="images/04_particles/04_particles_1.png" alt="">
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">let particle;
function setup() {
createCanvas(640, 360);
particle = new Particle(width / 2, 10);
}
function draw() {
background(255);
//{!2} Operating the single Particle
particle.update();
particle.display();
//{!2} Applying a gravity force
let gravity = createVector(0, 0.1);
particle.applyForce(gravity);
//{!3} Checking the particle's state
if (p.isDead()) {
print("Particle dead!");
}
}
class Particle {
constructor(x,y) {
this.position = createVector(x, y);
//{!1 .offset-top} For demonstration purposes the Particle has a random velocity.
this.velocity = createVector(random(-1, 1), random(-2, 0));
this.acceleration = createVector(0, 0);
this.lifespan = 255.0;
}
update() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.lifespan -= 2.0;
}
display() {
stroke(0, this.lifespan);
fill(0, this.lifespan);
circle(this.position.x, this.position.y, 8);
}
//{!3} Keeping the same physics model as with previous chapters
applyForce(force) {
this.acceleration.add(force);
}
//{!3} Is the Particle alive or dead?
isDead() {
return (this.lifespan &#x3C; 0.0);
}
}</pre>
<div data-type="exercise">
<h3 id="exercise-41">Exercise 4.1</h3>
<p>Create a <code>run()</code> function in the <code>Particle</code> class that handles <code>update()</code>, <code>display()</code>, and <code>applyForce()</code>. What are the pros and cons of this approach?</p>
</div>
<div data-type="exercise">
<h3 id="exercise-42">Exercise 4.2</h3>
<p>Add angular velocity (rotation) to the particle. Create your own non-circle particle design.</p>
</div>
<p>Armed with a class to describe a single particle, Im ready for the next big step. How do you keep track of many particles, not knowing exactly how many particles you might have at any given time?</p>
<h2 id="43-the-array">4.3 The Array</h2><a data-type="indexterm" data-primary="ArrayList class (Java)"></a><a data-type="indexterm" data-primary="contract() function (p5)"></a><a data-type="indexterm" data-primary="expand() function (p5)"></a><a data-type="indexterm" data-primary="particle systems" data-secondary="ArrayList" data-tertiary="using"></a><a data-type="indexterm" data-primary="splice() function (p5)"></a><a data-type="indexterm" data-primary="subset() function (p5)"></a>
<p>Thankfully, the wonderful JavaScript <code>Array</code> has all the functionality we need for managing a list of <code>Particle</code> objects. The the built-in JavaScript functions available in the JavaScript class <code>Array</code> will allow me to add and remove particles and manipulate the arrays in all sorts of powerful ways. Although there are some cons to this approach, in order to keep the subsequent code examples more concise, I'm using a solution to Exercise 4.1 and assume a <code>run()</code> method that handles all of the particle's functionality. <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array">JavaScript Array Documentation</a>.</p>
<pre class="codesplit" data-code-language="javascript">let total = 10;
//{!1} Starting with an empty array
let particles = [];
function setup() {
//{!3} This is what youre probably used to, accessing elements on the array via an index and brackets—[].
for (let i = 0; i &#x3C; total; i++) {
particles[i] = new Particle(width/2, height/2);
}
}
function draw() {
for (let i = 0; i &#x3C; particles.length; i++) {
let particle = particles[i];
particle.run();
}
}</pre><a data-type="indexterm" data-primary="ArrayList class (Java)" data-secondary="generics" data-tertiary="using"></a><a data-type="indexterm" data-primary="for loops" data-secondary="ArrayList objects and"></a>
<p>This last <code>for</code> loop demonstrates how to call functions on every element of an array by accessing each index. I initialize a variable <code>i</code> with value 0 and count up by 1, accessing each element of the array until I reach the end. However, this is a good time to mention the JavaScript "<code>for of</code> loop”, which is a bit more concise. The "for of" loop works with arrays as follows:</p><a data-type="indexterm" data-primary="for of loops" data-secondary="enhanced"></a>
<pre class="codesplit" data-code-language="javascript">function draw() {
for (let particle of particles) {
particle.run();
}
}
</pre>
<p>Lets translate that. Say “each” instead of “let” and “in” instead of “of”:</p>
<p>“For each particle in particles, update and display that particle!”</p>
<p>I know. You cannot contain your excitement. I cant. I know its not necessary, but I just have to type that again.</p>
<pre class="codesplit" data-code-language="javascript">for (let particle of particles) {
particle.run();
}</pre>
<p>Simple, elegant, concise, lovely. Take a moment. Breathe. I have some bad news. Yes, I may love that "for of" loop and I will get to use it in examples. But not just yet.</p><a data-type="indexterm" data-primary="ArrayList class (Java)" data-secondary="resizability of"></a>
<p>The code Ive written above doesnt take advantage of the JavaScript's ability to remove elements from an array. I need to design an example that fits with the particle system scenario, where a continuous stream of particles are emitted, adding one new particle with each cycle through <code>draw()</code>. Ill skip rehashing the <code>Particle</code> class code here, as it doesnt need to change. What we have so far is:</p>
<pre class="codesplit" data-code-language="javascript">let particles = [];
function setup() {
createCanvas(640, 360);
}
function draw() {
background(255);
//{!1 .offset-top} A new Particle object is added to the array every cycle through draw().
particles.push(new Particle(width/2, 50));
for (let particle of particles) {
particle.run();
}
}</pre>
<p>Run the above code for a few minutes and youll start to see the frame rate slow down further and further until the program grinds to a halt (my tests yielded horrific performance after fifteen minutes). The issue of course is that I am adding more and more particles without removing any.</p><a data-type="indexterm" data-primary="remove() function (ArrayList class)"></a>
<p>Fortunately, particles can be removed from the array referencing the index position of the particle to be removed. This is why I cannot use the enhanced <code>for of</code> loop; this loop provides no means for deleting elements while iterating. Instead, I can use the Array <code>splice()</code> method. (Yes, an array in JavaScript is actually an object created from the class <code>Array</code> with many methods!). The <code>splice()</code> method removes one or more elements from an array starting from a given index.</p>
<pre class="codesplit" data-code-language="javascript"> for (let i = 0; i &#x3C; particles.length; i++) {
let particle = particles[i];
particle.run();
if (particle.isDead()) {
//{!1} Remove one particle at index i
particles.splice(i, 1);
}
}</pre>
<p>Although the above code will run just fine (and the program will never grind to a halt), I have opened up a medium-sized can of worms. Whenever you manipulate the contents of an array while iterating through that very array, you can get into trouble. Take, for example, the following code.</p>
<pre class="codesplit" data-code-language="javascript"> for (let i = 0; i &#x3C; particles.length; i++) {
let particle = particles[i];
particle.run();
//{!1 .offset-top} Adding a new Particle to the list while iterating?
particles.push(new Particle(width / 2, 50));
}</pre><a data-type="indexterm" data-primary="size() function (ArrayList class)"></a>
<p>This is a somewhat extreme example (with flawed logic), but it proves the point. In the above case, for each particle in the array, I add a new particle to that array (and so the <code>length</code> of the array increases). This results in an infinite loop, as <code>i</code> will never increment past the size of the array.</p><a data-type="indexterm" data-primary="iterating" data-secondary="removing elements in for loops"></a>
<p>While removing elements from the array during a loop doesnt cause the program to crash (as it does with adding), the problem is almost more insidious in that it leaves no evidence. To discover the flaw I must first establish an important fact. When an object is removed from the array with <code>splice()</code>, all elements are shifted one spot to the left. Note the diagram below where particle C (index 2) is removed. particles A and B keep the same index, while particles D and E shift from 3 and 4 to 2 and 3, respectively.</p>
<figure>
<img src="images/04_particles/04_particles_2.png" alt="Figure 4.1">
<figcaption>Figure 4.1</figcaption>
</figure>
<p>Lets pretend we are <code>i</code> looping through the <code>array</code>.</p>
<div data-type="equation">when~i = 0 → Check~particle~A → Do~not~delete</div>
<div data-type="equation">when~i = 1 → Check~particle~B → Do~not~delete</div>
<div data-type="equation">when~i = 2 → Check~particle~C → Delete!</div>
<div data-type="equation">Slide~particals~D~and~E~back~from~slots~3~and~4~to~2~and~3</div>
<div data-type="equation">when~i = 3 → Check~particle~E → Do~not~delete</div>
<p>Notice the problem? Particle D was never checked! When C was deleted from slot #2, D moved into slot #2, but <code>i</code> has already moved on to slot #3. This is not a total disaster, since particle D will get checked the next time around. Still, the expectation is that the code should iterate through every single element of the array. Skipping one is unacceptable!</p>
<p>There are two solutions to this problem. The first solution is to iterate through the array backwards. If you are sliding elements from right to left as elements are removed, its impossible to skip an element. Heres how the code looks:</p>
<pre class="codesplit" data-code-language="javascript"> //{!1 .bold} Looping through the list backwards
for (let i = particles.length - 1; i >= 0; i--) {
let particle = particles[i];
particle.run();
if (particle.isDead()) {
particles.splice(i, 1);
}
}</pre>
<p>A second solution is to use something known as a “higher-order” function. A higher-order function is one that receives another function as an argument (or returns a function). In the case of JavaScript arrays there are many higher-order functions. A common one is <code>sort()</code> which takes as its argument a function that defines how to compare two elements of the array (therefore sorting the array according to that comparison.) Here, I can make use of the higher order function <code>filter()</code>. <code>filter()</code> checks each item in the specified array and keeps only the item(s) where the given condition is true (removing those that return false).</p>
<pre class="codesplit" data-code-language="javascript"> particles = particles.filter(function(particle) {
//{!1} Keep particles that are not dead!
return !particle.isDead();
});</pre>
<p>This is more commonly written using JavaScript's arrow notation. (To learn more, you can watch this <a href="https://youtu.be/H4awPsyugS0">higher-order functions and arrow notation video tutorial</a>.)</p>
<pre class="codesplit" data-code-language="javascript"> particles = particles.filter(particle => !particle.isDead());</pre>
<p>For the purposes of this book, I am going to stick with the <code>splice()</code> method, but I encourage you to explore writing your code with higher-order functions and arrow notation.</p>
<div data-type="example">
<h3 id="example-42-array-of-particles">Example 4.2: Array of particles</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/B1tgPxW_e"></div>
<figcaption></figcaption>
</figure>
<figure>
<img src="images/04_particles/04_particles_3.png" alt="">
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">let particles = [];
function setup() {
createCanvas(640, 360);
}
function draw() {
background(255);
particles.push(new Particle(width / 2, 50);
//{!1} Looping through the array backwards for deletion
for (let i = particles.length - 1; i >= 0; i--) {
let particle = particles[i];
particle.run();
if (particle.isDead()) {
particles.splice(i, 1);
}
}
}</pre>
<h2 id="44-the-particle-system-class">4.4 The Particle System Class</h2><a data-type="indexterm" data-primary="object-oriented programming" data-secondary="classes of user-defined objects" data-tertiary="creating"></a><a data-type="indexterm" data-primary="particle systems" data-secondary="class for" data-tertiary="creating"></a>
<p>OK. Now Ive done two things. Ive written a class to describe an individual <code>Particle</code> object. Ive conquered the array and used it to manage a list of many particles (with the ability to add and delete at will).</p>
<p>I could stop here. However, one additional step I can and should take is to write a class to describe the list of <code>Particle</code> objects itself—the <code>ParticleSystem</code> class. This allows me to remove the bulky logic of looping through all particles from <code>draw()</code>, as well as open up the possibility of having more than one particle system.</p>
<p>If you recall the goal I set at the beginning of this chapter was to write code like:</p>
<pre class="codesplit" data-code-language="javascript">//{!1} Just one wee ParticleSystem!
let system;
function setup() {
createCanvas(640, 360);
system = new ParticleSystem();
}
function draw() {
background(255);
system.run();
}</pre>
<p>Lets take the code from Example 4.2 and review a bit of object-oriented programming, looking at how each piece <code>setup()</code> and <code>draw()</code> can fit into the <code>ParticleSystem</code> class.</p>
<p><strong>Array in </strong><code><strong>setup()</strong></code><strong> and </strong><code><strong>draw()</strong></code></p>
<pre class="codesplit" data-code-language="javascript"><strong>let particles = [];</strong>
function setup() {
createVector(640, 360);
}
function draw() {
background(255);
<strong>particles.push(new Particle());</strong>
<strong>for (let i = particles.length - 1; i >= 0; i--) {
let particles = particles[i];
particle.run();
if (particle.isDead()) {
particles.splice(i, 1);
}
}</strong>
}</pre>
<p><strong>Array in the ParticleSystem class</strong></p>
<pre class="codesplit" data-code-language="javascript">class ParticleSystem {
constructor() {
<strong>this.particles = [];</strong>
}
addParticle() {
<strong>this.particles.push(new Particle());</strong>
}
run() {
<strong>for (let i = this.particles.length - 1; i >= 0; i--) {
let particle = this.particles[i];
particle.run();
if (particle.isDead()) {
this.particles.splice(i, 1);
}
}</strong>
}
}</pre><a data-type="indexterm" data-primary="particle systems" data-secondary="origin point (of particles)"></a>
<p>I could also add new features to the particle system itself. For example, it might be useful for the <code>ParticleSystem</code> class to keep track of an origin point where particles are made. This fits with the idea of a particle system being an “emitter,” a place where particles are born and sent out into the world. The origin point could be initialized in the constructor.</p>
<div data-type="example">
<h3 id="example-43-simple-single-particle-system">Example 4.3: Simple Single Particle System</h3>
</div>
<pre class="codesplit" data-code-language="javascript">class ParticleSystem {
ParticleSystem(x, y) {
//{!1 .bold} This particular ParticleSystem implementation includes an origin point where each Particle begins.
this.origin = createVector(x, y);
this.particles = [];
}
addParticle() {
//{!1 .bold} The origin is passed to each Particle when it is added.
this.particles.add(new Particle(origin.x, origin.y));
}</pre>
<div data-type="exercise">
<h3 id="exercise-43">Exercise 4.3</h3>
<p>Make the origin point move dynamically. Emit particles from the mouse position or use the concepts of velocity and acceleration to make the system move autonomously.</p>
</div>
<div data-type="exercise">
<h3 id="exercise-44">Exercise 4.4</h3>
<p>Building off Chapter 3s “Asteroids” example, use a particle system to emit particles from the ships “thrusters” whenever a thrust force is applied. The particles initial velocity should be related to the ships current direction.</p>
</div>
<h2 id="45-a-system-of-systems">4.5 A System of Systems</h2><a data-type="indexterm" data-primary="particle systems" data-secondary="multiple systems" data-tertiary="organizing"></a>
<p>Lets take a moment to recap what Ive covered so far. I described an individual <code>Particle</code> object. I also described a system of <code>Particle</code> objects, and this we call a “particle system.” And Ive defined a particle system as a collection of independent objects. But isnt a particle system itself an object? If thats the case (which it is), theres no reason why I couldnt also build a collection of many particle systems, i.e. a system of systems.</p>
<p>This line of thinking could of course take you even further, and you might lock yourself in a basement for days sketching out a diagram of a system of systems of systems of systems of systems of systems. Of systems. After all, I could create a description of the world in this way. An organ is a system of cells, a human body is a system of organs, a neighborhood is a system of human bodies, a city is a system of neighborhoods, and so on and so forth. While this is an interesting road to travel down, its a bit beyond where Id like to be right now. It is, however, quite useful to know how to write a p5.js sketch that keeps track of many particle systems, each of which keep track of many particles. Take the following scenario.</p>
<p>You start with a blank screen. </p>
<figure>
<img src="images/04_particles/04_particles_4.png" alt="">
<figcaption></figcaption>
</figure>
<p>You click the mouse and generate a particle system at the mouses position.</p>
<figure>
<img src="images/04_particles/04_particles_5.png" alt="">
<figcaption></figcaption>
</figure>
<p>Each time you click the mouse, a new particle system is created at the mouses position.</p>
<figure>
<img src="images/04_particles/04_particles_6.png" alt="">
<figcaption></figcaption>
</figure>
<p>In <a href="#example-43-simple-single-particle-system">Example 4.3</a>, I stored a single reference to a <code>ParticleSystem</code> object in the variable <code>system</code>.</p>
<pre class="codesplit" data-code-language="javascript">let system;
function setup() {
createCanvas(640, 360);
system = new ParticleSystem(1, createVector(width/2, 50));
}
function draw() {
background(255);
system.run();
system.addParticle();
}</pre>
<p>For this new example, what I want to do instead is create an <code>Array</code> to keep track of multiple instances of particle systems themselves. When the sketch begins, i.e. in <code>setup()</code>, the <code>Array</code> is empty.</p>
<div data-type="example">
<h3 id="example-44-system-of-systems">Example 4.4: System of systems</h3>
</div>
<pre class="codesplit" data-code-language="javascript">//{!1} This time, the type of thing we are putting in the array is a particle system itself!
let systems = [];
function setup() {
createCanvas(600, 200);
}</pre>
<p>Whenever the mouse is pressed, a new <code>ParticleSystem</code> object is created and placed into the <code>Array</code>.</p>
<pre class="codesplit" data-code-language="javascript">function mousePressed() {
systems.push(new ParticleSystem(createVector(mouseX, mouseY)));
}</pre>
<p>And in <code>draw()</code>, instead of referencing a single <code>ParticleSystem</code> object, I can now iterate over all the systems in the <code>Array</code> and call <code>run()</code> on each of them.</p>
<pre class="codesplit" data-code-language="javascript">function draw() {
background(255);
//{!4} Since we arent deleting elements, we can use the for of loop!
for (let system of systems) {
system.run();
system.addParticle();
}
}</pre>
<div data-type="exercise">
<h3 id="exercise-45">Exercise 4.5</h3>
<p>Rewrite Example 4.4 so that each particle system doesnt live forever. When a particle system is empty (i.e. has no particles left), remove it from the array <code>systems</code>.</p>
</div>
<div data-type="exercise">
<h3 id="exercise-46">Exercise 4.6</h3>
<p>Create a simulation of an object shattering into many pieces. How can you turn one large shape into many small particles? Can you create several large shapes on the screen that each shatter clicked on?</p>
</div>
<h2 id="46-inheritance-and-polymorphism-an-introduction">4.6 Inheritance and Polymorphism: An Introduction</h2><a data-type="indexterm" data-primary="<em>Learning p5<" data-secondary="em> (Shiffman)"></a><a data-type="indexterm" data-primary="inheritance"></a><a data-type="indexterm" data-primary="object-oriented programming" data-secondary="inheritance"></a><a data-type="indexterm" data-primary="object-oriented programming" data-secondary="polymorphism"></a><a data-type="indexterm" data-primary="polymorphism"></a>
<p>You may have encountered the terms <em>inheritance</em> and <em>polymorphism</em> in your programming life before this book. After all, they are two of the three fundamental principles behind the theory of object-oriented programming (the other being <em>encapsulation</em>). If youve read other programming books, chances are its been covered. My beginner text, <em>Learning Processing</em>, has close to an entire chapter (#22) dedicated to these two topics.</p>
<p>Still, perhaps youve only learned about it in the abstract sense and never had a reason to really use inheritance and polymorphism. If this is true, youve come to the right place. Without these two topics, your ability to program a variety of particles and particle systems is extremely limited. (In the next chapter, Ill also demonstrate how understanding these topics will help you use physics libraries.)</p>
<p>Imagine the following. Its a Saturday morning, youve 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. Its your old friend So and Sos birthday and youve decided youd like to make a greeting card with p5. How about some confetti for a birthday? Purple confetti, pink confetti, star-shaped confetti, square confetti, fast confetti, fluttery confetti, etc. All of these pieces of confetti with different appearances and different behaviors explode onto the screen at once.</p>
<p>What youve got here is clearly a particle system—a collection of individual pieces of confetti (i.e. particles). You might be able to cleverly design a <code>Particle</code> class to have variables that store color, shape, behavior, etc. For a variety, you initalize those variables with random values. But what if your particles are drastically different? This could become very messy, having all sorts of code for different ways of being a particle in the same class. Well, you might consider doing the following:</p>
<pre class="codesplit" data-code-language="javascript">class HappyConfetti {
}
class FunConfetti {
}
class WackyConfetti {
}</pre>
<p>This is a nice solution: create three different classes to describe the different kinds of pieces of confetti that are part of your particle system. The <code>ParticleSystem</code> constructor could then have some code to pick randomly from the three classes when filling the array. Note that this probabilistic method is the same one I employed in the random walk examples in the <a href="/introduction#i2-the-random-walker-class">Introduction</a>.</p>
<pre class="codesplit" data-code-language="javascript">class ParticleSystem {
constructor(num) {
this.particles = [];
for (let i = 0; i &#x3C; num; i++) {
let r = random(1);
//{!7} Randomly picking a "kind" of particle
if (r &#x3C; 0.33) {
this.particles.add(new HappyConfetti());
} else if (r &#x3C; 0.67) {
this.particles.add(new FunConfetti());
} else {
this.particles.add(new WackyConfetti());
}
}
}</pre>
<p>OK, I need to pause for a moment. Youve done nothing wrong. All you wanted to do was wish your friend a happy birthday and enjoy writing some code. But while the reasoning behind the above approach is quite sound, theres a problem.</p>
<p><span class="highlight">Arent you going to be copying/pasting a lot of code between the different “confetti” classes?</span></p><a data-type="indexterm" data-primary="code duplication" data-secondary="inheritance and"></a>
<p>Yes. Even though the kinds of particles are different enough to merit our breaking them out into separate classes, there is still a ton of code that they will likely share. Theyll all have vectors to keep track of position, velocity, and acceleration; an <code>update()</code> function that implements the motion algorithm; etc.</p>
<p>This is where <strong><em>inheritance</em></strong> comes in. Inheritance allows you to write a class that <em>inherits</em> variables and functions from another class, all the while implementing its own custom features.</p>
<p>Now that we understand the problem, lets look at this concept in a bit more detail and then create a particle system example that implements inheritance.</p>
<h2 id="47-inheritance-basics">4.7 Inheritance Basics</h2>
<p>Lets take a different example, the world of animals: dogs, cats, monkeys, pandas, wombats, and sea nettles. Ill start by coding a <code>Dog</code> class. A <code>Dog</code> object will have an age variable (an integer), as well as <code>eat()</code>, <code>sleep()</code>, and <code>bark()</code> functions.</p>
<pre class="codesplit" data-code-language="javascript">class Dog {
constructor() {
this.age = 0;
}
eat() {
print("Yum!");
}
sleep() {
print("Zzzzzz");
}
bark() {
print("WOOF");
}
}</pre>
<p>Now, lets move on to cats.</p>
<pre class="codesplit" data-code-language="javascript">class Cat {
// Dogs and cats have the same variables (age) and functions (eat, sleep).
constructor() {
this.age = 0;
}
eat() {
print("Yum!");
}
sleep() {
print("Zzzzzz");
}
//{!3} No bark(), instead a unique function for meowing.
meow() {
print("MEOW!");
}
}</pre>
<p>As I move on to rewriting the same code for fish, horses, koalas, and lemurs, this process will become rather tedious. A better solution is to develop a generic <code>Animal</code> class that can describe any type of animal. All animals eat and sleep, after all. I could then say:</p>
<ul>
<li>A dog is an animal and has all the properties of animals and can do all the things animals do. Also, a dog can bark.</li>
<li>A cat is an animal and has all the properties of animals and can do all the things animals do. Also, a cat can meow.</li>
</ul><a data-type="indexterm" data-primary="inheritance" data-secondary="subclass"></a><a data-type="indexterm" data-primary="inheritance" data-secondary="superclasses"></a><a data-type="indexterm" data-primary="subclass"></a><a data-type="indexterm" data-primary="superclasses"></a>
<p>Inheritance makes this all possible. With inheritance, classes can inherit properties (variables) and functionality (methods) from other classes. A <code>Dog</code> class is a child (<strong><em>subclass</em></strong>) of an <code>Animal</code> class. Children will automatically inherit all variables and functions from the parent (<strong><em>superclass</em></strong>), but can also include functions and variables not found in the parent. Like a phylogenetic "tree of life," inheritance follows a tree structure. Dogs inherit from mammals, which inherit from animals, etc.</p>
<figure>
<img src="images/04_particles/04_particles_7.png" alt="Figure 4.2">
<figcaption>Figure 4.2</figcaption>
</figure><a data-type="indexterm" data-primary="inheritance" data-secondary="syntax for"></a><a data-type="indexterm" data-primary="p5" data-secondary="class inheritance" data-tertiary="syntax for"></a>
<p>Here is how the syntax works with inheritance.</p>
<pre class="codesplit" data-code-language="javascript">//{!1} The Animal class is the parent (or super) class.
class Animal {
constructor() {
//{!1} Dog and Cat will inherit the variable age.
this.age = 0;
}
//{!7} Dog and Cat will inherit the functions eat() and sleep().
eat() {
print("Yum!");
}
sleep() {
print("Zzzzzz");
}
}
//{!1 .bold} The Dog class is the child (or sub) class, indicated by the code "extends Animal".
class Dog extends Animal {
constructor() {
//{!1 .bold} super() executes code found in the parent class.
super();
}
//{!3} bark() is defined in the child class, since it isn't part of the parent class.
bark() {
print("WOOF!");
}
}
class Cat extends Animal {
constructor() {
super();
}
meow() {
print("MEOW!");
}
}</pre>
<p>This brings up two new terms:</p><a data-type="indexterm" data-primary="extends keyword (p5)"></a><a data-type="indexterm" data-primary="inheritance" data-secondary="extends keyword (p5)"></a><a data-type="indexterm" data-primary="inheritance" data-secondary="super() function (p5)"></a><a data-type="indexterm" data-primary="super() function(p5)"></a>
<ul>
<li><strong><em>extends</em></strong> This keyword is used to indicate a parent for the class being defined. Note that classes can only extend <em>one</em> class. However, classes can extend classes that extend other classes, i.e. <code>Dog extends Animal</code>, <code>Terrier extends Dog</code>. Everything is inherited all the way down the line.</li>
<li><strong><em>super()</em></strong> This calls the constructor in the parent class. In other words, whatever you do in the parent constructor, do so in the child constructor as well. Other code can be written into the constructor in addition to <code>super()</code>. <code>super()</code> can also receive arguments if there is a parent constructor defined with matching arguments.</li>
</ul>
<p>A subclass can be expanded to include additional functions and properties beyond what is contained in the superclass. For example, lets assume that a <code>Dog</code> object has a haircolor variable in addition to age. The class would now look like this:</p>
<pre class="codesplit" data-code-language="javascript">class Dog extends Animal {
constructor() {
super();
//{!1} A child class can introduce new variables not included in the parent.
this.haircolor = color(210, 105, 30);
}
bark() {
print("WOOF!");
}
}</pre><a data-type="indexterm" data-primary="inheritance" data-secondary="overriding superclass functions"></a><a data-type="indexterm" data-primary="superclasses" data-secondary="overriding functions from"></a>
<p>Note how the parent constructor is called via <code>super()</code>, which sets the age to 0, but the haircolor is set inside the <code>Dog</code> constructor itself. If a <code>Dog</code> object eats differently than a generic <code>Animal</code> object, parent functions can be <em>overridden</em> by rewriting the function inside the subclass.</p>
<pre class="codesplit" data-code-language="javascript">class Dog extends Animal {
constructor() {
super();
this.haircolor = color(random(255));
}
// A child can override a parent function if necessary.
eat() {
//{!1} A Dog's specific eating characteristics
print("Woof! Woof! Slurp.")
}
bark() {
print("WOOF!");
}
}</pre><a data-type="indexterm" data-primary="inheritance" data-secondary="adding functionality to superclass objects"></a><a data-type="indexterm" data-primary="subclass" data-secondary="adding functionality to superclass objects"></a><a data-type="indexterm" data-primary="superclasses" data-secondary="adding functionality within subclasses"></a>
<p>But what if a dog eats the same way as a generic animal, just with some extra functionality? A subclass can both run the code from a parent class and incorporate custom code.</p>
<pre class="codesplit" data-code-language="javascript">class Dog extends Animal {
constructor() {
super();
this.haircolor = color(random(255));
}
eat() {
//{!1 .bold} Call eat() from Animal. A child can execute a function from the parent while adding its own code.
super.eat();
//{!1 .bold} Add some additional code for a Dog's specific eating characteristics.
print("Woof!!!");
}
bark() {
print("WOOF!");
}
}</pre>
<h2 id="48-particles-with-inheritance">4.8 Particles with Inheritance</h2><a data-type="indexterm" data-primary="inheritance" data-secondary="implementing"></a>
<p>Now that Ive offered an introduction to the theory of inheritance and its syntax, its time write a working example in p5.js based on the <code>Particle</code> class.</p>
<p>Lets review a simple <code>Particle</code> implementation, further simplified from <a href="#example-41-a-single-particle">Example 4.1</a>:</p>
<pre class="codesplit" data-code-language="javascript">class Particle {
Particle(pos) {
this.acceleration = createVector(0, 0.05);
this.velocity = createVector(random(-1, 1), random(-2, 0));
this.position = pos.copy();
}
run() {
this.update();
this.display();
}
update() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
}
display() {
fill(0);
ellipse(this.position.x, this.position.y, 8, 8);
}
}</pre>
<p>Next, Ill create a subclass that extends <code>Particle</code> (Ill call it <code>Confetti</code>). It will inherit all the instance variables and methods from <code>Particle</code>. Ill also include a constructor and execute the code from the parent class with <code>super()</code>.</p>
<pre class="codesplit" data-code-language="javascript">class Confetti extends Particle {
constructor(pos) {
super(pos);
// I could add variables for only Confetti here.
}
// There is no code here because update() is inherited from the parent.
//{!6} Override the display method.
display() {
rectMode(CENTER);
fill(175);
stroke(0);
rect(this.position.x, this.position.y, 8, 8);
}
}</pre>
<p>Lets make this a bit more sophisticated. Lets say I want to have the <code>Confetti</code> particle rotate as it flies through the air. One option, of course, is to model angular velocity and acceleration as described in Chapter 3. For ease, however, Ill try a quick and dirty solution.</p>
<p>I know a particle has an <em>x</em> position somewhere between 0 and the width of the canvas. What if I said: when the particles <em>x</em> position is 0, its rotation should be 0; when its <em>x</em> position is equal to the width, its rotation should be equal to <code>TWO_PI</code>? Does this ring a bell? Whenever a value has one range that you want to map to another range, you can use p5s <code>map()</code> function, which I discussed in the <a href="/introduction#i6-perlin-noise-a-smoother-approach">Introduction</a>!</p>
<pre class="codesplit" data-code-language="javascript">let angle = map(this.position.x, 0, width, 0, TWO_PI);</pre>
<p>And just to give it a bit more spin, I can actually map the angles range from 0 to <code>TWO_PI * 2</code>. Heres how this code fits into the <code>display()</code> function.</p>
<pre class="codesplit" data-code-language="javascript"> display() {
let theta = map(this.position.x, 0, width, 0, TWO_PI * 2);
rectMode(CENTER);
fill(0, this.lifespan);
stroke(0, this.lifespan);
//{!6} To rotate() a shape in p5, transformations are necessary. For more, visit: http://p5.org/learning/transform2d/
push();
translate(this.position.x, this.position.y);
rotate(theta);
rectMode(CENTER);
rect(0, 0, 8, 8);
pop();
}</pre>
<div data-type="exercise">
<h3 id="exercise-47">Exercise 4.7</h3>
<p>Instead of using <code>map()</code> to calculate theta, try modeling angular velocity and acceleration?</p>
</div>
<p>Now that I have a <code>Confetti</code> subclass that extends the base <code>Particle</code> class, the next step is to also add <code>Confetti</code> objects to the array of particles defined in the <code>ParticleSystem</code> class.</p>
<h2 id="410-particle-systems-with-inheritance">4.10 Particle Systems with Inheritance</h2>
<div data-type="example">
<h3 id="example-45-particle-system-inheritance">Example 4.5: Particle system inheritance</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/rkJJFxbOe"></div>
<figcaption></figcaption>
</figure>
<figure>
<img src="images/04_particles/04_particles_8.png" alt="">
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">class ParticleSystem {
constructor(position) {
this.origin = this.position.copy();
//{!1 .bold} One list, for anything that is a Particle or extends Particle
this.particles = [];
}
addParticle() {
const r = random(1);
//{!5 .bold .code-wide} A 50% chance of adding each kind of particle.
if (r &#x3C; 0.5) {
this.particles.add(new Particle(this.origin));
} else {
this.particles.add(new Confetti(this.origin));
}
}
run() {
for (let i = this.particles.length - 1; i >= 0; i--) {
let p = this.particles[i];
p.run();
if (p.isDead() {
this.particles.splice(i, 1);
}
}
}
}</pre>
<div data-type="exercise">
<h3 id="exercise-48">Exercise 4.8</h3>
<p>Create a particle system with more than two “kinds” of particles. Try varying the behavior of the particles in addition to the design.</p>
</div>
<h2 id="411-particle-systems-with-forces">4.11 Particle Systems with Forces</h2><a data-type="indexterm" data-primary="forces" data-secondary="particle systems with"></a><a data-type="indexterm" data-primary="particle systems" data-secondary="forces and"></a>
<p>So far in this chapter, Ive focused on structuring code in an object-oriented way to manage a collection of particles. Maybe you noticed, or maybe you didnt, but during this process I unwittingly took a couple steps backward from the examples in previous chapters. Take a look at the constructor of the <code>Particle</code> class.</p>
<pre class="codesplit" data-code-language="javascript"> constructor(pos) {
//{!1 .bold} Setting acceleration to a constant value!
this.acceleration = createVector(0, 0.05);
this.velocity = createVector(random(-1, 1), random(-2, 0));
this.position = pos.copy();
this.lifespan = 255.0;
}</pre>
<p>And now the <code>update()</code> function:</p>
<pre class="codesplit" data-code-language="javascript"> update() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
[inline]// Where is the line of code to clear acceleration?
this.lifespan -= 2.0;
}</pre>
<p>The <code>Particle</code> class is structured to have a constant acceleration, one that never changes. A better framework would be to return to Newtons second law (F = M * A) and incorporate the force accumulation algorithm from <a href="/forces#23-force-accumulation">Chapter 2</a>.</p>
<p>Step 1 is to add in the <code>applyForce()</code> function. (Remember, you need to make a copy of the <code>p5.Vector</code> before dividing it by mass.)</p>
<pre class="codesplit" data-code-language="javascript"> applyForce(force) {
let f = force.copy();
f.div(this.mass);
this.acceleration.add(f);
}</pre>
<p>Once I have this, I can add one more line of code to clear the acceleration at the end of <code>update()</code>.</p>
<pre class="codesplit" data-code-language="javascript"> update() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
//{!1} There it is!
this.acceleration.mult(0);
this.lifespan -= 2.0;
}</pre>
<p>And the <code>Particle</code> class is complete!</p>
<pre class="codesplit" data-code-language="javascript">class Particle {
constructor(pos) {
//{!1} Now start with acceleration of 0,0.
this.acceleration = createVector(0, 0);
this.velocity = createVector(random(-1, 1),random(-2, 0));
this.position = pos.copy();
this.lifespan = 255.0;
//{!1} We could vary mass for more interesting results.
this.mass = 1;
}
run() {
this.update();
this.display();
}
// Newtons second law and force accumulation
applyForce(force) {
const f = force.copy();
f.div(this.mass);
this.acceleration.add(f);
}
// Standard update
update() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.acceleration.mult(0);
this.lifespan -= 2.0;
}
// The Particle is a circle.
display() {
stroke(255, this.lifespan);
fill(255, this.lifespan);
ellipse(this.position.x, this.position.y, 8, 8);
}
//{!3} Should the Particle be deleted?
isDead() {
return (this.lifespan &#x3C; 0.0);
}
}</pre>
<p>Now that the <code>Particle</code> class is complete, I have a very important question to ask. Where do you call the <code>applyForce()</code> function? Where in the code is it appropriate to apply a force to a particle? In my view theres no right or wrong answer; it really depends on the exact functionality and goals of a particular p5 sketch. Still, lets proceed by considering a generic situation that would likely apply to most cases and craft a model for applying forces to individual particles in a system.</p>
<p>Consider the following goal: apply a force globally every time through <code>draw()</code> to all particles. Ill pick an easy one for now: a force pointing down, like gravity.</p>
<pre class="codesplit" data-code-language="javascript">let gravity = createVector(0, 0.1);</pre>
<p>I said it should always be applied, i.e. in <code>draw()</code>, so lets take a look at our <code>draw()</code> function as it stands.</p>
<pre class="codesplit" data-code-language="javascript">function draw() {
background(100);
system.addParticle();
system.run();
}</pre>
<p>Well, it seems theres a small problem. <code>applyForce()</code> is a method written inside the <code>Particle</code> class, but there is no reference to the individual particles themselves, only the <code>ParticleSystem</code> object: the variable <code>system</code>.</p>
<p>Since I want all particles to receive the force, however, I can decide to apply the force to the particle system and let it manage applying the force to all the individual particles:</p>
<pre class="codesplit" data-code-language="javascript">function draw() {
background(100);
let gravity = createVector(0, 0.1);
//{!1} Applying a force to the system as a whole
system.applyForce(gravity);
system.addParticle();
system.run();
}</pre><a data-type="indexterm" data-primary="object-oriented programming" data-secondary="maintaining encapsulation"></a>
<p>Of course, if I call a new function on the <code>ParticleSystem</code> object in <code>draw()</code>, well, I have to write that function in the <code>ParticleSystem</code> class. Lets describe the job that function needs to perform: receive a force as a <code>p5.Vector</code> and apply that force to all the particles.</p>
<p>Now in code:</p>
<pre class="codesplit" data-code-language="javascript"> applyForce(force) {
for (let p of this.particles) {
p.applyForce(force);
}
}</pre>
<p>It almost seems silly to write this function. What the code says is “apply a force to a particle system so that the system can apply that force to all of the individual particles.” Nevertheless, its actually quite reasonable. After all, the <code>ParticleSystem</code> object is in charge of managing the particles, so if you want to talk to the particles, youve got to talk to them through their manager. (Also, heres a chance for the enhanced loop since no particles are being deleted!)</p>
<p>Here is the full example (assuming the existence of the <code>Particle</code> class written above; no need to include it again since nothing has changed):</p>
<div data-type="example">
<h3 id="example-46-particle-system-with-forces">Example 4.6: Particle system with forces</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/rkrHFebul"></div>
<figcaption></figcaption>
</figure>
<figure>
<img src="images/04_particles/04_particles_9.png" alt="">
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">let system;
function setup() {
createCanvas(640, 360);
system = new ParticleSystem(createVector(width/2, 50));
}
function draw() {
background(100);
//{!2 .bold} Apply a force to all particles.
const gravity = createVector0, 0.1);
system.applyForce(gravity);
system.addParticle();
system.run();
}
class ParticleSystem {
constructor(position) {
this.origin = this.position.copy();
this.particles = [];
}
addParticle() {
this.particles.push(new Particle(this.origin));
}
applyForce(force) {
//{!3} Using a for of loop to apply the force to all particles
for (let p of this.particles) {
p.applyForce(force);
}
}
run() {
//{!7} Cant use the enhanced loop because checking for particles to delete.
for (let i = this.particles.length - 1; i >= 0; i--) {
const p = this.particles[i];
p.run();
if (p.isDead()) {
this.particles.splice(i, 1);
}
}
}
}</pre>
<h2 id="412-particle-systems-with-repellers">4.12 Particle Systems with Repellers</h2><a data-type="indexterm" data-primary="forces" data-secondary="applying to single objects in a system"></a><a data-type="indexterm" data-primary="particle systems" data-secondary="applying force to single particles in"></a><a data-type="indexterm" data-primary="particles" data-secondary="applying force to single particles in"></a>
<p>What if I wanted to take this example one step further and add a <code>Repeller</code> object—the inverse of the <code>Attractor</code> object covered in <a href="/forces#29-gravitational-attraction">Chapter 2</a> that pushes any particles away that get close? This requires a bit more sophistication because, unlike the gravity force, each force an attractor or repeller exerts on a particle must be calculated for each particle.</p>
<div class="col-list">
<div>
<figure>
<img src="images/04_particles/04_particles_10.png" alt="Figure 4.3: Gravity force—vectors are all identical ">
<figcaption>Figure 4.3: Gravity force—vectors are all identical </figcaption>
</figure>
</div>
<div>
<figure>
<img src="images/04_particles/04_particles_11.png" alt="Figure 4.4: Attractor force—vectors are all different ">
<figcaption>Figure 4.4: Attractor force—vectors are all different </figcaption>
</figure>
</div>
</div>
<p>Lets start solving this problem by examining how I might incorporate a new <code>Repeller</code> object into a simple particle system plus forces example. Im going to need two major additions to the code:</p>
<ol>
<li>A <code>Repeller</code> object (declared, initialized, and displayed).</li>
<li>A function that passes the <code>Repeller</code> object into the <code>ParticleSystem</code> so that it can apply a force to each particle object.</li>
</ol>
<pre class="codesplit" data-code-language="javascript">let system;
//{!1 .bold} New thing: we declare a Repeller object.
let repeller;
function setup() {
createCanvas(640,360);
system = new ParticleSystem(createVector(width/2, 50));
//{!1 .bold} New thing: we initialize a Repeller object.
repeller = new Repeller(width / 2 - 20, height / 2);
}
function draw() {
background(100);
system.addParticle();
let gravity = createVector(0, 0.1);
system.applyForce(gravity);
//{!1 .bold} New thing: a function to apply a force from a repeller.
system.applyRepeller(repeller);
system.run();
//{!1 .bold} New thing: display the Repeller object.
repeller.display();
}</pre>
<p>Making a <code>Repeller</code> object is quite easy; its a duplicate of the <code>Attractor</code> class from Chapter 2, Example 2.6.</p>
<pre class="codesplit" data-code-language="javascript">class Repeller {
constructor(x, y) {
//{!1 .bold} A Repeller doesnt move, so you just need position.
this.position = createVector(x, y);
this.r = 10;
}
display() {
stroke(255);
fill(255);
ellipse(this.position.x, this.position.y, this.r * 2, this.r * 2);
}
}</pre>
<p>The more difficult question is, how do I write the <code>applyRepeller()</code> function? Instead of passing a <code>p5.Vector</code> into a function like with <code>applyForce()</code>, I need to instead pass a <code>Repeller</code> object into <code>applyRepeller()</code> and ask that function to do the work of calculating the force between the repeller and all particles. Lets look at both of these functions side by side.</p>
<p><strong>applyForce(force)</strong></p>
<pre class="codesplit" data-code-language="javascript">applyForce(force) {
for (let p of this.particles) {
p.applyForce(force);
}
}</pre>
<p><strong>applyRepeller(repeller)</strong></p>
<pre class="codesplit" data-code-language="javascript">applyRepeller(repeller) {
for (let p of this.particles) {
let force = repeller.repel(p);
p.applyForce(force);
}
}</pre>
<p>The functions are almost identical. There are only two differences. One I mentioned before—a <code>Repeller</code> object is the argument, not a <code>p5.Vector</code>. The second difference is the more important one. I must calculate a custom <code>p5.Vector</code> force for each and every particle and apply that force. How is that force calculated? In a function called <code>repel()</code>, the inverse of the <code>attract()</code> function from the <code>Attractor</code> class.</p>
<pre class="codesplit" data-code-language="javascript"> // All the same steps we had to calculate an attractive force, only pointing in the opposite direction.
repel(particle) {
// 1) Get force direction.
let dir = p5.Vector.sub(this.position, particle.position);
//{!2} 2) Get distance (constrain distance).
let d = dir.mag();
d = constrain(d, 5, 100);
// 3) Calculate magnitude.
let strength = -1 * this.G / (d * d);
// 4) Make a vector out of direction and magnitude.
dir.setMag(strength);
return dir;
}</pre>
<p>Notice how throughout this entire process of adding a repeller to the environment, I never once considered editing the <code>Particle</code> class itself. A particle doesnt actually have to know anything about the details of its environment; it simply needs to manage its position, velocity, and acceleration, as well as have the ability to receive an external force and act on it.</p>
<p>Now look at this example in its entirety, again leaving out the <code>Particle</code> class, which hasnt changed.</p>
<div data-type="example">
<h3 id="example-47-particlesystem-with-repeller">Example 4.7: ParticleSystem with repeller</h3>
<figure>
<div data-type="embed" data-p5-editor="https://editor.p5js.org/natureofcode/sketches/rkVTFxZ_e"></div>
<figcaption></figcaption>
</figure>
<figure>
<img src="images/04_particles/04_particles_12.png" alt="">
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">// One ParticleSystem
let system;
//{!1} One repeller
let repeller;
function setup() {
createCanvas(640, 360);
system = new ParticleSystem(createVector(width/2, 50));
repeller = new Repeller(width/2 - 20, height/2);
}
function draw() {
background(100);
system.addParticle();
// Were applying a universal gravity.
let gravity = createVector(0, 0.1);
system.applyForce(gravity);
//{!1} Applying the repeller
system.applyRepeller(repeller);
system.run();
repeller.display();
}
//{!1} The ParticleSystem manages all the Particles.
class ParticleSystem {
constructor(position) {
this.origin = this.position.copy();
this.particles = [];
}
addParticle() {
this.particles.add(new Particle(this.origin));
}
applyForce(force) {
//{!3} Applying a force as a p5.Vector
for (let p of this.particles) {
p.applyForce(force);
}
}
applyRepeller(repeller) {
//{!4} Calculating a force for each Particle based on a Repeller
for (let p of this.particles) {
let force = repeller.repel(p);
p.applyForce(force);
}
}
run() {
for (let i = this.particles.length - 1; i >= 0; i--) {
const p = this.particles[i];
p.run();
if (p.isDead()) {
this.particles.splice(i, 1);
}
}
}
}
class Repeller {
constructor(x, y) {
position = createVector(x,y);
//{!1} How strong is the repeller?
this.strength = 100;
this.r = 10;
}
display() {
stroke(255);
fill(255);
ellipse(this.position.x, this.position.y, this.r * 2, this.r * 2);
}
repel(particle) {
//{!6 .code-wide} This is the same repel algorithm we used in Chapter 2: forces based on gravitational attraction.
let dir = p5.Vector.sub(this.position, particle.position);
let distance = dir.mag();
distance = constrain(d, 5, 100);
let force = -1 * this.strength / (distance * distance);
dir.mult(force);
return dir;
}
}</pre>
<div data-type="exercise">
<h3 id="exercise-49">Exercise 4.9</h3>
<p>Expand the above example to include many repellers (using an array).</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. (Note that Ill be going through this in detail in Chapter 6.)</p>
</div>
<h2 id="413-image-textures-and-additive-blending">4.13 Image Textures and Additive Blending</h2><a data-type="indexterm" data-primary="image textures"></a><a data-type="indexterm" data-primary="p5" data-secondary="incorporating images into projects"></a>
<p>Even though this book is almost exclusively focused on behaviors and algorithms rather than computer graphics and design, I dont think I would be able to live with myself if I finished a discussion of particle systems and never once looked at an example that involves texturing each particle with an image. The way you choose to draw a particle is the “juice,” a key piece of the puzzle in terms of designing certain types of visual effects.</p>
<p>Consider a smoke simulation. Take a look at the following two images:</p>
<div class="col-list">
<div>
<figure>
<img src="images/04_particles/04_particles_13.png" alt="White circles ">
<figcaption>White circles </figcaption>
</figure>
</div>
<div>
<figure>
<img src="images/04_particles/04_particles_14.png" alt="Fuzzy images with transparency ">
<figcaption>Fuzzy images with transparency </figcaption>
</figure>
</div>
</div>
<p>Both of these images were generated from identical algorithms. The only difference is that a white circle is drawn in image A for each particle and a “fuzzy” blob is drawn for each in B.</p>
<figure>
<img src="images/04_particles/04_particles_15.png" alt="Figure 4.5">
<figcaption>Figure 4.5</figcaption>
</figure><a data-type="indexterm" data-primary="image textures" data-secondary="PNG format and"></a><a data-type="indexterm" data-primary="PNG graphic file format"></a>
<p>The good news here is that you get a lot of bang for very little buck. Before you write any code, however, youve got to make your image texture! I recommend using PNG format, as p5 will retain the alpha channel (i.e. transparency) when drawing the image, which is needed for blending the texture as particles layer on top of each other. Once youve made your PNG and deposited it in your sketchs “data” folder, you are on your way with just a few lines of code.</p><a data-type="indexterm" data-primary="image textures" data-secondary="PImage objects (p5)"></a><a data-type="indexterm" data-primary="PImage objects (p5)"></a><a data-type="indexterm" data-primary="p5" data-secondary="PImage objects"></a>
<p>First, well need to declare a <code>p5.Image</code> object.</p>
<div data-type="example">
<h3 id="example-48-image-texture-particle-system">Example 4.8: Image texture particle system</h3>
</div>
<pre class="codesplit" data-code-language="javascript">let img;</pre>
<p>Load the image in <code>preload()</code>.</p>
<pre class="codesplit" data-code-language="javascript">function preload() {
//{!1} Loading the PNG
img = loadImage("texture.png");
}</pre>
<p>And when it comes time to draw the particle, use the image variable instead of drawing an ellipse or rectangle.</p>
<pre class="codesplit" data-code-language="javascript"> render() {
imageMode(CENTER);
//{!1} Note how tint() is the image equivalent of shapes fill().
tint(255, this.lifespan);
image(img, this.position.x, this.position.y);
}</pre><a data-type="indexterm" data-primary="natural phenomena" data-secondary="smoke" data-tertiary="modeling with particle systems"></a><a data-type="indexterm" data-primary="particle systems" data-secondary="smoke" data-tertiary="modeling"></a>
<p>Incidentally, this smoke example is a nice excuse to revisit the Gaussian distributions from the <a href="/introduction#i4-a-normal-distribution-of-random-numbers">Introduction</a>. To make the smoke appear a bit more realistic, instead of launching allow the particles in a purely random direction initial velocity vectors mostly around a mean value (with a lower probability of outliers) can produce an effect that appears less fountain-like and more like smoke (or fire).</p>
<p>Using <code>randomGaussian()</code> velocities can be intialized as follows:</p>
<pre class="codesplit" data-code-language="javascript"> let vx = randomGaussian() * 0.3;
let vy = randomGaussian() * 0.3 - 1.0;
let vel = createVector(vx, vy);</pre>
<p>Finally, in this example, a wind force is applied to the smoke mapped from the mouses horizontal position.</p>
<pre class="codesplit" data-code-language="javascript">function draw() {
background(0);
//{!2} Wind force direction based on mouseX.
let dx = map(mouseX, 0, width, -0.2, 0.2);
let wind = createVector(dx, 0);
system.applyForce(wind);
system.run();
//{!3} Two particles are added each cycle through draw().
for (let i = 0; i &#x3C; 2; i++) {
system.addParticle();
}
}</pre>
<div data-type="exercise">
<h3 id="exercise-411">Exercise 4.11</h3>
<p>Try creating your own textures for different types of effects. Can you make it look like fire, instead of smoke?</p>
</div>
<div data-type="exercise">
<h3 id="exercise-412">Exercise 4.12</h3>
<p>Use an array of images and assign each <code>Particle</code> object a different image. Even though single images are drawn by multiple particles, make sure you dont call <code>loadImage()</code> any more than you need to, i.e. once for each image file.</p>
</div><a data-type="indexterm" data-primary="blend modes"></a><a data-type="indexterm" data-primary="image textures" data-secondary="blend modes"></a>
<p>Finally, its worth noting that there are many different algorithms for blending colors in computer graphics. These are often referred to as “blend modes.” By default, when you draw something on top of something else in p5, you only see the top layer—this is the default “blend” behavior. When pixels have alpha transparency (as they do in the smoke example), p5 uses an alpha compositing algorithm that combines a percentage of the background pixels with the new foreground pixels based on those alpha values themselves.</p><a data-type="indexterm" data-primary="additive blend mode"></a><a data-type="indexterm" data-primary="blend modes" data-secondary="additive"></a><a data-type="indexterm" data-primary="Hodgin" data-secondary="Robert"></a><a data-type="indexterm" data-primary="iTunes visualizer"></a><a data-type="indexterm" data-primary="Magnetosphere"></a>
<p>However, its possible to draw using other blend modes, and a much loved blend mode for particle systems is “additive.” Additive blending in Processing with particle systems was pioneered by <a href="http://roberthodgin.com/">Robert Hodgin</a> in his famous particle system and forces exploration, Magnetosphere, which later became the iTunes visualizer. For more see: <a href="http://roberthodgin.com/magnetosphere-part-2/">Magnetosphere</a>.</p>
<p>Additive blending is one of the simpler blend algorithms and involves simply adding the pixel values of one layer to another (capping all values at 255 of course). This results in a space-age glow effect due to the colors getting brighter and brighter with more layers.</p>
<div data-type="example">
<h3 id="example-49-additive-blending">Example 4.9: Additive blending</h3>
<figure>
<img src="images/04_particles/04_particles_16.png" alt="">
<figcaption></figcaption>
</figure>
</div>
<pre class="codesplit" data-code-language="javascript">function setup() {
createCanvas(640, 360);
}</pre>
<p>Then, before you go to draw anything, you set the blend mode using <code>blendMode()</code>:</p>
<pre class="codesplit" data-code-language="javascript">function draw() {
//{!1} Additive blending
blendMode(ADD);
//{!1} Also need to clear() since the background is added and does not cover what was previously drawn.
clear();
// Also note that the “glowing” effect of additive
// blending will not work with a white
// (or very bright) background.
background(0);
// All other particle stuff goes here.
}</pre>
<div data-type="exercise">
<h3 id="exercise-413">Exercise 4.13</h3>
<p>Use <code>tint()</code> in combination with additive blending to create a rainbow effect.</p>
</div><a data-type="indexterm" data-primary="blend modes" data-secondary="list of"></a>
<div data-type="exercise">
<h3 id="exercise-414">Exercise 4.14</h3>
<p>Try blending with other modes, such as <code>SUBTRACT</code>, <code>LIGHTEST</code>, <code>DARKEST</code>, <code>DIFFERENCE</code>, <code>EXCLUSION</code>, or <code>MULTIPLY</code>.</p>
</div>
<div data-type="project">
<h3 id="the-ecosystem-project-3">The Ecosystem Project</h3>
<p>Step 4 Exercise:</p>
<p>Take your creature from Step 3 and build a system of creatures. How can they interact with each other? Can you use inheritance and polymorphism to create a variety of creatures, derived from the same code base? Develop a methodology for how they compete for resources (for example, food). Can you track a creatures “health” much like a particles lifespan, removing creatures when appropriate? What rules can you incorporate to control how creatures are born into the system?</p>
<p>(Also, you might consider using a particle system itself in the design of a creature. What happens if an emitter is tied to the creatures position?)</p>
</div>
</section>