54

So i have a page already which draws a force directed graph, like the one shown here.

And that works fine. I'm using the JS from here, with a few tweaks to spread out the nodes slightly nicer.

These are more or less the only differences:

d3.json("force.json", function(json) {
  var force = d3.layout.force()
      .gravity(0.1)
      .charge(-2000)
      .linkDistance(1)
      .linkStrength(0.1)
      .nodes(json.nodes)
      .links(json.links)
      .size([w, h])
      .start();

Where reducing the link strength seems to make the links more like springs, so it becomes similar to the Fruchterman & Reingold technique often used. This works reasonably well, but only for fairly small graphs. With larger graphs the number of crossings just goes up - as one would expect, but the solution it lands on is normally far from optimal. I'm not looking for a method to get the optimal solution, I know that's very difficult. I would just like it to have some crude addition that tries to force the lines apart as well as the nodes.

Is there a way to add a repulsion between in links, as well as between the nodes? I'm not familiar with the way D3 force works, and i can't seem to find anything that says this is possible...

VividD
  • 10,456
  • 6
  • 64
  • 111
will
  • 10,260
  • 6
  • 46
  • 69
  • Unfortunately not. I didn't go delving into the js though, as I'm not familiar with the language. I did try using an actual Fruchterman & Reingold technique, but the result still were not as good as if I moved the nodes about by hand. – will Oct 22 '12 at 09:34
  • @pocketfullofcheese - I actually was using networkX (a python module) and matplotlib, but they have a D3.js example on their website that looks quite nice, that's why i tried it. – will Oct 22 '12 at 11:51
  • Visualising large graphs *is* difficult... I know this does not solve the problem, but maybe you find my networkX port helpful: http://felix-kling.de/JSNetworkX/. It uses d3 as well and lets you easily zoom in and out at parts of the graphs, which can make it easier to examine it. – Felix Kling Oct 28 '12 at 08:41
  • @FelixKling does this add any functionality to the two other than the zooming in and out? It's not that my graphs are *big* persay, more that they're quite highly connectd - They're somewhere in the region of 40 nodes – will Oct 28 '12 at 10:55
  • @will: Well, it's supposed to be like networkX but in JavaScript, so it should make manipulating graphs and drawing them easy. You can also create directed graphs (if that's what you have). I was experimenting with an option that only draws the edges of a node when one hovers over it (because of exactly that reason, too many edges clutter the view), but it's not in the source yet. If you are interested in this, I can put it in or send you a specially built version. – Felix Kling Oct 28 '12 at 17:30
  • @FelixKling to be honest i was just looking for it out of curiosity. Since i looked at it i read up on it a bit more, and it seemed like it was a much more complicated problem than i'd hoped... – will Oct 28 '12 at 23:20
  • @will i don't think any inter-edge dynamics is implemented in d3js, only inter-nodes. – Falcon Nov 27 '12 at 21:14
  • have you tried computing the layout based on the dual of the graph and seeing what that gives you? Probably won't work, but could be interesting. – Superboggly Nov 27 '12 at 21:47

4 Answers4

15

Unfortunately, the answer to your question does not exist.

There is no built-in mechanism in D3 that repels edges or minimizes edge crossings. You would think it wouldn't be that hard to implement a charge on an edge, but here we are.

Furthermore, there doesn't seem to be any mechanism anywhere that reduces edge crossings in general. I've looked through dozens of visualization libraries and layout algorithms, and none of them deal with reducing edge crossings on a generic undirected graph.

There are a number of algorithms that work well for planar graphs, or 2-level graphs, or other simplifications. dagre works well in theory for 2-level graphs, although the utter lack of documentation makes it almost impossible to work with.

Part of the reason for this is that laying out graphs is hard. In particular, minimizing edge crossings is NP-hard, so I suspect that most layout designers hit that problem, bang their head against the keyboard a few times, and give up.

If anyone does come up with a good library for this, please publish it for the rest of us :)

GreySage
  • 1,153
  • 19
  • 39
  • 2
    This is the conclusion i came to. Managers don't like the answer "Can you make this graph look bigger?" of "No. It really is a *hard* problem.". You have a piece of software that can simulate the phyiscs inside complicated machines, but you can't draw the output in a any pleasant way. Shame. – will Jul 01 '16 at 08:59
  • on another note though, [graphviz](http://www.graphviz.org/) is now available under the [epl](https://tldrlegal.com/license/eclipse-public-license-1.0-(epl-1.0)) license. – will Jul 01 '16 at 09:03
6

Something that might be easier than trying to forcefully repel the edges is to wiggle the nodes around until the amount of crossing lines in the system is lower.

http://en.wikipedia.org/wiki/Simulated_annealing

Start with the nodes with the least amount of connections and wiggle down.

If you try and use the edges as nodes I suspect you're just going to get the same spatial locking problems. The solution is in figuring out where there are edge intersections and if they can be resolved. You might find that resolving many of the edge crossings is not possible

A more lateral approach to the visualization is to animate it such that it only shows a subset of the nodes and connections at a time. Or to make the edges transparent until the user places mouse focus over a node, which point the associated edges become more visible.

VoronoiPotato
  • 3,113
  • 20
  • 30
0

I followed the Force Editor example and I saw that setting charge and linkDistance values solves the problem.

  ...
  .charge(-200)
  .linkDistance(50)
  ...

Screenshot:

enter image description here

Ionică Bizău
  • 109,027
  • 88
  • 289
  • 474
  • 2
    All changing the carge does is change the force that the points repel each other by. linkDistance jsut changes the ideal length of the poitns. This works fine in a graph like the example you give, but when you have a very highly connected one like i do, it does not work. I'll update the question on monday with some real example data to illustrate the problem. It's a chemistry and reaction data set, so al lthe points are linked to several other poitns via special *reaction* points. – will Aug 10 '14 at 16:48
  • @will Notify me after you update the question. Maybe I can help. I use this library in an application. – Ionică Bizău Aug 10 '14 at 17:00
  • Yah, i looked at using it, but found that it basically wasn't the right tool for the job, the graphs i want to represent are simply too connected. – will Aug 10 '14 at 17:06
-2

I have 'solved' the problem with this:

nodes[0].x = width / 2;
nodes[0].y = 100;
nodes[0].fixed = true;
force.on("tick", function(e) {

    var kx = .4 * e.alpha, ky = 1.4 * e.alpha;
    links.forEach(function(d, i) {
      d.target.x += (d.source.x - d.target.x) * kx;
      d.target.y += (d.source.y + 80 - d.target.y) * ky;
    });
    [...]
 }

http://mbostock.github.io/d3/talk/20110921/parent-foci.html

It's not exactly what we wanted but better as before. Importend is, that you define a "root"-Node and fixed it.

nodes[0].fixed = true;

It look like more as a tree but so it is clearer.

Baum
  • 95
  • 10
  • This doesn't solve it for my problem. For your problem it may work, but i'm not sure it would have worked for mine, since my grapgh was very highly connected, while yours is a tree. – will Sep 13 '17 at 09:17
  • 1
    The problem with your answer is that your graph could actually be layouted without any crossing edge! And even worse in your example there are several edges crossing each other. – Yoshi Jan 31 '19 at 13:38