Published unlisted
Edited
Aug 23, 2019
Insert cell
Insert cell
simulation = {
return this
? this.nodes(nodes)
: d3.forceSimulation(nodes)
// .force("charge", d3.forceManyBody())
.force("links", d3.forceLink(links) .iterations(1))
.force("radial", d3.forceRadial(svgSize * 3 / 8))
// .force('center', d3.forceCenter(svgSize / 8, svgSize / 8))
;
}
Insert cell
Insert cell
svg = {
const svg = d3.select(this || DOM.svg(svgSize, svgSize));
const g = svg.select("g.top").empty() ?
svg.append("g")
.attr("class", "top")
.attr("transform", `translate(${svgSize / 2},${svgSize / 2})`)
.attr("stroke", "currentColor")
.attr("stroke-width", 1.5) :
svg.select("g");
const allNodes = g.selectAll(".node").data(nodes, d => d.name);

// transition
const t = d3.transition().duration(tickPeriod * .8);

// Apply the general update pattern to the nodes.

// exit
// shrinking black text on a shrinking red circle
const nodesExiting = allNodes.exit();
nodesExiting.transition(t).remove();
nodesExiting.selectAll("circle")
.attr("class", "exit")
.style("fill", "red")
.transition(t)
// .attr("opacity", 0)
.attr("r", 1e-6);
nodesExiting.selectAll("text")
.attr("class", "exit")
.style("fill", "black")
.transition(t)
.attr("font-size", 6);
// update
// white text on a correctly sized steelblue circle
const updatingCircles = allNodes.selectAll("circle").data(nodes, d => d.name);
updatingCircles.transition(t)
.style("fill", "steelblue")
.attr("r", d => d.size);
const updatingText = allNodes.selectAll("text").data(nodes, d => d.name);
updatingText.transition(t)
.style("fill", "white");
// enter
// yellow text on a slightly oversized green circle
const nodesEntering = allNodes.enter().append("g")
.attr("class", "node");
// const nodeCirc =
nodesEntering.append("circle")
.style("fill", "lightgreen")
// .attr("opacity", 1)
.attr("r", d => (d.size + 5))
.attr("cx", d => d.initX).attr("cy", d => d.initY);
// const nodeText =
nodesEntering.append("text")
.attr("font-size", 22)
.style("fill", "blue")
.style("stroke", "none")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.text(d => d.name)
.attr("x", d => d.initX).attr("y", d => d.initY);
// Update and restart the simulation.
// refresh();
simulation
.force("collide",
d3.forceCollide()
.strength(1)
.radius(d => (d.size + 25))
.iterations(5))
.on("tick", ticked);
function ticked() {
const allNodes = d3.select("g.top").selectAll(".node").data(nodes);
allNodes.selectAll("circle").attr("cx", d => d.x).attr("cy", d => d.y);
allNodes.selectAll("text") .attr("x", d => d.x).attr("y", d=> d.y);
}
return svg.node();
}
Insert cell
viewof nodes = {
let display = html`node labels: []`;
let prior = -1;
while (true) {
const data = rawPoints;
if(prior != data.length) {
prior = data.length;
display = html`node labels: [${data.map(d => d.name).sort().join(", ")}]`;
display.value = data;
}
yield Promises.tick(tickPeriod, display);
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
// This cell is a hack which fixes the issue of restarting the simulation when nSides changes.
// I really need to clean things up and use the mutation pattern that Mike suggested, but this will do for now.
// The reference to nPoints will trigger this cell to be updated when nPoints changes,
// which in turn allows the simulation to be restarted.
simulation.restart();
return nPoints;
}
Insert cell
Insert cell
Insert cell
Insert cell
tickPeriod = 50;
Insert cell
Insert cell
Insert cell
Insert cell
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