Published
Edited
4 forks
29 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
polygon = geometric.polygonRegular(sides, size ** 2 / (
sides === 4 ? 1.1 :
sides === 6 ? 1.6 :
sides === 8 ? 1.3 :
sides === 10 ? 1.4 :
1.3
), [size / 2, size / 2]);
Insert cell
rmin = 1.5; // Increasing this can kill your browser
Insert cell
pad = 1; // Increasing this can kill your browser
Insert cell
N = {
again;
return 2000;
}
Insert cell
c = ["#181818", "#59354b", "#436098", "#97acd7", "#2d42de", "#2c7f2c", "#6ab469", "#f2d51a", "#bf3226", "#f3a9aa"]
.map(d => d3.cubehelix(d))
.sort((a, b) => d3.ascending(a.h, b.h))
.map(d => d.hex())
Insert cell
Insert cell
size = min(width, 640)
Insert cell
circles = {
const circles = [];
for (let i = 0; i < N; i++){
pack(polygon, circles, rmin, rmax, pad);
}
return circles;
}
Insert cell
maxiters = d3.max(circles, d => d.iters)
Insert cell
Insert cell
pack = (polygon, circles, rmin = 1, rmax = 20, pad = 1) => {
const [[x1, y1], [x2, y2]] = geometric.polygonBounds(polygon),
width = x2 - x1,
height = y2 - y1;
let r, x, y;
let safe = false,
iters = 0;
while (!safe){
let intersection = false;
r = max(rmin, random() * rmax);
x = x1 + r + random() * (width - (r * 2));
y = y1 + r + random() * (height - (r * 2));
const p = [x, y];

if (geometric.pointInPolygon(p, polygon) && circleInPolygon(x, y, r, polygon)){
for (let i = 0, l = circles.length; i < l; i++){
const c = circles[i];
const l = geometric.lineLength([p, [c.x, c.y]]);
if (l < (r + c.r + pad)){
intersection = true;
break;
}
}
safe = !intersection;
}
else {
safe = false;
}
iters++;
}
if (safe) {
circles.push({ x, y, r, iters, index: circles.length });
}
}
Insert cell
function circleInPolygon(x, y, r, polygon){
let inside = true;
for (let i = 0, l = polygon.length; i < l; i++){
const line = [polygon[i], polygon[i + 1] || polygon[0]];
const point = [x, y];
if (geometric.lineLength([point, pointToLine(point, line)]) < r){
inside = false;
break;
}
}
return inside;
}
Insert cell
// https://observablehq.com/@mbostock/closest-point-on-line
function pointToLine(point, line){
const [[x1, y1], [x2, y2]] = line;
const [x3, y3] = point;
const x21 = x2 - x1;
const y21 = y2 - y1;
const x31 = x3 - x1
const y31 = y3 - y1;
const t = max(0, min(1, (x31 * x21 + y31 * y21) / (x21 * x21 + y21 * y21)));

return [x1 + (x2 - x1) * t, y1 + (y2 - y1) * t];
}
Insert cell
Insert cell
f = d3.format(",")
Insert cell
min = Math.min
Insert cell
max = Math.max
Insert cell
pi = Math.PI
Insert cell
pow = Math.pow
Insert cell
random = Math.random
Insert cell
round = Math.round
Insert cell
sqrt = Math.sqrt
Insert cell
geometric = require("geometric@2")
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