xxxxxxxxxx
197
// Reece Gilbert
// Flocking with spatial hashing
let flock = [];
let alignSlider, cohesionSlider, separationSlider;
let cellSize = 50;
function setup() {
createCanvas(800, 800);
alignSlider = 5
cohesionSlider = 5
separationSlider = 10
for (let i = 0; i < 500; i++) {
flock.push(new Boid());
}
}
function draw() {
background(0);
drawGrid();
let spatialHash = new SpatialHash(cellSize);
for (let boid of flock) {
spatialHash.addObject(boid);
}
for (let boid of flock) {
let neighbors = spatialHash.getNeighbors(boid);
boid.flock(neighbors);
boid.update();
}
for (let boid of flock) {
boid.edges();
boid.show();
}
}
function drawGrid() {
stroke(100);
for (let x = 0; x < width; x += cellSize) {
line(x, 0, x, height);
}
for (let y = 0; y < height; y += cellSize) {
line(0, y, width, y);
}
}
class SpatialHash {
constructor(cellSize) {
this.cellSize = cellSize;
this.grid = {};
}
hashPosition(position) {
let cellX = floor(position.x / this.cellSize);
let cellY = floor(position.y / this.cellSize);
return `${cellX},${cellY}`;
}
addObject(obj) {
let cellPos = this.hashPosition(obj.position);
if (!this.grid[cellPos]) {
this.grid[cellPos] = [];
}
this.grid[cellPos].push(obj);
}
getNeighbors(obj) {
let cellPos = this.hashPosition(obj.position);
let neighbors = [];
let cellX = int(cellPos.split(",")[0]);
let cellY = int(cellPos.split(",")[1]);
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
let cellKey = `${cellX + dx},${cellY + dy}`;
if (this.grid[cellKey]) {
neighbors = neighbors.concat(this.grid[cellKey]);
}
}
}
return neighbors;
}
}
class Boid {
constructor() {
this.position = createVector(random(width), random(height));
this.velocity = p5.Vector.random2D();
this.velocity.setMag(random(2, 4));
this.acceleration = createVector();
this.maxForce = 0.2;
this.maxSpeed = 5;
}
edges() {
if (this.position.x > width) this.position.x = 0;
else if (this.position.x < 0) this.position.x = width;
if (this.position.y > height) this.position.y = 0;
else if (this.position.y < 0) this.position.y = height;
}
align(boids) {
let perceptionRadius = 25;
let steering = createVector();
let total = 0;
for (let other of boids) {
let d = dist(this.position.x, this.position.y, other.position.x, other.position.y);
if (other !== this && d < perceptionRadius) {
steering.add(other.velocity);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.setMag(this.maxSpeed);
steering.sub(this.velocity);
steering.limit(this.maxForce);
}
return steering;
}
separation(boids) {
let perceptionRadius = 20;
let steering = createVector();
let total = 0;
for (let other of boids) {
let d = dist(this.position.x, this.position.y, other.position.x, other.position.y);
if (other !== this && d < perceptionRadius) {
let diff = p5.Vector.sub(this.position, other.position);
diff.div(d * d);
steering.add(diff);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.setMag(this.maxSpeed);
steering.sub(this.velocity);
steering.limit(this.maxForce);
}
return steering;
}
cohesion(boids) {
let perceptionRadius = 50;
let steering = createVector();
let total = 0;
for (let other of boids) {
let d = dist(this.position.x, this.position.y, other.position.x, other.position.y);
if (other !== this && d < perceptionRadius) {
steering.add(other.position);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.sub(this.position);
steering.setMag(this.maxSpeed);
steering.sub(this.velocity);
steering.limit(this.maxForce);
}
return steering;
}
flock(boids) {
let alignment = this.align(boids);
let cohesion = this.cohesion(boids);
let separation = this.separation(boids);
this.acceleration.add(alignment);
this.acceleration.add(cohesion);
this.acceleration.add(separation);
}
update() {
this.position.add(this.velocity);
this.velocity.add(this.acceleration);
this.velocity.limit(this.maxSpeed);
this.acceleration.mult(0);
}
show() {
let angle = this.velocity.heading() + PI / 2;
push();
translate(this.position.x, this.position.y);
rotate(angle);
stroke(255, 0, 0);
line(0, 0, 0, -10)
stroke(255)
fill(255)
circle(0, 0, 3)
pop();
}
}