Skip to content

Instantly share code, notes, and snippets.

@rdpoor
Forked from mbostock/.block
Last active June 22, 2023 22:15

Revisions

  1. rdpoor revised this gist Apr 19, 2016. No changes.
  2. rdpoor revised this gist Apr 19, 2016. 2 changed files with 134 additions and 45 deletions.
    4 changes: 4 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1 +1,5 @@
    This is a nearly verbatim copy of Mike Bostock's [Modifying a Force Layout example](https://bl.ocks.org/mbostock/1095795) -- the main difference is it has ample documentation explaining what's going on. We've taken the liberty of renaming some variables and methods to make them more descriptive, and in one case (can you spot it?) made a small pessimization to the code in the name of clarity.

    From the original version:

    This example demonstrates how to add and remove nodes and links from a force layout. The graph initially appears with three nodes A, B and C connected in a loop. Three seconds later, node B is removed, along with the links B-A and B-C. At six seconds, node B is reintroduced, restoring the original links B-A and B-C. This example uses the [general update pattern](http://bl.ocks.org/3808218) for [data joins](http://bost.ocks.org/mike/join/).
    175 changes: 130 additions & 45 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -8,87 +8,172 @@
    }

    .node {
    fill: #000;
    fill: #000; /* this is over-ridden by node.a (etc) below */
    stroke: #fff;
    stroke-width: 1.5px;
    }

    /* the class of the node determines its color */
    .node.a { fill: #1f77b4; }
    .node.b { fill: #ff7f0e; }
    .node.c { fill: #2ca02c; }

    </style>
    <body>
    <script src="//d3js.org/d3.v3.min.js"></script>
    <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script>

    var width = 960,
    var width = 960, // width and height of the SVG canvas
    height = 500;

    var color = d3.scale.category10();
    // A list of node objects. We represent each node as {id: "name"}, but the D3
    // system will decorate the node with addtional fields, notably x: and y: for
    // the force layout and index" as part of the binding mechanism.
    var nodes = [];

    var nodes = [],
    links = [];
    // A list of links. We represent a link as {source: <node>, target: <node>},
    // and in fact, the force layout mechanism expects those names.
    links = [];

    // Create the force layout. After a call to force.start(), the tick method will
    // be called repeatedly until the layout "gels" in a stable configuration.
    var force = d3.layout.force()
    .nodes(nodes)
    .links(links)
    .charge(-400)
    .linkDistance(120)
    .linkDistance(100)
    .size([width, height])
    .on("tick", tick);

    // add an SVG element inside the DOM's BODY element
    var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

    var node = svg.selectAll(".node"),
    link = svg.selectAll(".link");
    function update_graph() {

    // First update the links...

    // The call to <selection>.data() gets the link objects that need updating
    // and associates them with the corresponding DOM elements, in this case,
    // SVG line elements. The first argument, force.links() simply returns our
    // links[] array. The second argument is a function that produces a unique
    // and invariate identifier for each link. This makes it possible for D3 to
    // correctly associate each link object with each line element in the DOM,
    // even if the link object moves around in memory.
    //
    // link_update, in addition to processing objects that need updating,
    // defines two custom methods, .enter() and .exit(), that refer to link
    // objects that are newly added and that have been deleted (respectively).
    // See below to see how these methods are used.
    var link_update = svg.selectAll(".link").data(
    force.links(),
    function(d) { return d.source.id + "-" + d.target.id; }
    );

    // link_update.enter() creates an SVG line element for each new link
    // object. Note that we call .insert("line", ".node") to add SVG line
    // elements to the DOM before any elements with class="node". This
    // guarantees that the lines will be drawn first. (Try changing that to
    // .append("line") to see what happens...)
    link_update.enter()
    .insert("line", ".node")
    .attr("class", "link");

    // link_update.exit() processes link objects that have been removed
    // by removing its corresponding SVG line element.
    link_update.exit()
    .remove();

    // Now update the nodes. This is similar in structure to what we just
    // did for the links:
    //
    // node_selection.data() returns a selection of nodes that need updating
    // and defines two custom methods, .enter() and .exit(), that will process
    // newly created node objects and deleted node objects.
    //
    // Note that, similar to what we did for links, we've provided an id method
    // that returns a unique and invariate identifier for each node. This is
    // what lets D3 associate each node object with its corresponding circle
    // element in the DOM, even if the node object moves around in memory.
    var node_update = svg.selectAll(".node").data(
    force.nodes(),
    function(d) { return d.id;}
    );

    // Create an SVG circle for each new node added to the graph. Note that we
    // use a function to set the class of each node, because in this demo, the
    // color is determined by the class of the node, e.g.
    //
    // <style>
    // .node.a { fill: #1f77b4; }
    // </style>
    // ...
    // <circle class="node a" r="8"</circle>
    //
    // ... but other techniques are possible
    node_update.enter()
    .append("circle")
    .attr("class", function(d) { return "node " + d.id; })
    .attr("r", 8);

    // Remove the SVG circle whenever a node vanishes from the node list.
    node_update.exit()
    .remove();

    // Start calling the tick() method repeatedly to lay out the graph.
    force.start();
    }

    // This tick method is called repeatedly until the layout stabilizes.
    //
    // NOTE: the order in which we update nodes and links does NOT determine which
    // gets drawn first -- the drawing order is determined by the ordering in the
    // DOM. See the notes under link_update.enter() above for one technique for
    // setting the ordering in the DOM.
    function tick() {

    // Drawing the nodes: Update the cx, cy attributes of each circle element
    // from the x, y fields of the corresponding node object.
    svg.selectAll(".node")
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; })

    // Drawing the links: Update the start and end points of each line element
    // from the x, y fields of the corresponding source and target node objects.
    svg.selectAll(".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; });
    }

    // ================================================================
    // Toplevel methods start here

    // 1. Add three nodes and three links.
    setTimeout(function() {
    var a = {id: "a"}, b = {id: "b"}, c = {id: "c"};
    nodes.push(a, b, c);
    links.push({source: a, target: b}, {source: a, target: c}, {source: b, target: c});
    start();
    var a = {id: "a"}, b = {id: "b"}, c = {id: "c"};
    nodes.push(a, b, c);
    links.push({source: a, target: b},
    {source: a, target: c},
    {source: b, target: c});
    update_graph();
    }, 0);

    // 2. Remove node B and associated links.
    setTimeout(function() {
    nodes.splice(1, 1); // remove b
    links.shift(); // remove a-b
    links.pop(); // remove b-c
    start();
    nodes.splice(1, 1); // remove b
    links.shift(); // remove a-b
    links.pop(); // remove b-c
    update_graph();
    }, 3000);

    // Add node B back.
    // 3. Add node B back.
    setTimeout(function() {
    var a = nodes[0], b = {id: "b"}, c = nodes[1];
    nodes.push(b);
    links.push({source: a, target: b}, {source: b, target: c});
    start();
    var a = nodes[0], b = {id: "b"}, c = nodes[1];
    nodes.push(b);
    links.push({source: a, target: b}, {source: b, target: c});
    update_graph();
    }, 6000);

    function start() {
    link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; });
    link.enter().insert("line", ".node").attr("class", "link");
    link.exit().remove();

    node = node.data(force.nodes(), function(d) { return d.id;});
    node.enter().append("circle").attr("class", function(d) { return "node " + d.id; }).attr("r", 8);
    node.exit().remove();

    force.start();
    }

    function tick() {
    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>
  3. @mbostock mbostock revised this gist Feb 9, 2016. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions .block
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    license: gpl-3.0
  4. @mbostock mbostock revised this gist Oct 30, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -19,7 +19,7 @@

    </style>
    <body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
    <script src="//d3js.org/d3.v3.min.js"></script>
    <script>

    var width = 960,
  5. @mbostock mbostock revised this gist Jun 11, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -19,7 +19,7 @@

    </style>
    <body>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
    <script>

    var width = 960,
  6. @mbostock mbostock revised this gist Dec 20, 2012. 1 changed file with 0 additions and 0 deletions.
    Binary file modified thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  7. @mbostock mbostock revised this gist Dec 8, 2012. 2 changed files with 3 additions and 3 deletions.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1 +1 @@
    This example demonstrates how to add and remove nodes and links from a force layout.
    This example demonstrates how to add and remove nodes and links from a force layout. The graph initially appears with three nodes A, B and C connected in a loop. Three seconds later, node B is removed, along with the links B-A and B-C. At six seconds, node B is reintroduced, restoring the original links B-A and B-C. This example uses the [general update pattern](http://bl.ocks.org/3808218) for [data joins](http://bost.ocks.org/mike/join/).
    4 changes: 2 additions & 2 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -53,15 +53,15 @@
    start();
    }, 0);

    // 2. Remove node bbb and associated links.
    // 2. Remove node B and associated links.
    setTimeout(function() {
    nodes.splice(1, 1); // remove b
    links.shift(); // remove a-b
    links.pop(); // remove b-c
    start();
    }, 3000);

    // Add node bbb back.
    // Add node B back.
    setTimeout(function() {
    var a = nodes[0], b = {id: "b"}, c = nodes[1];
    nodes.push(b);
  8. @mbostock mbostock revised this gist Dec 8, 2012. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -3,12 +3,14 @@
    <style>

    .link {
    stroke: #ccc;
    stroke: #000;
    stroke-width: 1.5px;
    }

    .node {
    fill: #000;
    stroke: #fff;
    stroke-width: 1.5px;
    }

    .node.a { fill: #1f77b4; }
    @@ -32,6 +34,7 @@
    .nodes(nodes)
    .links(links)
    .charge(-400)
    .linkDistance(120)
    .size([width, height])
    .on("tick", tick);

  9. @mbostock mbostock revised this gist Dec 8, 2012. 1 changed file with 4 additions and 5 deletions.
    9 changes: 4 additions & 5 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -31,6 +31,7 @@
    var force = d3.layout.force()
    .nodes(nodes)
    .links(links)
    .charge(-400)
    .size([width, height])
    .on("tick", tick);

    @@ -41,23 +42,21 @@
    var node = svg.selectAll(".node"),
    link = svg.selectAll(".link");

    start();

    // 1. Add three nodes and three links.
    setTimeout(function() {
    var a = {id: "a"}, b = {id: "b"}, c = {id: "c"};
    nodes.push(a, b, c);
    links.push({source: a, target: b}, {source: a, target: c}, {source: b, target: c});
    start();
    }, 2000);
    }, 0);

    // 2. Remove node bbb and associated links.
    setTimeout(function() {
    nodes.splice(1, 1); // remove b
    links.shift(); // remove a-b
    links.pop(); // remove b-c
    start();
    }, 4000);
    }, 3000);

    // Add node bbb back.
    setTimeout(function() {
    @@ -73,7 +72,7 @@
    link.exit().remove();

    node = node.data(force.nodes(), function(d) { return d.id;});
    node.enter().append("circle").attr("class", function(d) { return "node " + d.id; }).attr("r", 4.5);
    node.enter().append("circle").attr("class", function(d) { return "node " + d.id; }).attr("r", 8);
    node.exit().remove();

    force.start();
  10. @mbostock mbostock revised this gist Dec 8, 2012. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    This example demonstrates how to add and remove nodes and links from a force layout.
  11. @mbostock mbostock revised this gist Dec 8, 2012. 1 changed file with 10 additions and 4 deletions.
    14 changes: 10 additions & 4 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -11,6 +11,10 @@
    stroke: #fff;
    }

    .node.a { fill: #1f77b4; }
    .node.b { fill: #ff7f0e; }
    .node.c { fill: #2ca02c; }

    </style>
    <body>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    @@ -19,6 +23,8 @@
    var width = 960,
    height = 500;

    var color = d3.scale.category10();

    var nodes = [],
    links = [];

    @@ -39,7 +45,7 @@

    // 1. Add three nodes and three links.
    setTimeout(function() {
    var a = {id: "aaa"}, b = {id: "bbb"}, c = {id: "ccc"};
    var a = {id: "a"}, b = {id: "b"}, c = {id: "c"};
    nodes.push(a, b, c);
    links.push({source: a, target: b}, {source: a, target: c}, {source: b, target: c});
    start();
    @@ -55,19 +61,19 @@

    // Add node bbb back.
    setTimeout(function() {
    var a = nodes[0], b = {id: "bbb"}, c = nodes[1];
    var a = nodes[0], b = {id: "b"}, c = nodes[1];
    nodes.push(b);
    links.push({source: a, target: b}, {source: b, target: c});
    start();
    }, 4000);
    }, 6000);

    function start() {
    link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; });
    link.enter().insert("line", ".node").attr("class", "link");
    link.exit().remove();

    node = node.data(force.nodes(), function(d) { return d.id;});
    node.enter().append("circle").attr("class", "node").attr("r", 4.5);
    node.enter().append("circle").attr("class", function(d) { return "node " + d.id; }).attr("r", 4.5);
    node.exit().remove();

    force.start();
  12. @mbostock mbostock revised this gist Dec 8, 2012. 1 changed file with 56 additions and 86 deletions.
    142 changes: 56 additions & 86 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -1,116 +1,86 @@
    <!DOCTYPE html>
    <html>
    <head>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.1.3"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js?2.1.3"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?2.1.3"></script>
    <style type="text/css">
    <meta charset="utf-8">
    <style>

    .link {
    stroke: #ccc;
    }

    .nodetext {
    pointer-events: none;
    font: 10px sans-serif;
    .node {
    fill: #000;
    stroke: #fff;
    }

    </style>
    </head>
    <body>
    <script type="text/javascript">
    </style>
    <body>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script>

    var w = 960,
    h = 500;
    var width = 960,
    height = 500;

    var force = d3.layout.force()
    .gravity(.05)
    .distance(100)
    .charge(-100)
    .size([w, h]);

    var nodes = force.nodes(),
    links = force.links();

    var vis = d3.select("body").append("svg:svg")
    .attr("width", w)
    .attr("height", h);

    force.on("tick", function() {
    vis.selectAll("g.node")
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

    vis.selectAll("line.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; });
    });

    function restart() {
    var link = vis.selectAll("line.link")
    .data(links, function(d) { return d.source.id + "-" + d.target.id; });
    var nodes = [],
    links = [];

    link.enter().insert("svg:line", "g.node")
    .attr("class", "link");
    var force = d3.layout.force()
    .nodes(nodes)
    .links(links)
    .size([width, height])
    .on("tick", tick);

    link.exit().remove();
    var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

    var node = vis.selectAll("g.node")
    .data(nodes, function(d) { return d.id;});

    var nodeEnter = node.enter().append("svg:g")
    .attr("class", "node")
    .call(force.drag);

    nodeEnter.append("svg:image")
    .attr("class", "circle")
    .attr("xlink:href", "https://d3nwyuy0nl342s.cloudfront.net/images/icons/public.png")
    .attr("x", "-8px")
    .attr("y", "-8px")
    .attr("width", "16px")
    .attr("height", "16px");

    nodeEnter.append("svg:text")
    .attr("class", "nodetext")
    .attr("dx", 12)
    .attr("dy", ".35em")
    .text(function(d) { return d.id });
    var node = svg.selectAll(".node"),
    link = svg.selectAll(".link");

    node.exit().remove();

    force.start();
    }
    start();

    // Add three nodes and three links.
    function step1() {
    // 1. Add three nodes and three links.
    setTimeout(function() {
    var a = {id: "aaa"}, b = {id: "bbb"}, c = {id: "ccc"};
    nodes.push(a, b, c);
    links.push({source: a, target: b}, {source: a, target: c}, {source: b, target: c});
    restart();
    }
    start();
    }, 2000);

    // Remove node bbb and associated links.
    function step2() {
    // 2. Remove node bbb and associated links.
    setTimeout(function() {
    nodes.splice(1, 1); // remove b
    links.shift(); // remove a-b
    links.pop(); // remove b-c
    restart();
    }
    start();
    }, 4000);

    // Add node bbb back.
    function step3() {
    setTimeout(function() {
    var a = nodes[0], b = {id: "bbb"}, c = nodes[1];
    nodes.push(b);
    links.push({source: a, target: b}, {source: b, target: c});
    restart();
    start();
    }, 4000);

    function start() {
    link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; });
    link.enter().insert("line", ".node").attr("class", "link");
    link.exit().remove();

    node = node.data(force.nodes(), function(d) { return d.id;});
    node.enter().append("circle").attr("class", "node").attr("r", 4.5);
    node.exit().remove();

    force.start();
    }

    restart();
    setTimeout(step1, 2000);
    setTimeout(step2, 4000);
    setTimeout(step3, 6000);
    function tick() {
    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>
    </body>
    </html>
    </script>
  13. @mbostock mbostock revised this gist Oct 12, 2012. 1 changed file with 0 additions and 0 deletions.
    Binary file added thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  14. @mbostock mbostock revised this gist Sep 18, 2011. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -51,7 +51,7 @@
    var link = vis.selectAll("line.link")
    .data(links, function(d) { return d.source.id + "-" + d.target.id; });

    link.enter().append("svg:line")
    link.enter().insert("svg:line", "g.node")
    .attr("class", "link");

    link.exit().remove();
  15. @mbostock mbostock revised this gist Sep 18, 2011. 1 changed file with 85 additions and 114 deletions.
    199 changes: 85 additions & 114 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -1,145 +1,116 @@
    <!DOCTYPE html>
    <html>
    <head>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js"></script>
    <style type="text/css">
    .link { stroke: #ccc; }
    .nodetext { pointer-events: none; font: 10px sans-serif; }
    </style>
    </head>
    <body>
    <script type="text/javascript">
    <head>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.1.3"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js?2.1.3"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?2.1.3"></script>
    <style type="text/css">

    .link {
    stroke: #ccc;
    }

    .nodetext {
    pointer-events: none;
    font: 10px sans-serif;
    }

    </style>
    </head>
    <body>
    <script type="text/javascript">

    var w = 960,
    h = 500
    h = 500;

    var force = d3.layout.force()
    .gravity(.05)
    .distance(100)
    .charge(-100)
    .size([w, h]);

    var nodes = [],
    links = [];
    var nodes = force.nodes(),
    links = force.links();

    var vis = d3.select("body").append("svg:svg")
    .attr("width", w)
    .attr("height", h);

    var force = self.force = d3.layout.force()
    .nodes(nodes)
    .links(links)
    .gravity(.05)
    .distance(100)
    .charge(-100)
    .size([w, h]);

    force.on("tick", function() {
    var node = vis.selectAll("g.node")
    .data(nodes, function(d) { return d.id;} )
    var link = vis.selectAll("line.link")
    .data(links, function(d) { return d.source.id + ',' + d.target.id})
    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; });
    node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
    vis.selectAll("g.node")
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

    vis.selectAll("line.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; });
    });

    function recalc() {
    var link = vis.selectAll("line.link")
    .data(links, function(l) { return l.source.id + '-' + l.target.id; });
    function restart() {
    var link = vis.selectAll("line.link")
    .data(links, function(d) { return d.source.id + "-" + d.target.id; });

    link.enter().append("svg:line")
    .attr("class", "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; });
    link.enter().append("svg:line")
    .attr("class", "link");

    link.exit().remove();
    var node = vis.selectAll("g.node")
    .data(nodes, function(d) { return d.dpid;}).call(force.drag);
    link.exit().remove();

    var node = vis.selectAll("g.node")
    .data(nodes, function(d) { return d.id;});

    var nodeEnter = node.enter().append("svg:g")
    .attr("class", "node")
    .call(force.drag);
    var nodeEnter = node.enter().append("svg:g")
    .attr("class", "node")
    .call(force.drag);

    nodeEnter.append("svg:image")
    .attr("class", "circle")
    .attr("xlink:href", "https://d3nwyuy0nl342s.cloudfront.net/images/icons/public.png")
    .attr("x", "-8px")
    .attr("y", "-8px")
    .attr("width", "16px")
    .attr("height", "16px");

    nodeEnter.append("svg:text")
    .attr("class", "nodetext")
    .attr("dx", 12)
    .attr("dy", ".35em")
    .text(function(d) { return d.id });

    node.exit().remove();
    nodeEnter.append("svg:image")
    .attr("class", "circle")
    .attr("xlink:href", "https://d3nwyuy0nl342s.cloudfront.net/images/icons/public.png")
    .attr("x", "-8px")
    .attr("y", "-8px")
    .attr("width", "16px")
    .attr("height", "16px");

    nodeEnter.append("svg:text")
    .attr("class", "nodetext")
    .attr("dx", 12)
    .attr("dy", ".35em")
    .text(function(d) { return d.id });

    node.exit().remove();

    force
    .nodes(nodes)
    .links(links)
    .start();

    force.start();
    }


    /* Scenario */

    /* step 1: add three nodes and three links */
    // Add three nodes and three links.
    function step1() {
    var nA = {id: 'aaa'};
    var nB = {id: 'bbb'};
    var nC = {id: 'ccc'};
    nodes.push(nB);
    nodes.push(nA);
    nodes.push(nC);

    var lAB = {source: nA, target: nB};
    var lAC = {source: nA, target: nC};
    var lBC = {source: nB, target: nC};
    links.push(lAB );
    links.push(lAC);
    links.push(lBC);

    recalc();
    var a = {id: "aaa"}, b = {id: "bbb"}, c = {id: "ccc"};
    nodes.push(a, b, c);
    links.push({source: a, target: b}, {source: a, target: c}, {source: b, target: c});
    restart();
    }

    /* step 2: node B disappears with links */
    // Remove node bbb and associated links.
    function step2() {
    nodes = nodes.filter(function(n) { return n.id !== 'bbb'; });
    links = links.filter(function(l) { return (l.source.id !== 'bbb' && l.target.id !== 'bbb'); });

    recalc();
    nodes.splice(1, 1); // remove b
    links.shift(); // remove a-b
    links.pop(); // remove b-c
    restart();
    }

    /* step 3: node B reappears with links */
    // Add node bbb back.
    function step3() {
    var nB = {id: 'bbb'};

    nodes.push(nB);

    /* find exiting nodes for links */
    var nA = nodes.filter(function(n) { return n.id === 'aaa'; })[0];
    var nC = nodes.filter(function(n) { return n.id === 'ccc'; })[0];

    var lAB = {source: nA, target: nB};
    var lBC = {source: nB, target: nC};
    links.push(lAB);
    links.push(lBC);

    recalc();
    var a = nodes[0], b = {id: "bbb"}, c = nodes[1];
    nodes.push(b);
    links.push({source: a, target: b}, {source: b, target: c});
    restart();
    }

    window.setTimeout(step1, 2000);
    window.setTimeout(step2, 4000);
    window.setTimeout(step3, 6000);

    force.start();
    recalc();
    restart();
    setTimeout(step1, 2000);
    setTimeout(step2, 4000);
    setTimeout(step3, 6000);

    </script>
    </body>
    </script>
    </body>
    </html>
  16. @mbostock mbostock revised this gist Sep 18, 2011. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -93,8 +93,8 @@
    var nA = {id: 'aaa'};
    var nB = {id: 'bbb'};
    var nC = {id: 'ccc'};
    nodes.push(nA);
    nodes.push(nB);
    nodes.push(nA);
    nodes.push(nC);

    var lAB = {source: nA, target: nB};
  17. @mbostock mbostock revised this gist Jul 20, 2011. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -78,7 +78,10 @@

    node.exit().remove();

    force.start();
    force
    .nodes(nodes)
    .links(links)
    .start();

    }

  18. @mbostock mbostock renamed this gist Jul 20, 2011. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  19. @ZoltanLajosKis ZoltanLajosKis created this gist Jul 20, 2011.
    142 changes: 142 additions & 0 deletions d3jsproblem.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,142 @@
    <!DOCTYPE html>
    <html>
    <head>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js"></script>
    <style type="text/css">
    .link { stroke: #ccc; }
    .nodetext { pointer-events: none; font: 10px sans-serif; }
    </style>
    </head>
    <body>
    <script type="text/javascript">

    var w = 960,
    h = 500

    var nodes = [],
    links = [];

    var vis = d3.select("body").append("svg:svg")
    .attr("width", w)
    .attr("height", h);

    var force = self.force = d3.layout.force()
    .nodes(nodes)
    .links(links)
    .gravity(.05)
    .distance(100)
    .charge(-100)
    .size([w, h]);

    force.on("tick", function() {
    var node = vis.selectAll("g.node")
    .data(nodes, function(d) { return d.id;} )
    var link = vis.selectAll("line.link")
    .data(links, function(d) { return d.source.id + ',' + d.target.id})
    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; });
    node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
    });

    function recalc() {
    var link = vis.selectAll("line.link")
    .data(links, function(l) { return l.source.id + '-' + l.target.id; });

    link.enter().append("svg:line")
    .attr("class", "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; });

    link.exit().remove();

    var node = vis.selectAll("g.node")
    .data(nodes, function(d) { return d.dpid;}).call(force.drag);

    var nodeEnter = node.enter().append("svg:g")
    .attr("class", "node")
    .call(force.drag);

    nodeEnter.append("svg:image")
    .attr("class", "circle")
    .attr("xlink:href", "https://d3nwyuy0nl342s.cloudfront.net/images/icons/public.png")
    .attr("x", "-8px")
    .attr("y", "-8px")
    .attr("width", "16px")
    .attr("height", "16px");

    nodeEnter.append("svg:text")
    .attr("class", "nodetext")
    .attr("dx", 12)
    .attr("dy", ".35em")
    .text(function(d) { return d.id });

    node.exit().remove();

    force.start();

    }


    /* Scenario */

    /* step 1: add three nodes and three links */
    function step1() {
    var nA = {id: 'aaa'};
    var nB = {id: 'bbb'};
    var nC = {id: 'ccc'};
    nodes.push(nA);
    nodes.push(nB);
    nodes.push(nC);

    var lAB = {source: nA, target: nB};
    var lAC = {source: nA, target: nC};
    var lBC = {source: nB, target: nC};
    links.push(lAB );
    links.push(lAC);
    links.push(lBC);

    recalc();
    }

    /* step 2: node B disappears with links */
    function step2() {
    nodes = nodes.filter(function(n) { return n.id !== 'bbb'; });
    links = links.filter(function(l) { return (l.source.id !== 'bbb' && l.target.id !== 'bbb'); });

    recalc();
    }

    /* step 3: node B reappears with links */
    function step3() {
    var nB = {id: 'bbb'};

    nodes.push(nB);

    /* find exiting nodes for links */
    var nA = nodes.filter(function(n) { return n.id === 'aaa'; })[0];
    var nC = nodes.filter(function(n) { return n.id === 'ccc'; })[0];

    var lAB = {source: nA, target: nB};
    var lBC = {source: nB, target: nC};
    links.push(lAB);
    links.push(lBC);

    recalc();
    }

    window.setTimeout(step1, 2000);
    window.setTimeout(step2, 4000);
    window.setTimeout(step3, 6000);

    force.start();
    recalc();

    </script>
    </body>
    </html>