Public
Edited
Jan 10, 2024
2 stars
Insert cell
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
canvas = {
// Particles can move out of the html canvas. These two variables define a "container",
// when a particular is outside the limits defined by those variables,
// we bring them back into view.
const minDistance2 = minDistance * minDistance;
const maxDistance2 = maxDistance * maxDistance;
const context = DOM.context2d(width, height);

// Create n particles and give them a random location within the limits of the html canvas.
// They all will be visible.
//const particles = genParticles(n);
// We generate outside this cell so we can stop the animation

let c = this || DOM.context2d(width, height);
if (stop) return yield c;

while (true) {
context.save(); // Not sure why this is necessary
context.clearRect(0, 0, width, height);

// Loop over the particles
for (let i = 0; i < n; ++i) {
const p = particles[i];
// Update their position using the velocity (another vector)
p.x += p.vx;
// If the x coordinate is outside the maxDistance limits,
// bring the particle back to a "visible" x location
if (p.x < -maxDistance) p.x += width + maxDistance * 2;
else if (p.x > width + maxDistance) p.x -= width + maxDistance * 2;

// Same for the y axis
p.y += p.vy;
if (p.y < -maxDistance) p.y += height + maxDistance * 2;
else if (p.y > height + maxDistance) p.y -= height + maxDistance * 2;

// Give the velocity a new value so the particle moves to a new position
// that is close to the current one (see plots below).
p.vx += 0.2 * (Math.random() - 0.5) - 0.01 * p.vx;
p.vy += 0.2 * (Math.random() - 0.5) - 0.01 * p.vy;

// Draw the particle
context.beginPath();
context.arc(p.x, p.y, radius, 0, 2 * Math.PI);
context.fill();
}

// Work on the particle connections. Check one particle against all the
// other ones.
for (let i = 0; i < n; ++i) {
for (let j = i + 1; j < n; ++j) {
// get the two particles we are going to work on (pi, pj)
const pi = particles[i];
const pj = particles[j];
// compute their distance
const dx = pi.x - pj.x;
const dy = pi.y - pj.y;
const d2 = dx * dx + dy * dy;

// if the particles are close enough connect them an give them an opacity
if (d2 < maxDistance2) {
const a =
d2 > minDistance2
? (maxDistance2 - d2) / (maxDistance2 - minDistance2)
: 1;
context.globalAlpha = a;
context.beginPath();
context.moveTo(pi.x, pi.y);
context.lineTo(pj.x, pj.y);
context.stroke();
if (false) {
console.log(
`d2:${d2.toFixed(
1
)} < max2:${maxDistance2} -> min2:${minDistance2}: a:${a.toFixed(
1
)} (${pi.x.toFixed(0)},${pi.y.toFixed(0)}) (${pj.x.toFixed(
0
)},${pj.y.toFixed(0)})`
);
}
}
}
}
context.restore();
yield context.canvas;
await Promises.delay(delayms);
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function genParticles(n) {
const particles = new Array(n);
for (let i = 0; i < n; ++i) {
particles[i] = {
x: Math.random() * width,
y: Math.random() * height,
vx: 0,
vy: 0
};
}
return particles;
}
Insert cell
{
const d = d3
.range(0, maxDistance * maxDistance * 2)
.map((x) => ({ distance: x, opacity: computeOpacity(x) }));
return Plot.dot(d, { x: "distance", y: "opacity" }).plot({ width });
}
Insert cell
function distance(pi, pj) {
const dx = pi.x - pj.x;
const dy = pi.y - pj.y;
const d2 = dx * dx + dy * dy;
return d2;
}
Insert cell
distance({ x: 100, y: 100 }, { x: 110, y: 110 })
Insert cell
computeOpacity
Insert cell
function computeOpacity(d2) {
const minDistance2 = minDistance * minDistance;
const maxDistance2 = maxDistance * maxDistance;
let a = -1;
if (d2 < maxDistance2) {
a =
d2 > minDistance2
? (maxDistance2 - d2) / (maxDistance2 - minDistance2)
: 1;
}
return a;
}
Insert cell
randomVelocity = {
let vx = 0;
const a = [{ vx, i: 0 }];
for (let i = 0; i < 5000; i++) {
vx += 0.2 * (Math.random() - 0.5) - 0.01 * vx;
a.push({ vx, i });
}
return a;
}
Insert cell
Plot.dot(randomVelocity, { x: "i", y: "vx" }).plot({ width })
Insert cell
{
const d = randomVelocity.map(({ vx }) => vx);
return Plot.rectY({ length: 5000 }, Plot.binX({ y: "count" }, { x: d })).plot(
{ width }
);
}
Insert cell
radius = 2.5
Insert cell
import { record } from "@mourner/canvas-recorder"
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