From d92352bab73b87840a9cecc6e50d2e19c6742203 Mon Sep 17 00:00:00 2001 From: shiffman Date: Sat, 24 Feb 2024 18:57:18 +0000 Subject: [PATCH] Notion - Update docs --- content/05_steering.html | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/content/05_steering.html b/content/05_steering.html index ee6fe81..9bd26cc 100644 --- a/content/05_steering.html +++ b/content/05_steering.html @@ -288,7 +288,8 @@

Example 5.2: Arriving at a Target

-
  arrive(target) {
+
+
  arrive(target) {
     let desired = p5.Vector.sub(target, this.position);
 
     //{!1} The distance is the magnitude of
@@ -310,6 +311,7 @@ 

Example 5.2: Arriving at a Target

steer.limit(this.maxforce); this.applyForce(steer); }
+

The arrive behavior is a great demonstration of an autonomous agent’s perception of the environment—including its own state. This model differs from the inanimate forces of Chapter 2: a celestial body attracted to another body doesn’t know it is experiencing gravity, whereas a cheetah chasing its prey knows it’s chasing.

The key is in the way the forces are calculated. For instance, in the gravitational attraction sketch (Example 2.6), the force always points directly from the object to the target—the exact direction of the desired velocity. Here, by contrast, the vehicle perceives its distance to the target and adjusts its desired speed accordingly, slowing as it gets closer. The force on the vehicle itself is therefore based not just on the desired velocity but also on the desired velocity relative to its current velocity. The vehicle accounts for its own state as part of its assessment of the environment.

Put another way, the magic of Reynolds’s desired minus velocity equation is that it essentially makes the steering force a manifestation of the current velocity’s error: “I’m supposed to be going this fast in this direction, but I’m actually going this fast in another direction. My error is the difference between where I want to go and where I’m currently going.” Sometimes this can lead to seemingly unexpected results, as in Figure 5.10.

@@ -575,8 +577,10 @@

The Dot Product

Assume vectors \vec{A} and \vec{B}:

\vec{A}=(a_x,a_y)
\vec{B}=(b_x,b_y)
-

The formula for the dot product (represented by the \cdot character) is as follows:

-
\vec{A}\cdot\vec{B}=(a_x\times b_x) + (a_y\times b_y)
+
+

The formula for the dot product (represented by the \cdot character) is as follows:

+
\vec{A}\cdot\vec{B}=(a_x\times b_x) + (a_y\times b_y)
+

Crucially, the result of the dot product is a scalar value (a single number) and not a vector, even though the inputs are two vectors. For example, say you have these two vectors:

\vec{A}=(-3,5)
\vec{B}=(10,1)
@@ -598,9 +602,11 @@

The Dot Product

In other words, the dot product of \vec{A} and \vec{B} is equal to the magnitude of \vec{A} times the magnitude of \vec{B} times the cosine of theta (with theta being the angle between the two vectors \vec{A} and \vec{B}).

The two dot-product formulas can be derived from each other with trigonometry, but I’m happy not to follow that path and instead just operate on the following assumption:

||\vec{A}||\times||\vec{B}||\times\cos(\theta)=(a_x\times b_x) + (a_y\times b_y)
-

This works since both sides of the equation equal \vec{A} \cdot \vec{B}. What does that assumption do for me? Say I have two vectors \vec{A} and \vec{B}:

-
\vec{A}=(10,2)
-
\vec{B}=(4,-3)
+
+

This works since both sides of the equation equal \vec{A} \cdot \vec{B}. What does that assumption do for me? Say I have two vectors \vec{A} and \vec{B}:

+
\vec{A}=(10,2)
+
\vec{B}=(4,-3)
+
Figure 5.18: The angle between two vectors \vec{A} and \vec{B} @@ -758,7 +764,6 @@

Example 5.5: Creating a Path Object

I can encode that logic with a simple if statement and use my earlier seek() method to steer the vehicle when necessary:

let distance = p5.Vector.dist(future, normalPoint);
-
 // If the vehicle is outside the path, seek the target.
 if (distance > path.radius) {
   //{!1} The desired velocity and steering force can use the seek() method created in Example 5.1.
@@ -1279,7 +1284,8 @@ 

Flocking

Figure 5.35: The example vehicle (bold) interacts with only the vehicles within its neighborhood (the circle).

I already applied similar logic when I implemented separation, calculating a force based only on other vehicles within a certain distance. Now I want to do the same for alignment (and eventually, cohesion):

-
  align(boids) {
+
+
  align(boids) {
     //{!1} This is an arbitrary value that could vary from boid to boid.
     let neighborDistance = 50;
     let sum = createVector(0, 0);
@@ -1303,6 +1309,7 @@ 

Flocking

return createVector(0, 0); } }
+

As with the separate() method, I’ve included the condition this !== other to ensure that a boid doesn’t consider itself when calculating the average velocity. It would probably work regardless, but having each boid constantly be influenced by its own velocity could lead to a feedback loop that would disrupt the overall behavior.

Exercise 5.15

@@ -1513,9 +1520,11 @@

More Optimization Tricks

Each of these tips is detailed next.

Use the Magnitude Squared

What is magnitude squared, and when should you use it? Think back to how the magnitude of a vector is calculated:

-
function mag() {
+
+
function mag() {
   return sqrt(x * x + y * y);
 }
+

Magnitude requires the square-root operation. And so it should! After all, if you want the magnitude of a vector, you have to break out the Pythagorean theorem (we did this in Chapter 1). However, if you could somehow skip taking the square root, your code would run faster.

Say you just want to know the relative magnitude of a vector v. For example, is the magnitude greater than 10?

if (v.mag() > 10) {
@@ -1532,11 +1541,13 @@ 

Use the Magnitude Squared

It’s calculated the same as magnitude, but without the square root. In the case of a single vector, using magSq() rather than mag() will never significantly improve the performance of a p5.js sketch. However, if you’re computing the magnitude of thousands of vectors each time through draw(), working with the magnitude squared could help your code run a wee bit faster.

Calculate Sine and Cosine Lookup Tables

Taking the square root isn’t the only mathematical function that’s slow to compute. Trig functions like sine, cosine, and tangent are also slow. If you just need an individual sine or cosine value here or there in your code, you’re never going to run into a problem. But what if you had something like this?

-
function draw() {
+
+
function draw() {
   for (let i = 0; i < 10000; i++) {
     print(sin(PI));
   }
 }
+

Sure, this is a totally ridiculous code snippet that you would never write. But it illustrates a certain point: if you’re calculating the sine of pi 10,000 times, why not just calculate it once, save that value, and refer to it whenever necessary?

This is the principle behind sine and cosine lookup tables. Instead of calling the sine and cosine functions in your code whenever you need them, you can build an array that stores the results of sine and cosine at angles from 0 to 2\pi, and then just look up the precalculated values when you need them. For example, here are two arrays that store the sine and cosine values for every integer angle from 0 to 359 degrees. I’ll use angleMode(DEGREES) here to simplify the discussion, but the same technique can be applied with radians:

angleMode(DEGREES);