Public
Edited
Oct 23, 2023
5 forks
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// Choose a dataset. Deep copy the data so we can see what new attributes the Simulation adds.
graph = {
button; // reset the data when clicked
return JSON.parse(JSON.stringify(miserables));
return JSON.parse(JSON.stringify(flare));
}
Insert cell
Insert cell
simulation = {
let sim = d3
.forceSimulation(graph.nodes)
// insert forces here...

// Hookes law:
.force(
"spring",
d3
.forceLink(graph.links)
.id((v) => v.id)
//.strength(1)
.distance(30)
)

// Coulomb's law:
.force("repel", d3.forceManyBody().strength(-70))

// Any other useful forces?
// Gravity keeps disconnected components from flying away:
.force("gravityX", d3.forceX(width / 2).strength(0.05))
.force("gravityY", d3.forceY(height / 2).strength(0.05))
// Center it on screen:
.force("center", d3.forceCenter(width / 2, height / 2));

// Terminate the force layout when this cell re-runs.
invalidation.then(() => sim.stop());

return sim;
}
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.style("border", "1px solid lightgrey");

// Map edges to lines
const links = svg
.append("g")
.attr("stroke", "lightgrey")
.attr("stroke-width", 1)
.selectAll("line")
.data(graph.links)
.join("line");
//.attr("x1", (e) => e.source.x)
//.attr("y1", (e) => e.source.y)
//.attr("x2", (e) => e.target.x)
//.attr("y2", (e) => e.target.y);

// Map vertices to circles
const nodes = svg
.append("g")
.attr("fill", "steelblue")
.selectAll("circle")
.data(graph.nodes)
.join("circle")
.attr("r", 5);
//.attr("cx", (v) => v.x)
//.attr("cy", (v) => v.y);
nodes.append("title").text((d) => d.id);
setupDrag(svg, nodes, simulation);

// Callback after every iteration of the simulation, set xy using values computed by force Simulation
function update() {
// make updates here...
nodes.attr("cx", (v) => v.x).attr("cy", (v) => v.y);
links
.attr("x1", (e) => e.source.x)
.attr("y1", (e) => e.source.y)
.attr("x2", (e) => e.target.x)
.attr("y2", (e) => e.target.y);
}
simulation.on("tick", update);
update(); // call it once now to set locations if the sim is already done

return svg.node();
}
Insert cell
// Set the fx,fy values of the dragged node to fix it to the mouse position.
// Simulation will not move nodes with fx,fy set, and will fix x=fx, y=fy.
function setupDrag(svg, nodes, simulation) {
// D3 Drag API
const drag = d3
.drag()
.on("start", dragstart)
.on("drag", dragging)
.on("end", dragend);
nodes.call(drag);

// Drag event callback functions
function dragstart(event) {
// do stuff here...
//if (!event.active) // only needed in multi-touch scenarios
simulation.alphaTarget(0.3).restart(); // restart the sim and run indefinitely
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragging(event) {
// do stuff here...
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragend(event) {
// do stuff here...
//if (!event.active) // only needed in multi-touch scenarios
simulation.alphaTarget(0); // return to normal sim stopping condition
event.subject.fx = null; // release the fix
event.subject.fy = null;
}
}
Insert cell
height = 700
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more