简体   繁体   中英

d3.js force-directed graph maintain constant link distances

Does anybody have an idea of how to maintain constant link distances while at the same time repulsing nodes?

Here's an example of the problem (this is the standard FDG example, but with fewer nodes).

var graph = {
  "nodes":[
    {"name":"a","group":1},
      {"name":"a","group":1},
      {"name":"a","group":1},
      {"name":"a","group":1},
    {"name":"b","group":8}
  ],
  "links":[
    {"source":1,"target":0,"value":1},
      {"source":2,"target":0,"value":1},
      {"source":3,"target":0,"value":1},
      {"source":4,"target":0,"value":1}
  ]
};
var width = 300,
    height = 300;

var color = d3.scale.category20();

var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);

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

var drawGraph = function(graph) {
  force
      .nodes(graph.nodes)
      .links(graph.links)
      .start();

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

  var gnodes = svg.selectAll('g.gnode')
  .data(graph.nodes)
  .enter()
  .append('g')
  .classed('gnode', true)
  .call(force.drag);

  var node = gnodes.append("circle")
      .attr("class", "node")
      .attr("r", 5)
      .style("fill", function(d) { return color(d.group); });

  node.append("title")
      .text(function(d) { return d.name; });

   var labels = gnodes.append("text")
              .text(function(d) { return d.name; })
              .attr('text-anchor', 'middle')
              .attr('font-size', 8.0)
              .attr('font-weight', 'bold')
              .attr('y', 2.5)
              .attr('fill', d3.rgb(50,50,50))
              .attr('class', 'node-label')
              .append("svg:title")
              .text(function(d) { return d.name; });

  force.on("tick", function() {
    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; })
    .each(function(d) { console.log(Math.sqrt((d.source.x - d.target.x) * (d.source.x - d.target.x) + (d.source.y - d.target.y) * (d.source.y - d.target.y))); });

                  gnodes.attr("transform", function(d) {
                      return 'translate(' + [d.x, d.y] + ')';
                  });
  });
};

drawGraph(graph);

http://jsfiddle.net/pkerpedjiev/vs3foo80/1/

There's one central node and four attached nodes. The links should all have a length of 30, but because of the repulsion forces, they settle down to lengths of 35. Is there a way to counteract that and make the link lengths to converge to their desired values of 30 while maintaining the repulsion between non-connected nodes?

This would be akin to making the link force much stronger than the repulsion force. Increasing that, however, leads to very unstable behaviour.

Another way of putting this question is, is there a way to spread the nodes as far apart from each other while maintaining the desired link lengths?

Yes, use .chargeDistance(30) . The .chargeDistance() setting determines the maximum distance when charge is applied, and is infinite by default. A setting of 30 set your charge to only apply to nodes that are within 30px, and should give you the behavior you want.

The drawback of this is that on a large graph, you will no longer see add-on effects that unfold the graph faster and the layout will have a more localized appearance. To achieve something like that, I would suggest experimenting with a dynamic chargeDistance tied to the alpha parameter of the force algorithm (the cooldown) so that it starts at infinite and then moves toward 30 (or whatever) as the graph cools down.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM