plot = {
const context = DOM.context2d(width, height);
const yForceAccessor = yForceToggle === "beeswarm" ? () => height / 2 : d => yScale(d.pop);
let simulation;
let newNodes;
path.context(context);
if (Boolean(showMap)) {
drawBubbleMap();
Promise.race([
invalidation,
Promises.delay(1200, true)
])
.then(run => run && runSimulation());
} else {
runSimulation();
}
function drawBubbleMap() {
context.save();
context.beginPath(), path(counties), context.strokeStyle = "#ccc", context.stroke();
context.restore();
nodes.forEach(d => {
context.save();
context.beginPath();
context.arc(d.x0, d.y0, d.r, 0, 2 * Math.PI);
context.fillStyle = "rgba(0,0,0,0.5)";
context.fill();
context.restore();
});
}
function runSimulation() {
newNodes = nodes.map(({x0, y0, ...rest}) =>({x: x0, y: y0, ...rest}));
simulation = d3.forceSimulation(newNodes)
.force("x", d3.forceX().x(d => xScale(d.noaccessPct)).strength(xStrength))
.force("y", d3.forceY().y(yForceAccessor).strength(yStrength))
.force("collide",
d3.forceCollide()
.radius(d => d.r + NODE.PADDING)
.strength(collideStrength)
.iterations(2)
)
.force("attract", d3.forceManyBody().strength(attractStrength))
.on("tick", ticked);
invalidation.then(() => simulation.stop());
}
function ticked() {
context.clearRect(0, 0, width, height);
newNodes.forEach(d => {
context.save();
context.beginPath();
context.arc(d.x, d.y, d.r, 0, 2 * Math.PI);
context.fillStyle = "rgba(0,0,0,0.5)";
context.fill();
context.restore();
});
}
return context.canvas;
}