diff --git a/examples/curated/43_Flocking.js b/examples/curated/43_Flocking.js
index 832b1d60..f3c87aec 100644
--- a/examples/curated/43_Flocking.js
+++ b/examples/curated/43_Flocking.js
@@ -1,230 +1,258 @@
-/*
+/**
* @name Flocking
- * @arialabel Groups of little grey triangles moving across a darker grey background
- * @description Demonstration of Craig Reynolds' "Flocking" behavior.
- * See: http://www.red3d.com/cwr/
- * Rules: Cohesion, Separation, Alignment
- * (from natureofcode.com).
- * Drag mouse to add boids into the system.
+ * @description Demonstration of flocking behavior.
+ * Full discussion of the implementation can be found in the
+ *
+ * Nature of Code
+ * book by Daniel Shiffman. The simulation is based on the research of
+ * Craig Reynolds, who
+ * used the term 'boid' to represent a bird-like object.
*/
-
let flock;
function setup() {
createCanvas(640, 360);
- createP("Drag the mouse to generate new boids.");
+ createP('Drag the mouse to generate new boids.');
flock = new Flock();
+
// Add an initial set of boids into the system
for (let i = 0; i < 100; i++) {
- let b = new Boid(width / 2,height / 2);
+ let b = new Boid(width / 2, height / 2);
flock.addBoid(b);
}
+
+ describe(
+ 'A group of bird-like objects, represented by triangles, moving across the canvas, modeling flocking behavior.'
+ );
}
function draw() {
- background(51);
+ background(0);
flock.run();
}
-// Add a new boid into the System
+// On mouse drag, add a new boid to the flock
function mouseDragged() {
flock.addBoid(new Boid(mouseX, mouseY));
}
-// The Nature of Code
-// Daniel Shiffman
-// http://natureofcode.com
+// Flock class to manage the array of all the boids
+class Flock {
+ constructor() {
+ // Initialize the array of boids
+ this.boids = [];
+ }
-// Flock object
-// Does very little, simply manages the array of all the boids
+ run() {
+ for (let boid of this.boids) {
+ // Pass the entire list of boids to each boid individually
+ boid.run(this.boids);
+ }
+ }
-function Flock() {
- // An array for all the boids
- this.boids = []; // Initialize the array
+ addBoid(b) {
+ this.boids.push(b);
+ }
}
-Flock.prototype.run = function() {
- for (let i = 0; i < this.boids.length; i++) {
- this.boids[i].run(this.boids); // Passing the entire list of boids to each boid individually
+class Boid {
+ constructor(x, y) {
+ this.acceleration = createVector(0, 0);
+ this.velocity = createVector(random(-1, 1), random(-1, 1));
+ this.position = createVector(x, y);
+ this.size = 3.0;
+
+ // Maximum speed
+ this.maxSpeed = 3;
+
+ // Maximum steering force
+ this.maxForce = 0.05;
+ colorMode(HSB);
+ this.color = color(random(256), 255, 255);
}
-}
-Flock.prototype.addBoid = function(b) {
- this.boids.push(b);
-}
+ run(boids) {
+ this.flock(boids);
+ this.update();
+ this.borders();
+ this.render();
+ }
-// The Nature of Code
-// Daniel Shiffman
-// http://natureofcode.com
+ applyForce(force) {
+ // We could add mass here if we want A = F / M
+ this.acceleration.add(force);
+ }
-// Boid class
-// Methods for Separation, Cohesion, Alignment added
+ // We accumulate a new acceleration each time based on three rules
+ flock(boids) {
+ let separation = this.separate(boids);
+ let alignment = this.align(boids);
+ let cohesion = this.cohesion(boids);
-function Boid(x, y) {
- this.acceleration = createVector(0, 0);
- this.velocity = createVector(random(-1, 1), random(-1, 1));
- this.position = createVector(x, y);
- this.r = 3.0;
- this.maxspeed = 3; // Maximum speed
- this.maxforce = 0.05; // Maximum steering force
-}
+ // Arbitrarily weight these forces
+ separation.mult(1.5);
+ alignment.mult(1.0);
+ cohesion.mult(1.0);
-Boid.prototype.run = function(boids) {
- this.flock(boids);
- this.update();
- this.borders();
- this.render();
-}
+ // Add the force vectors to acceleration
+ this.applyForce(separation);
+ this.applyForce(alignment);
+ this.applyForce(cohesion);
+ }
-Boid.prototype.applyForce = function(force) {
- // We could add mass here if we want A = F / M
- this.acceleration.add(force);
-}
+ // Method to update location
+ update() {
+ // Update velocity
+ this.velocity.add(this.acceleration);
-// We accumulate a new acceleration each time based on three rules
-Boid.prototype.flock = function(boids) {
- let sep = this.separate(boids); // Separation
- let ali = this.align(boids); // Alignment
- let coh = this.cohesion(boids); // Cohesion
- // Arbitrarily weight these forces
- sep.mult(1.5);
- ali.mult(1.0);
- coh.mult(1.0);
- // Add the force vectors to acceleration
- this.applyForce(sep);
- this.applyForce(ali);
- this.applyForce(coh);
-}
+ // Limit speed
+ this.velocity.limit(this.maxSpeed);
+ this.position.add(this.velocity);
-// Method to update location
-Boid.prototype.update = function() {
- // Update velocity
- this.velocity.add(this.acceleration);
- // Limit speed
- this.velocity.limit(this.maxspeed);
- this.position.add(this.velocity);
- // Reset accelertion to 0 each cycle
- this.acceleration.mult(0);
-}
+ // Reset acceleration to 0 each cycle
+ this.acceleration.mult(0);
+ }
-// A method that calculates and applies a steering force towards a target
-// STEER = DESIRED MINUS VELOCITY
-Boid.prototype.seek = function(target) {
- let desired = p5.Vector.sub(target,this.position); // A vector pointing from the location to the target
- // Normalize desired and scale to maximum speed
- desired.normalize();
- desired.mult(this.maxspeed);
- // Steering = Desired minus Velocity
- let steer = p5.Vector.sub(desired,this.velocity);
- steer.limit(this.maxforce); // Limit to maximum steering force
- return steer;
-}
+ // A method that calculates and applies a steering force towards a target
+ // STEER = DESIRED MINUS VELOCITY
+ seek(target) {
+ // A vector pointing from the location to the target
+ let desired = p5.Vector.sub(target, this.position);
-Boid.prototype.render = function() {
- // Draw a triangle rotated in the direction of velocity
- let theta = this.velocity.heading() + radians(90);
- fill(127);
- stroke(200);
- push();
- translate(this.position.x, this.position.y);
- rotate(theta);
- beginShape();
- vertex(0, -this.r * 2);
- vertex(-this.r, this.r * 2);
- vertex(this.r, this.r * 2);
- endShape(CLOSE);
- pop();
-}
+ // Normalize desired and scale to maximum speed
+ desired.normalize();
+ desired.mult(this.maxSpeed);
-// Wraparound
-Boid.prototype.borders = function() {
- if (this.position.x < -this.r) this.position.x = width + this.r;
- if (this.position.y < -this.r) this.position.y = height + this.r;
- if (this.position.x > width + this.r) this.position.x = -this.r;
- if (this.position.y > height + this.r) this.position.y = -this.r;
-}
+ // Steering = Desired minus Velocity
+ let steer = p5.Vector.sub(desired, this.velocity);
-// Separation
-// Method checks for nearby boids and steers away
-Boid.prototype.separate = function(boids) {
- let desiredseparation = 25.0;
- let steer = createVector(0, 0);
- let count = 0;
- // For every boid in the system, check if it's too close
- for (let i = 0; i < boids.length; i++) {
- let d = p5.Vector.dist(this.position,boids[i].position);
- // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
- if ((d > 0) && (d < desiredseparation)) {
- // Calculate vector pointing away from neighbor
- let diff = p5.Vector.sub(this.position, boids[i].position);
- diff.normalize();
- diff.div(d); // Weight by distance
- steer.add(diff);
- count++; // Keep track of how many
- }
- }
- // Average -- divide by how many
- if (count > 0) {
- steer.div(count);
+ // Limit to maximum steering force
+ steer.limit(this.maxForce);
+ return steer;
}
- // As long as the vector is greater than 0
- if (steer.mag() > 0) {
- // Implement Reynolds: Steering = Desired - Velocity
- steer.normalize();
- steer.mult(this.maxspeed);
- steer.sub(this.velocity);
- steer.limit(this.maxforce);
+ render() {
+ // Draw a triangle rotated in the direction of velocity
+ let theta = this.velocity.heading() + radians(90);
+ fill(this.color);
+ stroke(255);
+ push();
+ translate(this.position.x, this.position.y);
+ rotate(theta);
+ beginShape();
+ vertex(0, -this.size * 2);
+ vertex(-this.size, this.size * 2);
+ vertex(this.size, this.size * 2);
+ endShape(CLOSE);
+ pop();
}
- return steer;
-}
-// Alignment
-// For every nearby boid in the system, calculate the average velocity
-Boid.prototype.align = function(boids) {
- let neighbordist = 50;
- let sum = createVector(0,0);
- let count = 0;
- for (let i = 0; i < boids.length; i++) {
- let d = p5.Vector.dist(this.position,boids[i].position);
- if ((d > 0) && (d < neighbordist)) {
- sum.add(boids[i].velocity);
- count++;
+ // Wraparound
+ borders() {
+ if (this.position.x < -this.size) {
+ this.position.x = width + this.size;
+ }
+
+ if (this.position.y < -this.size) {
+ this.position.y = height + this.size;
+ }
+
+ if (this.position.x > width + this.size) {
+ this.position.x = -this.size;
+ }
+
+ if (this.position.y > height + this.size) {
+ this.position.y = -this.size;
}
}
- if (count > 0) {
- sum.div(count);
- sum.normalize();
- sum.mult(this.maxspeed);
- let steer = p5.Vector.sub(sum, this.velocity);
- steer.limit(this.maxforce);
+
+ // Separation
+ // Method checks for nearby boids and steers away
+ separate(boids) {
+ let desiredSeparation = 25.0;
+ let steer = createVector(0, 0);
+ let count = 0;
+
+ // For every boid in the system, check if it's too close
+ for (let boid of boids) {
+ let distanceToNeighbor = p5.Vector.dist(this.position, boid.position);
+
+ // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
+ if (distanceToNeighbor > 0 && distanceToNeighbor < desiredSeparation) {
+ // Calculate vector pointing away from neighbor
+ let diff = p5.Vector.sub(this.position, boid.position);
+ diff.normalize();
+
+ // Scale by distance
+ diff.div(distanceToNeighbor);
+ steer.add(diff);
+
+ // Keep track of how many
+ count++;
+ }
+ }
+
+ // Average -- divide by how many
+ if (count > 0) {
+ steer.div(count);
+ }
+
+ // As long as the vector is greater than 0
+ if (steer.mag() > 0) {
+ // Implement Reynolds: Steering = Desired - Velocity
+ steer.normalize();
+ steer.mult(this.maxSpeed);
+ steer.sub(this.velocity);
+ steer.limit(this.maxForce);
+ }
return steer;
- } else {
- return createVector(0, 0);
}
-}
-// Cohesion
-// For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
-Boid.prototype.cohesion = function(boids) {
- let neighbordist = 50;
- let sum = createVector(0, 0); // Start with empty vector to accumulate all locations
- let count = 0;
- for (let i = 0; i < boids.length; i++) {
- let d = p5.Vector.dist(this.position,boids[i].position);
- if ((d > 0) && (d < neighbordist)) {
- sum.add(boids[i].position); // Add location
- count++;
+ // Alignment
+ // For every nearby boid in the system, calculate the average velocity
+ align(boids) {
+ let neighborDistance = 50;
+ let sum = createVector(0, 0);
+ let count = 0;
+ for (let i = 0; i < boids.length; i++) {
+ let d = p5.Vector.dist(this.position, boids[i].position);
+ if (d > 0 && d < neighborDistance) {
+ sum.add(boids[i].velocity);
+ count++;
+ }
+ }
+ if (count > 0) {
+ sum.div(count);
+ sum.normalize();
+ sum.mult(this.maxSpeed);
+ let steer = p5.Vector.sub(sum, this.velocity);
+ steer.limit(this.maxForce);
+ return steer;
+ } else {
+ return createVector(0, 0);
}
}
- if (count > 0) {
- sum.div(count);
- return this.seek(sum); // Steer towards the location
- } else {
- return createVector(0, 0);
- }
-}
-
+ // Cohesion
+ // For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
+ cohesion(boids) {
+ let neighborDistance = 50;
+ let sum = createVector(0, 0); // Start with empty vector to accumulate all locations
+ let count = 0;
+ for (let i = 0; i < boids.length; i++) {
+ let d = p5.Vector.dist(this.position, boids[i].position);
+ if (d > 0 && d < neighborDistance) {
+ sum.add(boids[i].position); // Add location
+ count++;
+ }
+ }
+ if (count > 0) {
+ sum.div(count);
+ return this.seek(sum); // Steer towards the location
+ } else {
+ return createVector(0, 0);
+ }
+ }
+} // class Boid
diff --git a/examples/curated/43_Flocking.png b/examples/curated/43_Flocking.png
new file mode 100644
index 00000000..ce947e3d
Binary files /dev/null and b/examples/curated/43_Flocking.png differ