Pierre
Pierre

Reputation: 943

How to attach nodes and links to simulation force in d3js version 4?

I try to port to d4.v4.js some graph drawing built with version 3. I quite don't understand how to attach svg nodes and links to the simulation with atlas force. The simplified version using d3.v3 is shared on fiddle, note that all nodes are grouped in a <g> element, and each node, simplified as a circle in this example is inserted in a <g> element as well. Similarly, links are grouped in a <g> element. I need those to draw more complexe networks.

svg.append("g").attr("class", "links");
svg.append("g").attr("class", "nodes");

var force = d3.layout.force()
.gravity(.15)
.distance(100)
.charge(-1200)
.friction(0.8)
.size([width, height]);

// push initial sets of nodes and links
var nodes = force.nodes(), links = force.links();
Array.prototype.push.apply(nodes, data.nodes);
Array.prototype.push.apply(links, data.edges);

My reference for drawing a network with d3.v4 in shared on fiddle as well, it actually works, however each node is not in a <g> element as required (I guess) for my more complex graphs.

This last fiddle shows the d3.v4 version of the simplified graph, as one can see, nodes remain in the upper left corner, only zoom works.

var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom);

var simulation = d3.forceSimulation()
.force("link", d3.forceLink()
       .id(function(d) { return d.id; })
       .distance(function(d) { return (1-d.value*d.value*d.value)*20; }))
.force("charge", d3.forceManyBody().strength(-200))
.force("center", d3.forceCenter(width / 2, height / 2));

var pane = svg.append("g");

var link = pane.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return d.value*d.value*d.value*10; });

var node = pane.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", function(d) { return d.value*5   })
.style("fill", function(d) { return color(d.group); })

My question is how should I attach nodes (<g>) and links to "simulation"? What is wrong in my script? Many thanks in advance for your help.

Upvotes: 3

Views: 1056

Answers (1)

miro marchi
miro marchi

Reputation: 737

Let's take your last fiddle with the d3v4 implementation. 2 changes and it will work.

1) the ticked function to move the elements. Since nodes are svg groups now, you can't use cx and cy attributes (good for circles). You need to use transform translate like this:

node
  .attr("transform", function(d) { 
    return "translate(" + d.x + ", " + d.y + ")"; 
  });

2) The node variable there contains only the update selection, since you called the enter selection nodeEnter. And since all elements are in the enter selection (just created), you don't move anything. So you just need to merge the two selections like this:

nodeEnter.append("circle")
  .attr("r", function(d) { return d.value*10 })
node.exit().remove();
node = nodeEnter.merge(node);

You can read more details in Mike Bostock documentation about this. Check also this and other similar bl.ocks.

To correctly target the links in ticked you should also merge enter and update link selections in the same way.

As a side note, you don't need to link source and target objects in the links as you do in getGraph function, since this is done by d3 simulation already. So you could just use your json object as data.

Here is a working fiddle. Hope it is clearer how to upgrade d3 v3 force directed layout to d3 v4 force simulation :D

Upvotes: 1

Related Questions