Skip to content

Instantly share code, notes, and snippets.

@hcientist
Forked from mbostock/.block
Last active April 3, 2017 18:41
Show Gist options
  • Save hcientist/9316025680da9e95e72ea38c919b8742 to your computer and use it in GitHub Desktop.
Save hcientist/9316025680da9e95e72ea38c919b8742 to your computer and use it in GitHub Desktop.
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.js"></script>
<script>
var mocks = {
// linear
"linear-to-branch" : [
// a comes online
{"id":"a", "action":"online"},
// b comes online
{"id":"b", "action":"online"},
// b listens to a
{"id":"b", "action":"listen", "target":"a"},
// c comes online
{"id":"c", "action":"online"},
// c listens to b
{"id":"c", "action":"listen", "target":"b"},
// d comes online
{"id":"d", "action":"online"},
// d listens to c
{"id":"d", "action":"listen", "target":"c"},
// e comes online
{"id":"e", "action":"online"},
// e listens to d
{"id":"e", "action":"listen", "target":"d"},
// f comes online
{"id":"f", "action":"online"},
// f listens to e
{"id":"f", "action":"listen", "target":"e"},
// g comes online
{"id":"g", "action":"online"},
// g listens to f
{"id":"g", "action":"listen", "target":"f"},
// linear to tree
// a listens to d
{"id":"a", "action":"listen", "target":"d"},
// implies c becomes choose (no longer listener)
{"id":"d", "action":"unlisten", "target":"c"}
],
"online-offline" : [
{"id":"a", "action":"online"},
{"id":"b", "action":"online"},
{"id":"c", "action":"online"},
{"id":"d", "action":"online"},
{"id":"e", "action":"online"},
{"id":"f", "action":"online"},
{"id":"g", "action":"online"},
{"id":"g", "action":"offline"},
{"id":"f", "action":"offline"},
{"id":"c", "action":"offline"},
{"id":"e", "action":"offline"},
{"id":"d", "action":"offline"},
{"id":"b", "action":"offline"},
{"id":"a", "action":"offline"},
]
};
//
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"},
// d = {id: "d"},
// e = {id: "e"},
// f = {id: "f"},
// g = {id: "g"},
var nodes = [],
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();
function online (id) {
nodes.push({id: id});
}
function offline (id) {
nodes.slice(nodes.indexOf({id: id}),1);
}
function listen (listener, broadcaster) {
links.push({source:listener, target:broadcaster});
}
function unlisten (listener, broadcaster) {
links.splice(links.indexOf({source:listener, target:broadcaster}), 1);
}
d3.interval(function() {
// if (mocks["linear-to-branch"].length > 0) {
// var instruction = mocks["linear-to-branch"].shift();
// if (instruction.action == "online") {
// online(instruction.id);
// } else if (instruction.action == "listen") {
// listen(instruction.id, instruction.target);
// }
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment