sketch = function( p ) {
let flock;
let cohesionSlider;
let separationSlider;
let alignmentSlider;
p.setup = function() {
p.createCanvas(p.windowWidth, 800);
cohesionSlider = p.createSlider(0, 1, 1, 0.1).position(100,10);
separationSlider = p.createSlider(0, 10, 1, 1).position(100,30);
alignmentSlider = p.createSlider(0, 2, 1, 0.1).position(100,50);
flock = new Flock();
for (let i = 0; i < 50; i++) {
let b = new Boid(p.width/2, p.height/2);
flock.addBoid(b);
}
}
p.draw = function() {
p.background(0, 1);
flock.run();
p.fill(255);
p.noStroke();
p.text('cohesion', 20, 20);
p.text('separation', 20, 40);
p.text('alignment', 20, 60);
}
class Flock {
constructor() {
this.boids = [];
}
run() {
for (let i = 0; i < this.boids.length; i++) {
this.boids[i].run(this.boids);
}
}
addBoid(b) {
this.boids.push(b);
}
}
class Boid {
constructor(x, y) {
this.acceleration = p.createVector(0, 0);
this.velocity = p.createVector(p.random(-1, 1), p.random(-1, 1));
this.position = p.createVector(x, y);
this.r = 20.0;
this.maxspeed = 8;
this.maxforce = 0.05;
}
run(boids) {
this.flock(boids);
this.update();
this.borders();
this.render();
}
applyForce(F) {
this.acceleration.add(F);
}
flock(boids) {
let sep = this.separate(boids);
let ali = this.align(boids);
let coh = this.cohesion(boids);
sep.mult(separationSlider.value());
ali.mult(alignmentSlider.value());
coh.mult(cohesionSlider.value());
this.applyForce(sep);
this.applyForce(ali);
this.applyForce(coh);
}
update() {
this.velocity.add(this.acceleration);
this.velocity.limit(this.maxspeed);
this.position.add(this.velocity);
this.acceleration.mult(0);
}
seek(target) {
let desired = p5.Vector.sub(target, this.position);
desired.normalize();
desired.mult(this.maxspeed);
let steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxforce);
return steer;
}
render() {
let theta = this.velocity.heading() + p.radians(90);
let hue = p.round(180 + p.cos(p.frameCount*0.001) * 180);
let c = p.color(`hsl(${hue}, 100%, 50%)`);
p.noFill();
p.stroke(c);
p.push();
p.translate(this.position.x, this.position.y);
p.rotate(theta);
p.beginShape();
p.vertex(0, -this.r * 2);
p.vertex(-this.r, this.r * 2);
p.vertex(this.r, this.r * 2);
p.endShape(p.CLOSE);
p.pop();
}
borders(boids) {
if (this.position.x < -this.r) this.position.x = p.width + this.r;
if (this.position.y < -this.r) this.position.y = p.height + this.r;
if (this.position.x > p.width + this.r) this.position.x = -this.r;
if (this.position.y > p.height + this.r) this.position.y = -this.r;
}
separate(boids) {
let desiredseparation = 25.0;
let steer = p.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 < desiredseparation)) {
let diff = p5.Vector.sub(this.position, boids[i].position);
diff.normalize();
diff.div(d);
steer.add(diff);
count++;
}
}
if (count > 0) {
steer.div(count);
}
if (steer.mag() > 0) {
steer.normalize();
steer.mult(this.maxspeed);
steer.sub(this.velocity);
steer.limit(this.maxforce);
}
return steer;
}
align(boids) {
let neighbordist = 50;
let sum = p.createVector(0, 0);
let count = 0;
for (let i = 0; i < boids.length; i++) {
var d = p5.Vector.dist(this.position, boids[i].position);
if ((d > 0) && (d < neighbordist)) {
sum.add(boids[i].velocity);
count++;
}
}
if (count > 0) {
sum.div(count);
sum.normalize();
sum.mult(this.maxspeed);
var steer = p5.Vector.sub(sum, this.velocity);
steer.limit(this.maxforce);
return steer;
} else {
return p.createVector(0, 0);
}
}
cohesion(boids) {
let neighbordist = 50;
let sum = p.createVector(0, 0);
let count = 0;
for (var 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);
count++;
}
}
if (count > 0) {
sum.div(count);
return this.seek(sum);
} else {
return p.createVector(0, 0);
}
}
}
}