Chapter 6 ( Exercise 6.17)
This exercise consisted in creating moving voids that will follow a specific pattern and three elements of ruling motion that were, separation, alignment and cohesion. But in this specific case there had to be a fourth element of flocking called view, that needed to be incorporated. The exercise demanded to include the view element which consist in moving laterally away the boid that blocks the view.
The experience by doing this exercise was really hard because there were many different things and types of voids that I had to incorporate and apply from the book.
The main trouble was to align all the three rule elements and make them accessible with the view element. Fortunately I had references from the book to implement this access.
// Jorge Escalona Gálvez
// Computer Sciences
// The Nature of Code Exercises
// Exercise Chapter 6 ( 6.17 Flocking)
void setup() {
size(900,600);
flock = new Flock();
// Implement initial boids for the program
for (int i = 0; i < 25; i++) {
Boid b = new Boid(width/2+random(0,75),height/2+random(0,75));
flock.addBoid(b);
}
}
void draw() {
background(191,13,211);
// Implement new boid
void mouseDragged() {
flock.addBoid(new Boid(mouseX,mouseY));
}
// Stablish variables for boid ruling
PVector location;
PVector velocity;
PVector acceleration;
float r;
float maxforce; // Implement an initial steering force
float maxspeed; // Same process but with a speed
color col;
Boid(float x, float y) {
acceleration = new PVector(0, 0);
velocity = new PVector(random(-1, 1), random(-1, 1));
location = new PVector(x, y);
r = 7.0;
maxspeed = 5;
maxforce = 0.05;
col = color(134,173,172);
}
void run(ArrayList<Boid> boids) {
//flock(boids);
update();
borders();
render();
}
void applyForce(PVector force) {
acceleration.add(force);
}
// The acceleration is accumulated according to the rules
void flock(ArrayList<Boid> boids) {
PVector sep = separate(boids); // Include Separation
PVector ali = align(boids); // Inlcude Alignment
PVector coh = cohesion(boids); // Include Cohesion
// PVector view = view(boids); // view
// forces weigth due to values
sep.mult(1.5);
ali.mult(1.0);
coh.mult(1.0);
// Not for every boid yet
// view.mult(1.0);
// Add the force vectors to acceleration
applyForce(sep);
applyForce(ali);
applyForce(coh);
// Not for every boid yet
// applyForce(view);
}
void update() {
// Update velocity
velocity.add(acceleration);
// Limit speed
velocity.limit(maxspeed);
location.add(velocity);
// Each cycle the acceleration goes back to 0
acceleration.mult(0);
}
// "A method that calculates and applies a steering force towards a target" ( Book reference)
// STEER = DESIRED MINUS VELOCITY
PVector seek(PVector target) {
PVector desired = PVector.sub(target, location); // A vector pointing from the location to the target
// Normalize desired and scale to maximum speed
desired.normalize();
desired.mult(maxspeed);
// Steering = Desired minus Velocity
PVector steer = PVector.sub(desired, velocity);
steer.limit(maxforce); // Limit to maximum steering force
return steer;
}
void render() {
// Draw a triangle rotated in the direction of velocity
float theta = velocity.heading() + radians(120);
fill(col);
stroke(0);
pushMatrix();
translate(location.x, location.y);
rotate(theta);
beginShape(TRIANGLES);
vertex(0, -r*2);
vertex(-r, r*2);
vertex(r, r*2);
endShape();
popMatrix();
}
// Wraparound
void borders() {
if (location.x < -r) location.x = width+r;
if (location.y < -r) location.y = height+r;
if (location.x > width+r) location.x = -r;
if (location.y > height+r) location.y = -r;
}
// Separation
// Method checks for nearby boids and steers away
PVector separate (ArrayList<Boid> boids) {
float desiredseparation = 25.0f;
PVector steer = new PVector(0, 0, 0);
int count = 0;
// For every boid in the system, check if it's too close
for (Boid other : boids) {
float d = PVector.dist(location, other.location);
// 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
PVector diff = PVector.sub(location, other.location);
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((float)count);
}
// As long as the vector is greater than 0
if (steer.mag() > 0) {
// Implement Reynolds: Steering = Desired - Velocity
steer.normalize();
steer.mult(maxspeed);
steer.sub(velocity);
steer.limit(maxforce);
}
return steer;
}
// Alignment
// For every nearby boid in the system, calculate the average velocity
PVector align (ArrayList<Boid> boids) {
float neighbordist = 50;
PVector sum = new PVector(0, 0);
int count = 0;
for (Boid other : boids) {
float d = PVector.dist(location, other.location);
if ((d > 0) && (d < neighbordist)) {
sum.add(other.velocity);
count++;
}
}
if (count > 0) {
sum.div((float)count);
sum.normalize();
sum.mult(maxspeed);
PVector steer = PVector.sub(sum, velocity);
steer.limit(maxforce);
return steer;
}
else {
return new PVector(0, 0);
}
}
// Cohesion
// For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
PVector cohesion (ArrayList<Boid> boids) {
float neighbordist = 100;
PVector sum = new PVector(0, 0); // Start with empty vector to accumulate all locations
int count = 0;
for (Boid other : boids) {
float d = PVector.dist(location, other.location);
if ((d > 0) && (d < neighbordist)) {
sum.add(other.location); // Add location
count++;
}
}
if (count > 0) {
sum.div(count);
return seek(sum); // Steer towards the location
}
else {
return new PVector(0, 0);
}
}
// View
// move laterally away from any boid that blocks the view
// Right now we are just drawing the view and highlighting boids
PVector view (ArrayList<Boid> boids) {
// How far can it see?
float sightDistance = 100;
float periphery = PI/6;
for (Boid other : boids) {
// A vector that points to another boid and that angle
PVector comparison = PVector.sub(other.location, location);
// How far is it
float d = PVector.dist(location, other.location);
// What is the angle between the other boid and this one's current direction
float diff = PVector.angleBetween(comparison, velocity);
// If it's within the periphery and close enough to see it
if (diff < periphery && d > 0 && d < sightDistance) {
// Just change its color
other.highlight();
}
}
// Debug Drawing
float currentHeading = velocity.heading();
pushMatrix();
translate(location.x, location.y);
rotate(currentHeading);
fill(0, 100);
arc(0, 0, sightDistance*4, sightDistance*4, -periphery, periphery);
popMatrix();
void highlight() {
col = color(0,255,78);
}
}
class Flock {
ArrayList<Boid> boids; // Create an initial Array List for the whole group boids
Flock() {
boids = new ArrayList<Boid>(); // Initialize the ArrayList
}
void run() {
for (Boid b : boids) {
b.col = color(15,85,75);
}
Boid b1 = boids.get(0);
b1.col = color(85,17,15);
b1.view(boids);
for (Boid b : boids) {
b.flock(boids); // Make each boid from the group as a sole
}
for (Boid b : boids) {
b.run(boids); // Same repetitive process
}
}
void addBoid(Boid b) {
boids.add(b);
}
}