Boids
Simulation
Code
chart = {
const width = fullWidth,
height = fullHeight;
const window = d3.create("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${fullWidth} ${fullHeight}`)
.attr("preserveAspectRatio", "xMidYMid meet");
const svg = window.append("g")
.attr("class", "boids");
let cohesionCoeff = defaults.cohesionCoeff,
alignmentCoeff = defaults.alignmentCoeff,
separationCoeff = defaults.separationCoeff;
const n = 500,
separationDistance = 30,
neighborDistance = 60,
maxVelocity = 2,
maxAcceleration = 0.02;
const line = d3.line();
const color = d3.scaleSequential(d3.interpolateYlGnBu)
.domain([0, maxVelocity]);
const randomX = d3.randomUniform(0, width),
randomY = d3.randomUniform(0, height),
randomVx = d3.randomUniform(0, maxVelocity),
randomVy = d3.randomNormal(0, maxVelocity / 4);
let boids = d3.range(n).map(() => ({
pos: new Vec(randomX(), randomY()),
vel: new Vec(randomVx(), randomVy()),
acc: new Vec()
}));
function tick(t) {
boids = boids.filter(b =>
(b.pos.x > 0) & (b.pos.x < width) &
(b.pos.y > 0) & (b.pos.y < height));
boids.push({
pos: new Vec(0, randomY()),
vel: new Vec(randomVx(), randomVy()),
acc: new Vec()
});
boids.forEach(b1 => {
const cohesionForce = new Vec(),
alignmentForce = new Vec(),
separationForce = new Vec();
boids.forEach(b2 => {
if (b1 === b2) {
return;
}
const separation = b2.pos.clone().minus(b1.pos),
distance = separation.length();
if (distance < separationDistance) {
separationForce.minus(separation);
} else if (distance < neighborDistance) {
cohesionForce.plus(separation);
alignmentForce.plus(b2.vel.clone().minus(b1.vel));
}
});
cohesionForce.normalize(cohesionCoeff);
alignmentForce.normalize(alignmentCoeff);
separationForce.normalize(separationCoeff);
b1.acc = new Vec()
.plus(cohesionForce)
.plus(alignmentForce)
.plus(separationForce)
.truncate(maxAcceleration);
b1.vel
.plus(b1.acc)
.truncate(maxVelocity);
b1.pos
.plus(b1.vel);
});
svg.selectAll(".boid")
.data(boids)
.join("path")
.attr("class", "boid")
.style("stroke-width", 2)
.style("stroke", b => color(b.vel.length()))
.attr("d", b => {
const v = b.vel.clone().normalize(20);
return line([
[b.pos.x - v.x / 2, b.pos.y - v.y / 2],
[b.pos.x + v.x / 2, b.pos.y + v.y / 2]
]);
});
}
const timer = d3.interval(tick, 20);
invalidation.then(() => {
timer.stop();
});
return Object.assign(window.node(), {
update(values) {
cohesionCoeff = values.cohesionCoeff;
alignmentCoeff = values.alignmentCoeff;
separationCoeff = values.separationCoeff;
},
});
}The Boids model can be used to simulate the flocking behavior of birds.