Public
Edited
May 2, 2024
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
while (animation) {
adjustVelocity();
moveData();
plot();
yield now;
}
}
Insert cell
plot = () => {
let plt, plt2;

let grid = true;

plt = Plot.plot({
grid,
x: { domain: [boundingBox.left, boundingBox.right] },
y: { domain: [boundingBox.bottom, boundingBox.top] },
color: { legend: true, scheme: "Viridis" },
marks: [
Plot.frame(),
Plot.dot(data, { x: "x", y: "y", fill: "velocityLimit" })
]
});

data.map((d) => {
d.neighbourRatio = d.neighbours.size / crowd;
});

plt2 = Plot.plot({
y: { grid },
color: { legend: true, scheme: "RdYlBu", type: "cyclical" },
marks: [
Plot.frame(),
Plot.linearRegressionY(data, {
x: "velocityLimit",
y: "neighbourRatio"
}),
Plot.dot(data, {
x: "velocityLimit",
y: "neighbourRatio",
fill: "neighbourRatio",
stroke: (d) => "gray"
})
]
});

document.getElementById("image-div-1").innerHTML = "";
document.getElementById("image-div-1").appendChild(htl.html`
${plt}
`);

document.getElementById("image-div-2").innerHTML = "";
document.getElementById("image-div-2").appendChild(htl.html`
${plt2}
`);

return plt;
}
Insert cell
Insert cell
Insert cell
Insert cell
data1 = data
Insert cell
data1
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
getVelocity = (d) => {
let v = distance({ x: d.vx, y: d.vy })
return v
}
Insert cell
ratios = {
let r1 = 0.8,
r3 = 0.001;

return {
r1,
r2: 1 - r1 - r3,
r3
};
}
Insert cell
rotate = (d, theta) => {
let cos = Math.cos((theta * Math.PI) / 180),
sin = Math.sin((theta * Math.PI) / 180);

let vx = cos * d.vx + sin * d.vy,
vy = -sin * d.vx + cos * d.vy;
Object.assign(d, { vx, vy });
}
Insert cell
limitVelocity = (d) => {
let v = getVelocity(d),
m = d.velocityLimit * velocityScale;

if (v > m) {
d.vx /= v / m;
d.vy /= v / m;
}
}
Insert cell
adjustVelocity = () => {
let { r1, r2, r3 } = ratios,
rnd1 = d3.randomUniform(30, 50),
rnd2 = d3.randomUniform(),
rndTheta = () => {
return rnd1() * (rnd2() > 0.5 ? 1 : -1);
};

data.map((d) => {
data.map((data) => Object.assign(data, { dist: distance(data, d) }));

let neighbours = data.filter(
(data) => (data.i !== d.i) & (data.dist < neighbourDetectionRadius)
),
center = Object.assign(
{},
{
x: d3.mean(neighbours, (d) => d.x),
y: d3.mean(neighbours, (d) => d.y)
}
);

neighbours.map((data) => d.neighbours.add(data.i));

if (neighbours.length > 0) {
neighbours.sort((a, b) => a.dist - b.dist);
let nearest = neighbours[0];

// Follow the neighbours
d.vx += r1 * d3.mean(neighbours, (d) => d.vx);
d.vy += r1 * d3.mean(neighbours, (d) => d.vy);

// Crowd to the center
let v = getVelocity(d);
v /= Math.max(1, distance({ x: center.x - d.x, y: center.y - d.y }));
d.vx += v * (center.x - d.x);
d.vy += v * (center.y - d.y);

// Prevent overlap
if (nearest.dist < overlapDetectionRadius) {
rotate(d, rndTheta());
}
}

limitVelocity(d);
});
}
Insert cell
moveData = () => {
data.map((data) => {
let { x, y, vx, vy } = data;

// vx *= (x < boundingBox.right) & (x > boundingBox.left) ? 1 : -1;
// vy *= (y < boundingBox.top) & (y > boundingBox.bottom) ? 1 : -1;

x += vx + boundingBox.right;
y += vy + boundingBox.top;

x %= boundingBox.right;
y %= boundingBox.top;

Object.assign(data, { x, y, vx, vy });
});
}
Insert cell
data = {
restart;

let data = [];

let rndPos = d3.randomUniform(0, size),
rndV = d3.randomUniform(-1, 1),
rndVelocityLimit = d3.randomUniform(1, 3);

for (let i = 0; i < crowd; ++i) {
data.push(
Object.assign(
{ i, neighbours: new Set() },
{
x: rndPos(),
y: rndPos(),
vx: rndV(),
vy: rndV(),
velocityLimit: rndVelocityLimit()
}
)
);
}

return data;
}
Insert cell
data
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
d3 = require("d3")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more