Public
Edited
Oct 24, 2023
6 forks
45 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function chart(d3Contour, title, grid, first, invalidation) {
const width = 464,
height = 400,
context = DOM.context2d(width, height),
path = d3.geoPath().context(context);

const Vmax = 0.15;

context.scale(4, 4);
context.font = `${14 / 4}px Arial`;

function drawsystem() {
context.strokeStyle = "#777";
context.lineWidth = 0.05 / 4
context.beginPath();
for (let i = 0; i < 101; i += 4) {
context.moveTo(i, 12);
context.lineTo(i, 100);
if (i > 8) {
context.moveTo(0, i);
context.lineTo(100, i);
}
}
context.stroke();

context.strokeStyle = "black";
context.lineWidth = .25 / 4;
context.beginPath();
for (const c of d3Contour
.contourDensity()
.size([100, 100])
.bandwidth(8)
.thresholds(10)(particles)
) path(c);
context.stroke();

if (grid) {
context.lineWidth = .75 / 4;
context.globalAlpha = 0.5;
for (const p of grid(particles)) {
context.beginPath();
path.pointRadius(Math.sqrt(p.weight) * 1.3);
path(p);
context.fillStyle = "red";
context.fill();
}
context.globalAlpha = 1;
}

context.beginPath();
path.pointRadius(1.3);
path({ type: "MultiPoint", coordinates: particles });
context.strokeStyle = context.fillStyle = "red";
context.stroke();

context.fillStyle = "black";
context.fillText(title, 10 / 4, 24 / 4);
}

function draw() {
if (first) {
for (const p of particles) {
p.v = (p.v || [0, 0]).map(d => d + (Math.random() - .5) ** 5);
const V = Math.hypot(...p.v);
if (V > Vmax) p.v = p.v.map(d => d / (V / Vmax));
p[0] += p.v[0];
p[1] += p.v[1];
if (p[0] < 10) p.v[0] = Math.abs(p.v[0]);
if (p[0] > 90) p.v[0] = -Math.abs(p.v[0]);
if (p[1] < 10) p.v[1] = Math.abs(p.v[1]);
if (p[1] > 90) p.v[1] = -Math.abs(p.v[1]);
}
}

context.fillStyle = "white";
context.fillRect(0, 0, width, height);
drawsystem();
}

if (!pause) {
const timer = d3.timer(draw);
invalidation.then(() => timer.stop());
}
draw();

return context.canvas;
}
Insert cell
Insert cell
Insert cell
function gridClosest(particles) {
function round42(x) {
return 4 * Math.floor(x / 4) + 2;
}
return particles.map(point => ({
type: "Point",
coordinates: point.map(round42),
weight: 1
}));
}
Insert cell
function gridLinear(particles) {
function round4(x) {
const a = 4 * Math.floor(x / 4);
return [a, 1 + (a - x) / 4];
}
return particles
.map(point => {
const [x, wx] = round4(point[0]),
[y, wy] = round4(point[1]);
return [
{
type: "Point",
coordinates: [x, y],
weight: wx * wy
},
{
type: "Point",
coordinates: [x + 4, y],
weight: (1 - wx) * wy
},
{
type: "Point",
coordinates: [x, y + 4],
weight: wx * (1 - wy)
},
{
type: "Point",
coordinates: [x + 4, y + 4],
weight: (1 - wx) * (1 - wy)
}
];
})
.flat();
}
Insert cell
d3v6 = require("d3@6") // to show the old method
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