Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Modifying a Force Layout
license: gpl-3.0

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>

What is the purpose of the var color = d3.scale.category10(); at line 28? It appears to have no effect.

@ghost

ghost commented Nov 10, 2015

can you give some description about start() method ? how ( and why ) dose it work ?

rdpoor commented Apr 19, 2016

I've just created a fork of this example, heavily commented, giving what I hope is a detailed explanation of the important concepts: https://gist.github.com/rdpoor/3a66b3e082ffeaeb5e6e79961192f7d8

(And I see that it appears in bl.ocks "by magic". Very nice!)

stevescc commented Sep 19, 2016

I'm hoping to update this gist from d3 v3 to v4 as it will directly help in a project of mine. I've made an attempt to upgrade however the nodes are positioned in the top left corner and the removal of the nodes doesn't appear to be happening.
My fork:
https://gist.github.com/stevescc/f964f8f4658bc11319765f83f67b7f9f
https://bl.ocks.org/stevescc/f964f8f4658bc11319765f83f67b7f9f
Ta

Is adding and removing nodes from a graph (interactively) something that is not very well supported and documented in D3 v4? Should I look for another library?

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 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

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