diff --git a/content/00_randomness.html b/content/00_randomness.html index cf80db50..ed81ce93 100644 --- a/content/00_randomness.html +++ b/content/00_randomness.html @@ -605,7 +605,7 @@
The p5.js noise reference explains that noise is calculated over several octaves. Calling the noiseDetail()
function changes both the number of octaves and their importance relative to one another. This, in turn, changes the quality of the noise values produced.
If you wanted to color every pixel of a canvas randomly using the random()
function, you would need a nested loop to cycle through the rows and columns of pixels and pick a random brightness for each. Note that in p5, the pixels are arranged in an array with four spots for each: red, green, blue, and alpha. For details, see the pixel array video in the “Pixels" track on the Coding Train website.
If you wanted to color every pixel of a canvas randomly using the random()
function, you would need a nested loop to cycle through the rows and columns of pixels and pick a random brightness for each. Note that in p5, the pixels are arranged in an array with four spots for each: red, green, blue, and alpha. For details, see the pixel array video in the “Pixels" track on the Coding Train website.
loadPixels(); for (let x = 0; x < width; x++) { for (let y = 0; y < height; y++) { diff --git a/content/02_forces.html b/content/02_forces.html index d50dc93f..7688b5a6 100644 --- a/content/02_forces.html +++ b/content/02_forces.html @@ -998,7 +998,7 @@Example 2.8: Two-Body Attraction
Example 2.8 could be improved by refactoring the code to include constructor arguments that assign the body velocities. For now, however, this approach serves as a quick way to experiment with patterns based on various initial positions and velocities.
Exercise 2.14
-The paper “Classification of Symmetry Groups for Planar n-Body Choreographies” by James Montaldi and Katrina Steckles (2013) explores choreographic solutions to the n-body problem (defined as periodic motions of bodies following one another at regular intervals). Educator and artist Dan Gries created an interactive demonstration of these choreographies. Try adding a third (or more!) body to Example 2.8 and experiment with setting initial positions and velocities. What choreographies can you achieve?
+The paper “Classification of Symmetry Groups for Planar n-Body Choreographies” by James Montaldi and Katrina Steckles (2013) explores choreographic solutions to the n-body problem (defined as periodic motions of bodies following one another at regular intervals). Educator and artist Dan Gries created an interactive demonstration of these choreographies. Try adding a third (or more!) body to Example 2.8 and experiment with setting initial positions and velocities. What choreographies can you achieve?
I’m now ready to move on to an example with n bodies by incorporating an array:
// Start with an empty array. diff --git a/content/03_oscillation.html b/content/03_oscillation.html index 9568bb6e..1a007649 100644 --- a/content/03_oscillation.html +++ b/content/03_oscillation.html @@ -888,21 +888,21 @@Exercise 3.14
Create a system of multiple bobs and spring connections. How about connecting a bob to another bob with no fixed anchor?
The Pendulum
-You might have noticed that in Example 3.10’s spring code, I never once used sine or cosine. Before you write off all this trigonometry stuff as a tangent, however, allow me to show an example of how it all fits together. Imagine a bob hanging from an anchor connected by a spring with a fully rigid connection that can neither be compressed nor extended. This idealized scenario describes a pendulum and provides an excellent opportunity to practice combining all that you’ve learned about forces and trigonometry.
+You might have noticed that in Example 3.10’s spring code, I never once used sine or cosine. Before you write off all this trigonometry stuff as a tangent, however, allow me to show an example of how it all fits together. Imagine a bob hanging from an anchor connected by a spring with a fully rigid connection that can neither be compressed nor extended. This idealized scenario describes a pendulum and provides an excellent opportunity to practice combining all that you’ve learned about forces and trigonometry.
A pendulum is a bob suspended by an arm from a pivot (previously called the anchor in the spring). When the pendulum is at rest, it hangs straight down, as in Figure 3.18. If you lift up the pendulum at an angle from its resting state and then release it, however, it starts to swing back and forth, tracing the shape of an arc. A real-world pendulum would live in a 3D space, but I’m going to look at a simpler scenario: a pendulum in the 2D space of a p5.js canvas. Figure 3.19 shows the pendulum in a nonresting position and adds the forces at play: gravity and tension.
+When the pendulum swings, its arm and bob are essentially rotating around the fixed point of the pivot. If no arm connected the bob and the pivot, the bob would simply fall to the ground under the influence of gravity. Obviously, that isn’t what happens. Instead, the fixed length of the arm creates the second force—tension. However, I’m not going to work with this scenario according to these forces, at least not in the way I approached the spring scenario.
-When the pendulum swings, its arm and bob are essentially rotating around the fixed point of the pivot. If no arm connected the bob and the pivot, the bob would simply fall to the ground under the influence of gravity. Obviously, that isn’t what happens. Instead, the fixed length of the arm creates the second force—tension. However, I’m not going to work with this scenario according to these forces, at least not in the way I approached the spring scenario.
Instead of using linear acceleration and velocity, I’m going to describe the motion of the pendulum in terms of angular acceleration and angular velocity, which refer to the change of the arm’s angle \theta relative to the pendulum’s resting position. I should first warn you, especially if you’re a seasoned physicist, that I’m going to conveniently ignore several important concepts here: conservation of energy, momentum, centripetal force, and more. This isn’t intended to be a comprehensive description of pendulum physics. My goal is to offer you an opportunity to practice your new skills in trigonometry and further explore the relationship between forces and angles through a concrete example.
To calculate the pendulum’s angular acceleration, I’m going to use Newton’s second law of motion but with a little trigonometric twist. Take a look back at Figure 3.19 and tilt your head so that the pendulum’s arm becomes the vertical axis. The force of gravity suddenly points askew, a little to the left—it’s at an angle with respect to your tilted head. If this is starting to hurt your neck, don’t worry. I’ll redraw the tilted figure and relabel the forces F_g for gravity and T for tension (Figure 3.20, left).
Note that the acceleration calculation now includes a multiplication by –1. When the pendulum is to the right of its resting position, the angle is positive, and so the sine of the angle is also positive. However, gravity should pull the bob back toward the resting position. Conversely, when the pendulum is to the left of its resting position, the angle is negative, and so its sine is negative too. In this case, the pulling force should be positive. Multiplying by –1 is necessary in both scenarios.
-Next, I need a
+show()
method to draw the pendulum on the canvas. But where exactly should I draw it? How do I calculate the x- and y-coordinates (Cartesian!) for both the pendulum’s pivot point (let’s call itpivot
) and bob position (let’s call itbob
)? This may be getting a little tiresome, but the answer, yet again, is trigonometry, as shown in Figure 3.22.Next, I need a
show()
method to draw the pendulum on the canvas. But where exactly should I draw it? How do I calculate the x- and y-coordinates (Cartesian!) for both the pendulum’s pivot point (let’s call itpivot
) and bob position (let’s call itbob
)? This may be getting a little tiresome, but the answer, yet again, is trigonometry, as shown in Figure 3.19.First, I’ll need to add a
this.pivot
property to the constructor to specify where to draw the pendulum on the canvas:this.pivot = createVector(100, 10);I know the bob should be a set distance away from the pivot, as determined by the arm length. That’s my variable
diff --git a/content/04_particles.html b/content/04_particles.html index c900a139..e4a223ac 100644 --- a/content/04_particles.html +++ b/content/04_particles.html @@ -451,7 +451,7 @@r
, which I’ll set now:A System of Emitters
You click the mouse and generate a particle system at the mouse’s position (Figure 4.3).
You keep clicking the mouse. Each time, another particle system springs up where you clicked (Figure 4.4).
Then in
@@ -348,7 +348,7 @@draw()
, allKochLine
objects (just one for now) can be rendered with afor...of
loop:The Monster Curve
function generate() { let next = []; for (let segment of segments) { - //{!5} A KochLine needs a method that returns all five points computed according to the Koch rules. + //{!5} AKochLine
needs a method that returns all five points computed according to the Koch rules. let [a, b, c, d, e] = segment.kochPoints(); next.push(new KochLine(a, b)); @@ -365,7 +365,7 @@The Monster Curve
Now I just need to write a new
kochPoints()
method in theKochLine
class that returns an array ofp5.Vector
objects representing the points a through e in Figure 8.15. I’ll knock offa
ande
first, which are the easiest: they’re just copies of thestart
andend
points of the original line:kochPoints() { - //{!1} Note the use of copy(). As discussed in Chapter 5, it’s best to avoid making copies whenever + //{!1} Note the use ofcopy()
. As discussed in Chapter 5, it’s best to avoid making copies whenever // possible, but here a new object is needed in case the segments need to move // independently of each other. let a = this.start.copy(); @@ -386,7 +386,7 @@The Monster Curve
//{!1} Add that vector to the beginning of the line to find the new point. let b = p5.Vector.add(a, v); - // d is just another one-third of the way past b! + //d
is just another one-third of the way pastb
! let d = p5.Vector.add(b, v);@@ -399,7 +399,7 @@The Monster Curve
//{!1} Rotate by –PI/3 radians (negative angle so it rotates “up”). v.rotate(-PI / 3); - //{!1} Move along from b by v to get to point c. + //{!1} Move along fromb
byv
to get to pointc
. let c = p5.Vector.add(b, v);Finally, after calculating the five points, I can return them all together in an array. This will match the code for destructuring the array into five separate variables, as previously outlined:
@@ -542,7 +542,7 @@Exercise 8.6
if (len > 2) { push(); rotate(angle); - //{!1} Subsequent calls to branch() include the length argument. + //{!1} Subsequent calls tobranch()
include the length argument. branch(len); pop(); @@ -568,7 +568,7 @@Example 8.6: A Recursive Tree
function draw() { background(255); - // Map the angle to range from 0 to 90° (HALF_PI) according to mouseX. + // Map the angle to range from 0 to 90° (HALF_PI
) according tomouseX
. angle = map(mouseX, 0, width, 0, HALF_PI); // Start the tree from the bottom of the canvas. @@ -646,7 +646,7 @@L-systems
// The length of a string is stored in its length property. for (let i = 0; i < message.length; i++) { - //{!1} Individual characters can be accessed by an index, just like an array! I'm using charAt(i) instead of [i]. + //{!1} Individual characters can be accessed by an index, just like an array! I'm usingcharAt(i)
instead of `[i]`. let character = message.charAt(i); }An L-system has three main components:
diff --git a/content/09_ga.html b/content/09_ga.html index aae117e0..e1f987dd 100644 --- a/content/09_ga.html +++ b/content/09_ga.html @@ -88,7 +88,7 @@Step 1: Creating a Population
- @@ -117,7 +117,7 @@Genotype +Genotype Phenotype Step 1: Creating a Population
- @@ -153,7 +153,7 @@Same Genotype +Same Genotype Different Phenotype (Line Length) Step 2: Selection
- @@ -179,7 +179,7 @@DNA +DNA Fitness Step 2: Selection
- @@ -212,7 +212,7 @@Element +Element Fitness Step 2: Selection
- Element +Element Fitness Normalized Fitness Expressed as a Percentage @@ -262,7 +262,7 @@Step 2: Selection
- @@ -430,11 +430,11 @@Element +Element DNA Step 2: Selection
let matingPool = []; for (let phrase of population) { - //{!1} n is equal to fitness times 100. + //{!1}n
is equal to fitness times 100. // 100 is an arbitrary way to scale the percentage of fitness to a larger integer value. let n = floor(phrase.fitness * 100); for (let j = 0; j < n; j++) { - //{!1} Add each member of the population to the mating pool n times. + //{!1} Add each member of the population to the mating pooln
times. matingPool.push(phrase); } } @@ -485,7 +485,7 @@Exercise 9.3
- @@ -508,7 +508,7 @@Element +Element Probability Exercise 9.3
- @@ -546,8 +546,8 @@Element +Element Rank Probability Step 3: Reproduction (Crosso child.mutate();
Of course, the
crossover()
andmutate()
methods don’t magically exist in theDNA
class; I have to write them. The way I’ve calledcrossover()
indicates that it should receive an instance ofDNA
as an argument (parentB
) and return a new instance ofDNA
, thechild
:crossover(partner) { - // The child is a new instance of DNA. - // (Note that the genes are generated randomly in the DNA constructor, + // The child is a new instance ofDNA
. + // (Note that the genes are generated randomly in theDNA
constructor, // but the crossover method will override the array.) let child = new DNA(this.genes.length); @@ -629,7 +629,7 @@Example 9.1: Gene let matingPool = []; for (let phrase of population) { - //{!4} Add each member n times according to its fitness score. + //{!4} Add each member
n
times according to its fitness score. let n = floor(phrase.fitness * 100); for (let j = 0; j < n; j++) { matingPool.push(phrase); @@ -646,12 +646,12 @@Example 9.1: Gene child.mutate(mutationRate); //{!1} Note that you are overwriting the population with the new - // children. When draw() loops, you will perform all the same + // children. When
draw()
loops, you will perform all the same // steps with the new population of children. population[i] = child; } - // Step 4: Repeat — go back to the beginning of draw()! + // Step 4: Repeat — go back to the beginning of `draw()`! }The sketch.js file precisely mirrors the steps of the GA. However, most of the functionality called upon is encapsulated in the
DNA
class:@@ -735,8 +735,8 @@Key 1: The Global Variables
- @@ -773,8 +773,8 @@Population Size -Mutation Rate +Phrase +Phrase Number of Generations Until the Phrase Is Solved Total Time (in Seconds) Until the Phrase Is Solved Key 1: The Global Variables
- @@ -814,7 +814,7 @@Population Size -Mutation Rate +Phrase +Phrase Number of Generations Until the Phrase Is Solved Total Time (in Seconds) Until the Phrase Is Solved Key 2: The Fitness Function
@@ -844,7 +844,7 @@ Phrase -Characters Correct +Characters Correct Fitness Key 2: The Fitness Function
- @@ -865,7 +865,7 @@Correct Characters +Correct Characters Fitness Key 2: The Fitness Function
- @@ -938,7 +938,7 @@Correct Characters +Correct Characters Fitness Key 3: The Genotype and Phenotype
class Vehicle { constructor() { - //{!1} A DNA object embedded into the Vehicle class + //{!1} ADNA
object embedded into theVehicle
class this.dna = new DNA(4); //{!4} Use the genes to set variables. this.maxspeed = dna.genes[0]; @@ -1079,7 +1079,7 @@Developing the Rockets
this.genes = []; // How strong can the thrusters be? this.maxForce = 0.1; - // Notice that the length of genes is equal to a global lifeSpan variable. + // Notice that the length of genes is equal to a globallifeSpan
variable. for (let i = 0; i < lifeSpan; i++) { this.genes[i] = p5.Vector.random2D(); //{!1} Scale the vectors randomly, but no stronger than the maximum force. @@ -1109,7 +1109,7 @@Developing the Rockets
this.dna = dna; // A rocket has fitness. this.fitness = 0; - //{!1} A counter for the dna genes array + //{!1} A counter for the DNA genes array this.geneCounter = 0; this.position = createVector(x, y); this.velocity = createVector(); @@ -1184,7 +1184,7 @@Managing the Population
live() { for (let rocket of this.population) { - //{!1} The run() method takes care of the simulation, updates the rocket’s + //{!1} Therun()
method takes care of the simulation, updates the rocket’s // position, and draws it to the canvas. rocket.run(); } @@ -1236,11 +1236,11 @@Example 9.2: Smart Rockets
background(255); // The revised GA if (lifeCounter < lifeSpan) { - // Step 2: The rockets live their lives until lifeCounter reaches lifeSpan. + // Step 2: The rockets live their lives untillifeCounter
reacheslifeSpan
. population.live(); lifeCounter++; } else { - // When lifeSpan is reached, reset lifeCounter and evolve the next + // WhenlifeSpan
is reached, resetlifeCounter
and evolve the next // generation (steps 3 and 4, selection and reproduction). lifeCounter = 0; population.fitness(); @@ -1276,7 +1276,7 @@Making Improvements
); }If I create an array of
-Obstacle
objects, I can then have each rocket check to see whether it has collided with each obstacle. If a collision occurs, the rocket can set the Boolean flaghitObstacle
totrue
. To achieve this, I need to add a method to theRocket
class:// This new method lives in the Rocket class and checks whether a rocket has +// This new method lives in theRocket
class and checks whether a rocket has // hit an obstacle. checkObstacles(obstacles) { for (let obstacle of obstacles) { @@ -1411,7 +1411,7 @@Interactive Selection
// The DNA values are assigned to flower properties // such as petal color, petal size, and number of petals. let genes = this.dna.genes; - // I’ll set the RGB range to from 0 to 1 with colorMode() and use map() as needed elsewhere for drawing the flower. + // I’ll set the RGB range to from 0 to 1 withcolorMode()
and usemap()
as needed elsewhere for drawing the flower. let petalColor = color(genes[0], genes[1], genes[2], genes[3]); let petalSize = map(genes[4], 0, 1, 4, 24); let petalCount = floor(map(genes[5], 0, 1, 2, 16)); @@ -1545,7 +1545,7 @@Ecosystem Simulation
update() { // Death is always looming. this.health -= 0.2; - /* All the rest of update() */+ /* All the rest ofupdate()
*/If
health
drops below0
, the bloop dies:// A method to test whether the bloop is alive or dead. @@ -1630,9 +1630,9 @@Selection and Reproduction
Note that I’ve lowered the probability of reproduction from 1 percent to 0.05 percent. This change makes a significant difference; with a high reproduction probability, the system will rapidly become overpopulated. Too low a probability, and everything will likely die out quickly.
Writing the
copy()
method into theDNA
class is easy with the JavaScript array methodslice()
, a standard JavaScript method that makes a new array by copying elements from an existing array:class DNA { - //{!1} This copy() method replaces crossover(). + //{!1} Thiscopy()
method replacescrossover()
. copy() { - // Create new DNA (with random genes). + // Create newDNA
(with random genes). let newDNA = new DNA(); //{!1} Overwrite the random genes with a copy this DNA’s genes. newDNA.genes = this.genes.slice(); @@ -1662,7 +1662,7 @@Example 9.5: An Evolving Ecosystem
World class manages the // population of bloops and all the food. constructor(populationSize) { // Create the population. diff --git a/content/10_nn.html b/content/10_nn.html index 843acd57..0c375922 100644 --- a/content/10_nn.html +++ b/content/10_nn.html @@ -73,7 +73,7 @@The Perceptron
- @@ -95,7 +95,7 @@Input +Phrase Value Step 1: Weight the Inputs
- @@ -114,8 +114,8 @@Weight +Phrase Value Step 1: Weight the Inputs
- @@ -193,8 +193,8 @@Input -Weight +Phrase +Phrase Input \boldsymbol{\times} Weight Simple Pattern Recognitio
- @@ -281,8 +281,8 @@Input Value -Weight +Phrase +Phrase Result The Perceptron Code
- Desired -Guess +Phrase +Phrase Error