Skip to content

Commit

Permalink
Merge pull request #621 from nature-of-code/notion-update-docs
Browse files Browse the repository at this point in the history
updated illustrations and code comment fixes
  • Loading branch information
shiffman authored Jan 7, 2024
2 parents 9ec0053 + f956d27 commit 95651e8
Show file tree
Hide file tree
Showing 73 changed files with 63 additions and 63 deletions.
10 changes: 5 additions & 5 deletions content/00_randomness.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,15 @@ <h3 id="coding-conventions">Coding Conventions</h3>
<pre class="codesplit" data-code-language="javascript">// A Walker object
let walker;</pre>
<p>Then create the object in <code>setup()</code> by referencing the class name with the <code>new</code> operator:</p>
<pre class="codesplit" data-code-language="javascript">//{!1} Remember how p5.js works? setup() is executed once when the sketch starts.
<pre class="codesplit" data-code-language="javascript">//{!1} Remember how p5.js works? <code>setup()</code> is executed once when the sketch starts.
function setup() {
createCanvas(640, 240);
// Create the walker.
walker = new Walker();
background(255);
}</pre>
<p>Finally, during each cycle through <code>draw()</code>, the walker takes a step and draws a dot:</p>
<pre class="codesplit" data-code-language="javascript">//{!1} Then draw() loops forever and ever (until you quit).
<pre class="codesplit" data-code-language="javascript">//{!1} Then <code>draw()</code> loops forever and ever (until you quit).
function draw() {
// Call functions on the walker.
walker.step();
Expand Down Expand Up @@ -473,7 +473,7 @@ <h2 id="a-smoother-approach-with-perlin-noise">A Smoother Approach with Perlin N
let x = random(0, width);
circle(x, 180, 16);</pre>
<p>Now, instead of a random x-position, you want a smoother Perlin noise x-position. You might think that all you need to do is replace <code>random()</code> with an identical call to <code>noise()</code>, like so:</p>
<pre class="codesplit" data-code-language="javascript">// Replace random() with noise()?
<pre class="codesplit" data-code-language="javascript">// Replace <code>random()</code> with noise()?
<s>let x = random(0, width);</s>
// Tempting, but this is not correct!
let x = noise(0, width);
Expand Down Expand Up @@ -547,7 +547,7 @@ <h3 id="noise-ranges">Noise Ranges</h3>

function draw() {
let n = noise(t);
//{!1} Use map() to customize the range of Perlin noise.
//{!1} Use <code>map()</code> to customize the range of Perlin noise.
let x = map(n, 0, 1, 0, width);
ellipse(x, 180, 16, 16);

Expand Down Expand Up @@ -634,7 +634,7 @@ <h3 id="noise-detail">Noise Detail</h3>
<strong>let yoff = 0.0;</strong>

for (let y = 0; y &#x3C; height; y++) {
// Use xoff and yoff for noise().
// Use xoff and yoff for <code>noise()</code>.
<strong>let bright = map(noise(xoff, yoff), 0, 1, 0, 255);</strong>
// Use x and y for the pixel position.
let index = (x + y * width) * 4;
Expand Down
16 changes: 8 additions & 8 deletions content/01_vectors.html
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ <h3 id="example-12-bouncing-ball-with-vectors">Example 1.2: Bouncing Ball with V

function setup() {
createCanvas(640, 240);
// Note that createVector() has to be called inside setup().
// Note that <code>createVector()</code> has to be called inside <code>setup()</code>.
<strong>position = createVector(100, 100);
velocity = createVector(2.5, 2);</strong>
}
Expand All @@ -285,7 +285,7 @@ <h3 id="example-12-bouncing-ball-with-vectors">Example 1.2: Bouncing Ball with V
background(255);
<strong>position.add(velocity);</strong>

// You still sometimes need to refer to the individual components of a p5.Vector and can do so using the dot syntax: position.x, velocity.y, and so forth.
// You still sometimes need to refer to the individual components of a p5.Vector and can do so using the dot syntax: <code>position.x</code>, <code>velocity.y</code>, and so forth.
<strong>if (position.x > width || position.x &#x3C; 0) {
velocity.x = velocity.x * -1;
}
Expand Down Expand Up @@ -481,7 +481,7 @@ <h3 id="example-13-vector-subtraction">Example 1.3: Vector Subtraction</h3>
mouse.sub(center);

// Draw a line to represent the result of subtraction.
// Notice that I move the origin with translate() to place the vector.
// Notice that I move the origin with <code>translate()</code> to place the vector.
stroke(0);
translate(width / 2, height / 2);
line(0, 0, mouse.x, mouse.y);
Expand Down Expand Up @@ -833,7 +833,7 @@ <h3 id="algorithm-1-constant-acceleration">Algorithm 1: Constant Acceleration</h
<pre class="codesplit" data-code-language="javascript"> this.acceleration = createVector(-0.001, 0.01);</pre>
<p>This means that for every frame of the animation, the object’s velocity should increase by –0.001 pixels in the x-direction and 0.01 pixels in the y-direction. Maybe you’re thinking, “Gosh, those values seem awfully small!” Indeed, they are quite tiny, but that’s by design. Acceleration values accumulate over time in the velocity, about 30 times per second, depending on the sketch’s frame rate. To keep the magnitude of the velocity vector from growing too quickly and spiraling out of control, the acceleration values should remain quite small.</p>
<p>I can also help keep the velocity within a reasonable range by incorporating the <code>p5.Vector</code> function <code>limit()</code>, which puts a cap on the magnitude of a vector:</p>
<pre class="codesplit" data-code-language="javascript"> // The limit() function constrains the magnitude of a vector.
<pre class="codesplit" data-code-language="javascript"> // The <code>limit()</code> function constrains the magnitude of a vector.
this.velocity.limit(10);</pre>
<p>This translates to the following:</p>
<p>What is the magnitude of <code>velocity</code>? If it’s less than 10, no worries; just leave it as is. If it’s more than 10, however, reduce it to 10!</p>
Expand Down Expand Up @@ -872,10 +872,10 @@ <h3 id="example-18-motion-101-velocity-and-constant-acceleration">Example 1.8: M
this.position.add(this.velocity);
}

// show() is the same.
// <code>show()</code> is the same.
show() {}

// checkEdges() is the same.
// <code>checkEdges()</code> is the same.
checkEdges() {}
}</pre>
<p>The net result is that the object falls down and to the left, gradually getting faster and faster until it reaches the maximum velocity.</p>
Expand All @@ -886,7 +886,7 @@ <h3 id="exercise-15">Exercise 1.5</h3>
<h3 id="algorithm-2-random-acceleration">Algorithm 2: Random Acceleration</h3>
<p>Now on to Acceleration Algorithm 2, a random acceleration. In this case, instead of initializing <code>acceleration</code> in the object’s constructor, I want to randomly set its value inside the <code>update()</code> method. This way, the object will get a different acceleration vector for every frame of the animation:</p>
<pre class="codesplit" data-code-language="javascript">update() {
// The random2D() function returns a unit vector pointing in a random direction.
// The <code>random2D()</code> function returns a unit vector pointing in a random direction.
this.acceleration = p5.Vector.random2D();

this.velocity.add(this.acceleration);
Expand Down Expand Up @@ -995,7 +995,7 @@ <h3 id="algorithm-3-interactive-motion">Algorithm 3: Interactive Motion</h3>
</ul>
<p>Let’s implement that by using <code>p5.Vector</code> syntax. Assuming the code will live inside the <code>Mover</code> class and thus have access to the object’s <code>position</code>, I can write this:</p>
<pre class="codesplit" data-code-language="javascript">let mouse = createVector(mouseX, mouseY);
// Look! I’m using the static reference to sub() because I want a new p5.Vector!
// Look! I’m using the static reference to <code>sub()</code> because I want a new p5.Vector!
let direction = p5.Vector.sub(mouse, this.position);</pre>
<p>I’ve used the static version of <code>sub()</code> to create a new vector <code>direction</code> that points from the mover’s position to the mouse. If the object were to actually accelerate using that vector, however, it would appear instantaneously at the mouse position, since the magnitude of <code>direction</code> is equal to the distance between the object and the mouse. This wouldn’t make for a smooth animation, of course. The next step, therefore, is to decide how quickly the object should accelerate toward the mouse by changing the vector’s magnitude.</p>
<p>To set the magnitude (whatever it may be) of the acceleration vector, I must first ______ the vector. That’s right, you said it: <em>normalize</em>! If I can shrink the vector to its unit vector (of length 1), I can easily scale it to any other value, because 1 multiplied by anything equals anything:</p>
Expand Down
6 changes: 3 additions & 3 deletions content/02_forces.html
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ <h3 id="example-24-including-friction">Example 2.4: Including Friction</h3>
<strong>mover.applyForce(friction);</strong>
}

//{!1} Call the new bounceEdges() method.
//{!1} Call the new <code>bounceEdges()</code> method.
mover.bounceEdges();
mover.update();
mover.show();
Expand Down Expand Up @@ -822,7 +822,7 @@ <h3 id="gravitational-attraction">Gravitational Attraction</h3>
<p>And I’m done. Sort of. Almost. I need to work out one small kink. Look at the code for the <code>attract()</code> method again. See that slash symbol for division? Whenever you have one of those, you should ask yourself this question: What would happen if the distance happened to be a really, really small number, or (even worse!) 0? You can’t divide a number by 0, and if you were to divide a number by something tiny like 0.0001, that’s the equivalent of multiplying that number by 10,000! That may be a viable outcome of this formula for gravitational attraction in the real world, but p5.js isn’t the real world. In the p5.js world, the mover could end up being very, very close to the attractor, and the resulting force could be so strong that the mover flies way off the canvas.</p>
<p>Conversely, what if the mover were to be, say, 500 pixels from the attractor (not unreasonable in p5.js)? You’re squaring the distance, so this will result in dividing the force by 250,000. That force might end up being so weak that it’s almost as if it’s not applied at all.</p>
<p>To avoid both extremes, it’s practical to constrain the range of <code>distance</code> before feeding it into the formula. Maybe, no matter where the <code>Mover</code> <em>actually</em> is, you should never consider it to be less than 5 pixels or more than 25 pixels away from the attractor, for the purposes of calculating the force of gravitational attraction:</p>
<pre class="codesplit" data-code-language="javascript"> // Here the constrain() function limits the value of distance between a minimum (5) and maximum (25).
<pre class="codesplit" data-code-language="javascript"> // Here the <code>constrain()</code> function limits the value of distance between a minimum (5) and maximum (25).
distance = constrain(distance, 5, 25);</pre>
<p>Ultimately, it’s up to you to choose the behaviors you want from your simulation. But if you decide you want a reasonable-looking attraction that’s never absurdly weak or strong, constraining the distance is a good technique.</p>
<p>The <code>Mover</code> class hasn’t changed at all, so let’s just look at the main sketch and the <code>Attractor</code> class as a whole, adding a variable <code>G</code> for the universal gravitational constant. (On the book’s website, you’ll find that this example also has code that allows you to move the <code>Attractor</code> object with the mouse.)</p>
Expand Down Expand Up @@ -946,7 +946,7 @@ <h2 id="the-n-body-problem">The n-Body Problem</h2>

/* All the other stuff from before */

// The attract() method is now part of the Body class.
// The <code>attract()</code> method is now part of the Body class.
attract(body) {
let force = p5.Vector.sub(this.position, body.position);
let d = constrain(force.mag(), 5, 25);
Expand Down
26 changes: 13 additions & 13 deletions content/03_oscillation.html
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ <h3 id="example-31-angular-motion-using-rotate">Example 3.1: Angular Motion Usin
circle(60, 0, 16);
circle(-60, 0, 16);

// Angular equivalent of velocity.add(acceleration)
// Angular equivalent of <code>velocity.add</code>(acceleration)
angleVelocity += angleAcceleration;
// Angular equivalent of position.add(velocity)
// Angular equivalent of <code>position.add</code>(velocity)
angle += angleVelocity;
}</pre>
<p>Instead of incrementing <code>angle</code> by a fixed amount to steadily rotate the baton, for every frame I add <code>angleAcceleration</code> to <code>angleVelocity</code>, then add <code>angleVelocity</code> to <code>angle</code>. As a result, the baton starts with no rotation and then spins faster and faster as the angular velocity accelerates.</p>
Expand Down Expand Up @@ -151,15 +151,15 @@ <h3 id="exercise-32">Exercise 3.2</h3>
stroke(0);
fill(175, 200);

// Use push() to save the current state so the rotation of this shape doesn’t affect the rest of the world.
// Use <code>push()</code> to save the current state so the rotation of this shape doesn’t affect the rest of the world.
push();
// Set the origin at the shape’s position.
translate(this.position.x, this.position.y);
//{!1} Rotate by the angle.
rotate(this.angle);
circle(0, 0, this.radius * 2);
line(0, 0, this.radius, 0);
// Use pop() to restore the previous state after rotation is complete.
// Use <code>pop()</code> to restore the previous state after rotation is complete.
pop();
}</pre>
<p>At this point, if you were to actually go ahead and create a <code>Mover</code> object, you wouldn’t see it behave any differently. This is because the angular acceleration is initialized to zero (<code>this.angleAcceleration = 0;</code>). For the object to rotate, it needs a nonzero acceleration! Certainly, one option is to hardcode a number in the constructor:</p>
Expand All @@ -184,7 +184,7 @@ <h3 id="example-32-forces-with-arbitrary-angular-motion">Example 3.2: Forces wit
//{!1} Calculate angular acceleration according to acceleration’s x-component.
this.angleAcceleration = this.acceleration.x / 10.0;
this.angleVelocity += this.angleAcceleration;
//{!1} Use constrain() to ensure that angular velocity doesn’t spin out of control.
//{!1} Use <code>constrain()</code> to ensure that angular velocity doesn’t spin out of control.
this.angleVelocity = constrain(this.angleVelocity, -0.1, 0.1);
this.angle += this.angleVelocity;

Expand Down Expand Up @@ -259,7 +259,7 @@ <h2 id="pointing-in-the-direction-of-movement">Pointing in the Direction of Move
</table>
<p>Now that I have the formula, let’s see where it should go in the <code>Mover</code> class’s <code>show()</code> method to make the mover (now drawn as a rectangle) point in its direction of motion. Note that in p5.js, the function for inverse tangent is <code>atan()</code>:</p>
<pre class="codesplit" data-code-language="javascript"> show() {
// Solve for the angle by using atan().
// Solve for the angle by using <code>atan()</code>.
let angle = atan(this.velocity.y / this.velocity.x);

stroke(0);
Expand Down Expand Up @@ -291,7 +291,7 @@ <h3 id="example-33-pointing-in-the-direction-of-motion">Example 3.3: Pointing in
</figure>
</div>
<pre class="codesplit" data-code-language="javascript"> show() {
// Use atan2() to account for all possible directions.
// Use <code>atan2()</code> to account for all possible directions.
let angle = atan2(this.velocity.y, this.velocity.x);

stroke(0);
Expand Down Expand Up @@ -354,7 +354,7 @@ <h3 id="example-34-polar-to-cartesian">Example 3.4: Polar to Cartesian</h3>
// Translate the origin point to the center of the screen.
translate(width / 2, height / 2);

// Polar coordinates (r, theta) are converted to Cartesian (x, y) for use in the circle() function.
// Polar coordinates (r, theta) are converted to Cartesian (x, y) for use in the <code>circle()</code> function.
let x = r * cos(theta);
let y = r * sin(theta);

Expand Down Expand Up @@ -627,7 +627,7 @@ <h3 id="example-38-static-wave">Example 3.8: Static Wave</h3>
for (let x = 0; x &#x3C;= width; x += 24) {
// Step 1: Calculate the y-position according to amplitude and sine of the angle.
let y = amplitude * sin(angle);
// Step 2: Draw a circle at the (<em>x</em>, <em>y</em>) position.
// Step 2: Draw a circle at the <code>(x, y)</code> position.
circle(x, y + height / 2, 48);
// Step 3: Increment the angle according to the delta angle.
angle += deltaAngle;
Expand Down Expand Up @@ -669,7 +669,7 @@ <h3 id="example-39-the-wave">Example 3.9: The Wave</h3>
function draw() {
background(255);

// Each time through draw(), the angle that increments is set to startAngle.
// Each time through <code>draw()</code>, the angle that increments is set to startAngle.
let angle = startAngle;

for (let x = 0; x &#x3C;= width; x += 24) {
Expand Down Expand Up @@ -767,7 +767,7 @@ <h2 id="spring-forces">Spring Forces</h2>
<strong>let springForce = _______________????
bob.applyForce(</strong><strong>spring</strong><strong>Force);</strong>

// The standard update() and show() methods
// The standard <code>update()</code> and <code>show()</code> methods
bob.update();
bob.show();
}</pre>
Expand Down Expand Up @@ -809,7 +809,7 @@ <h2 id="spring-forces">Spring Forces</h2>
<pre class="codesplit" data-code-language="javascript"> connect(bob) {
let force = some fancy calculations

// The connect() method takes care of calling applyForce() and therefore doesn’t have to return a vector to the calling area.
// The <code>connect()</code> method takes care of calling <code>applyForce()</code> and therefore doesn’t have to return a vector to the calling area.
bob.applyForce(force);
}</pre>
<p>Why do it one way with the <code>Attractor</code> class and another way with the <code>Spring</code> class? When I first discussed forces, showing all the forces being applied in the <code>draw()</code> loop was a clearer way to help you learn about force accumulation. Now that you’re more comfortable, perhaps it’s simpler to embed some of the details inside the objects themselves.</p>
Expand Down Expand Up @@ -1044,7 +1044,7 @@ <h3 id="example-311-swinging-pendulum">Example 3.11: Swinging Pendulum</h3>
}

show() {
//{!1} Apply polar-to-Cartesian conversion. Instead of creating a new vector each time, I’ll use set() to update the bob’s position.
//{!1} Apply polar-to-Cartesian conversion. Instead of creating a new vector each time, I’ll use <code>set()</code> to update the bob’s position.
this.bob.set(this.r * sin(this.angle), this.r * cos(this.angle));
this.bob.add(this.pivot);

Expand Down
Loading

0 comments on commit 95651e8

Please sign in to comment.