Public
Edited
Nov 1, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Type Markdown, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
width = 250
Insert cell
script = {
const blob = new Blob([`
importScripts("${await require.resolve("d3-delaunay@3")}");

onmessage = event => {
const {data: {data, width, height, n}} = event;

// Initialize the points using rejection sampling.
const points = new Float64Array(n * 2);
for (let i = 0; i < n; ++i) {
for (let j = 0; j < 30; ++j) {
const x = points[i * 2] = Math.floor(Math.random() * width);
const y = points[i * 2 + 1] = Math.floor(Math.random() * height);
if (Math.random() < data[y * width + x]) break;
}
}

for (let k = 0; k < 80; ++k) {
const delaunay = new d3.Delaunay(points);
const voronoi = delaunay.voronoi([0, 0, width, height]);

// Compute the weighted centroid for each Voronoi cell.
const c = new Float64Array(n * 2);
const s = new Float64Array(n);
for (let y = 0, i = 0; y < height; ++y) {
for (let x = 0; x < width; ++x) {
const w = data[y * width + x];
i = voronoi.find(x + 0.5, y + 0.5, i);
s[i] += w;
c[i * 2] += w * (x + 0.5);
c[i * 2 + 1] += w * (y + 0.5);
}
}

// Relax the diagram by moving points to the weighted centroid.
// Wiggle the points a little bit so they don’t get stuck.
const w = Math.pow(k + 1, -0.8) * 10;
for (let i = 0; i < n; ++i) {
const x0 = points[i * 2], y0 = points[i * 2 + 1];
const x1 = s[i] ? c[i * 2] / s[i] : x0, y1 = s[i] ? c[i * 2 + 1] / s[i] : y0;
points[i * 2] = x0 + (x1 - x0) * 1.8 + (Math.random() - 0.5) * w;
points[i * 2 + 1] = y0 + (y1 - y0) * 1.8 + (Math.random() - 0.5) * w;
}

postMessage(points);
}

close();
};
`], {type: "text/javascript"});
const script = URL.createObjectURL(blob);
invalidation.then(() => URL.revokeObjectURL(script));
return script;
}
Insert cell
data = {
const image = await FileAttachment("skull.jpg").image();
const height = Math.round((width * image.height) / image.width);
const context = DOM.context2d(width, height, 1);
context.drawImage(
image,
0,
0,
image.width,
image.height,
0,
0,
width,
height
);
const { data: rgba } = context.getImageData(0, 0, width, height);
const data = new Float64Array(width * height);
for (let i = 0, n = rgba.length / 4; i < n; ++i)
data[i] = 1 - Math.max(0, 1 - rgba[i * 4] / 254);
data.width = width;
data.height = height;
return data;
}
Insert cell
Insert cell
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