Skip to content

Instantly share code, notes, and snippets.

@mbostock
Forked from ZoltanLajosKis/d3jsproblem.html
Last active November 26, 2023 18:48
Show Gist options
  • Save mbostock/1095795 to your computer and use it in GitHub Desktop.
Save mbostock/1095795 to your computer and use it in GitHub Desktop.
Modifying a Force Layout
license: gpl-3.0
redirect: https://observablehq.com/@d3/modifying-a-force-directed-graph

This example demonstrates how to add and remove nodes and links from a force-directed layout. The graph initially appears with three disconnected nodes A, B and C. After one second, the three are connected in a loop. At two seconds, node C is removed, along with the links A-C and B-C. At three seconds, node C is reintroduced, restoring the original links A-C and B-C. Every subsequent second alternates between these two steps.

This example uses the general update pattern for data joins. See also modifying a force layout with transitions.

<!DOCTYPE html>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
color = d3.scaleOrdinal(d3.schemeCategory10);
var a = {id: "a"},
b = {id: "b"},
c = {id: "c"},
nodes = [a, b, c],
links = [];
var simulation = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody().strength(-1000))
.force("link", d3.forceLink(links).distance(200))
.force("x", d3.forceX())
.force("y", d3.forceY())
.alphaTarget(1)
.on("tick", ticked);
var g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"),
link = g.append("g").attr("stroke", "#000").attr("stroke-width", 1.5).selectAll(".link"),
node = g.append("g").attr("stroke", "#fff").attr("stroke-width", 1.5).selectAll(".node");
restart();
d3.timeout(function() {
links.push({source: a, target: b}); // Add a-b.
links.push({source: b, target: c}); // Add b-c.
links.push({source: c, target: a}); // Add c-a.
restart();
}, 1000);
d3.interval(function() {
nodes.pop(); // Remove c.
links.pop(); // Remove c-a.
links.pop(); // Remove b-c.
restart();
}, 2000, d3.now());
d3.interval(function() {
nodes.push(c); // Re-add c.
links.push({source: b, target: c}); // Re-add b-c.
links.push({source: c, target: a}); // Re-add c-a.
restart();
}, 2000, d3.now() + 1000);
function restart() {
// Apply the general update pattern to the nodes.
node = node.data(nodes, function(d) { return d.id;});
node.exit().remove();
node = node.enter().append("circle").attr("fill", function(d) { return color(d.id); }).attr("r", 8).merge(node);
// Apply the general update pattern to the links.
link = link.data(links, function(d) { return d.source.id + "-" + d.target.id; });
link.exit().remove();
link = link.enter().append("line").merge(link);
// Update and restart the simulation.
simulation.nodes(nodes);
simulation.force("link").links(links);
simulation.alpha(1).restart();
}
function ticked() {
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
}
</script>
@ohollo
Copy link

ohollo commented Oct 20, 2016

Am also having a lot of trouble converting this to v4. Am getting the same results as @steveSCC. Please advise how this can be performed in v4.

@glawler
Copy link

glawler commented Nov 8, 2016

This appears to do the trick in V4 though I've not played around with the code yet.

https://bl.ocks.org/tezzutezzu/cd04b3f1efee4186ff42aae66c87d1a7

@jeandat
Copy link

jeandat commented Apr 10, 2019

@rpdoor example above has nice transitions when nodes are added/deleted.
There is no transition in this example which is in v4.
Could someone point me in the right direction in order to achieve the same level or smoothness in v4?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment