xxxxxxxxxx
209
/*
SpatialHashMap from https://github.com/simondevyoutube/Tutorial_SpatialHashGrid_Optimized/blob/main/src/spatial-grid.js
methods:
- NewClient(position, dimensions)
- FindNear(position, bounds) // [x,y] , [w,h]
- Remove(client)
- UpdateClient(client)
*/
// size of a square in the hashmap
const dimensions = [50, 50];
// top left and lower right corner of the complete map
const boundaries = [
[0, 0],
[400, 400],
];
const AMT_PARTICLES = 400;
const SEARCH_SPACE = [18, 18]; // particles will rotate a bit towards the average centre of a bunch of particles around them
const LERP_AMOUNT = 0.08;
var t = 0;
var dt = 0.005;
const R = 200;
const SPEED = 5.842;
const ps = [];
var the_history;
var show_history = false;
function setup() {
createCanvas(400, 400);
the_history = createGraphics(400, 400);
the_history.background(255,255,255);
the_history.stroke(150, 0, 0, 5);
createButton("toggle history").mousePressed(function (e) {
if (!(mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height)) {
show_history = !show_history;
}
});
noFill();
randomSeed(1);
noiseSeed(1);
field = new SpatialHashMap(boundaries, dimensions);
for (var i = 0; i < AMT_PARTICLES; i++) {
addParticle();
}
}
function mouseClicked() {
if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
var s = random() * randomGaussian(42, 5) * random() * 1000 * random() * 1000;
print(`${s} is the news seed`);
background(255, 255, 255);
randomSeed(s);
noiseSeed(s);
the_history.background(255,255,255);
resetParticles();
}
}
function resetParticles() {
// also needs to unsub the particle clients from the SHM
for (var i = ps.length - 1; i > -1; i--) {
const p = ps[i];
field.Remove(p.client);
ps.splice(i, 1);
addParticle();
}
}
function addParticle() {
const t = random() * TWO_PI;
const x = R * cos(t) + (200 - 0.1);
const y = R * sin(t) + (200 - 0.1);
const pos = [x, y];
const centre = createVector(width / 2, height / 2);
const startPos = createVector(pos[0], pos[1]);
const speed = p5.Vector.sub(centre, startPos).setMag(0.5 + SPEED * noise(t));
// initial offset from heading to centre
speed.rotate(random() * 2);
const dimensions = [1, 1];
const client = field.NewClient(pos, dimensions, speed);
const particle = {
client: client,
speed: speed,
};
ps.push(particle);
}
function aimTowardsAverageCentre(p) {
const others = field.FindNear(p.client.position, SEARCH_SPACE); // [x,y] , [w,h]
cx = 0;
cy = 0;
for (const o of others) {
cx += o.position[0];
cy += o.position[1];
}
ax = cx / others.length;
ay = cy / others.length;
p.client.position[0] += p.speed.x;
p.client.position[1] += p.speed.y;
field.UpdateClient(p.client);
const m = p.speed.mag();
// p.speed.setHeading(noise(t)*noise(t)*sin(t) * TWO_PI);
p.speed = p5.Vector.lerp(
p.speed,
p5.Vector.sub(
createVector(p.client.position[0], p.client.position[1]),
createVector(ax, ay)
).setMag(m),
LERP_AMOUNT
).setMag(m);
}
function flockParticle(p) {
const others = field.FindNear(p.client.position, SEARCH_SPACE); // [x,y] , [w,h]
cx = 0;
cy = 0;
for (const o of others) {
cx += o.speed.x;
cy += o.speed.y;
}
ax = cx / others.length;
ay = cy / others.length;
p.client.position[0] += p.speed.x;
p.client.position[1] += p.speed.y;
const m = p.speed.mag();
p.speed = p.speed.lerp(createVector(ax, ay), LERP_AMOUNT).setMag(m);
const d = dist(p.client.position[0], p.client.position[1], 200, 200);
const sclr = noise(t) * map(d, 0, R / 2, 1, 0);
const off = createVector(1, 1).setHeading(noise(t) * TWO_PI);
p.speed.lerp(off, sclr).setMag(noise(t) * SPEED + 1);
p.client.speed = p.speed;
field.UpdateClient(p.client);
}
function flupdate() {
for (var i = ps.length - 1; i > -1; i--) {
const p = ps[i];
//aimTowardsAverageCentre(p);
flockParticle(p);
const d = dist(p.client.position[0], p.client.position[1], 200, 200);
the_history.point(p.client.position[0], p.client.position[1]);
if (d > R - 0.1) {
field.Remove(p.client);
ps.splice(i, 1);
addParticle();
}
}
}
function flaw() {
for (const [i, p] of ps.entries()) {
push();
// color based on time
// if (noise(t + i) > 0.4) {
// stroke(noise(t) * 200 + 50, 100, 255);
// } else {
// stroke(150, 90, noise(t) * 255 + 65);
// }
// color based on direction
if (p.speed.heading() < 0) {
stroke(noise(t) * 200 + 70, p.client.position[0], 255);
} else {
stroke(150, p.client.position[0], noise(t) * 255 + 65);
}
circle(p.client.position[0], p.client.position[1], 3);
pop();
}
}
function draw() {
background(0);
flupdate();
if (! show_history) flaw();
t += dt;
circle(200, 200, R * 2);
if (show_history) image(the_history,0,0);
}