Public
Edited
Jul 12, 2024
Insert cell
Insert cell
{
const svg = d3
.create("svg")
.attr("width", chartwidth + margin.left + margin.right)
.attr("height", chartheight + margin.top + margin.bottom);

const g = svg
.append("g")
.attr("transform", `translate(${[margin.left, margin.top]})`);

g.append("g")
.call(d3.axisBottom(x).tickSizeOuter(0))
.attr("transform", `translate(0, ${chartheight / 1.25})`);

g.selectAll("rect")
.data(beeswarm(data))
.join("rect")
.attr("stroke", "black")
.attr("fill", "none")
.attr("x", (d) => d.x)
.attr("y", (d) => d.y)
// .attr("r", d => r(d.data.size));
.attr("width", (d) => r(d.data.size))
.attr("height", (d) => r(d.data.size))
.attr(
"transform",
(d) =>
`rotate(${rotationAngle}, ${d.x + r(d.data.size) / 2}, ${
d.y + r(d.data.size) / 2
})`
);

g.selectAll("circle")
.data(beeswarm(data))
.join("circle")
.attr("stroke", "black")
.attr("fill", "black")
.attr("cx", (d) => d.x + r(d.data.size) / 2)
.attr("cy", (d) => d.y + r(d.data.size) / 2)
.attr("r", (d) => r(d.data.size) / 20);

return svg.node();
}
Insert cell
beeswarm = beeswarmForce()
.x(d => x(d.value))
.y(chartheight / 2)
.r(d => r(d.size))
Insert cell
viewof rotationAngle = Inputs.range([-180, 180], {
label: "Rotation Angle",
step: 5,
value: 45
})
Insert cell
beeswarm(data)
Insert cell
x = d3.scaleLinear(
d3.extent(data, d => d.value),
[0, chartwidth]
);
Insert cell
r = d3.scaleSqrt(
[100, 1000],
[1, Math.sqrt(width * height) / 30]
);
Insert cell
margin = ({ left: 2 * r.range()[1], right: 2 * r.range()[1], top: 1, bottom: 1 });
Insert cell
chartwidth = width - margin.left - margin.right;
Insert cell
height = 200;
Insert cell
chartheight = height - margin.top - margin.bottom;
Insert cell
// Make some random data
data = {
const random = d3.randomNormal();
return Array
.from({ length: 100 })
.map(d => ({ value: random(), size: d3.randomUniform(...r.domain())() }));
}
Insert cell
beeswarmForce = function() {
let x = d => d[0];
let y = d => d[1];
let r = d => d[2];
let ticks = 300;
function beeswarm(data) {
const sqrt2 = Math.sqrt(2);
const entries = data.map(d => ({
x0: typeof x === "function" ? x(d) : x,
y0: typeof y === "function" ? y(d) : y,
r: typeof r === "function" ? r(d) : r, // Assuming this is half the width or height
data: d
}));

const collisionForce = d3.forceCollide(d => d.r * sqrt2)
.iterations(16) // Increase iterations for a tighter packing
.strength(1); // Maximum strength to prevent overlap

const simulation = d3.forceSimulation(entries)
.force("x", d3.forceX(d => d.x0).strength(0.5))
.force("y", d3.forceY(d => d.y0).strength(0.5))
.force("collide", collisionForce);

for (let i = 0; i < ticks; i++) simulation.tick();
return entries;
}
beeswarm.x = f => f ? (x = f, beeswarm) : x;
beeswarm.y = f => f ? (y = f, beeswarm) : y;
beeswarm.r = f => f ? (r = f, beeswarm) : r;
beeswarm.ticks = n => n ? (ticks = n, beeswarm) : ticks;
return beeswarm;
}

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