Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing multiple links between fixed nodes

I have a force directed graph with links between each nodes. Now some node pairs have multiple links going to each other. I have found this example : Drawing multiple edges between two nodes with d3.

This worked great, I thought. But if you have fixed nodes and drag, the paths end up overlapping each other. I have put together an edited version of this example : http://jsfiddle.net/thatOneGuy/7HZcR/502/

Click the button to fix the nodes and move them around to see what I mean.

Code for working out amount of arc :

//sort links by source, then target
links.sort(function(a,b) {
    if (a.source > b.source) {return 1;}
    else if (a.source < b.source) {return -1;}
    else {
        if (a.target > b.target) {return 1;}
        if (a.target < b.target) {return -1;}
        else {return 0;}
    }
});
//any links with duplicate source and target get an incremented 'linknum'
for (var i=0; i<links.length; i++) {
    if (i != 0 &&
        links[i].source == links[i-1].source &&
        links[i].target == links[i-1].target) {
            links[i].linknum = links[i-1].linknum + 1;
        }
    else {links[i].linknum = 1;};
};

Can anyone think of another way of doing this or fixing this way maybe ? I could have 3 maybe even 4 links between two nodes.

like image 935
thatOneGuy Avatar asked May 24 '16 15:05

thatOneGuy


2 Answers

The important code is the one giving the radius of the arc. I propose the following function:

path.attr("d", function(d) {
 var curve=2;
 var homogeneous=3.2;
 var dx = d.target.x - d.source.x,
     dy = d.target.y - d.source.y,
     dr = Math.sqrt(dx*dx+dy*dy)*(d.linknum+homogeneous)/(curve*homogeneous);  //linknum is defined above
 return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});

The main difference is that it scales linearly with the node distance (which I think is best). Then, there are these two parameters which I call curve and homogeneous: you should play with them until you find suitable values.

See http://jsfiddle.net/7HZcR/504/

PS: the overlap happens when the radius given for the arc is smaller than half the distance between the nodes (then the radius is increased to reach this value, and all arcs get the same radius).

like image 143
tarulen Avatar answered Nov 06 '22 03:11

tarulen


Based on the solutions above I have a refined solution that has links evenly spread out, using the sweep parameter of the ellipse: http://jsfiddle.net/bigman73/v03x572h/

function tick() { 
   path.attr("d", function(d) { 
   let dx = d.target.x - d.source.x,
       dy = d.target.y - d.source.y,
       t = Math.sqrt(dx * dx + dy * dy),
       half_n = Math.floor(links.length / 2) + links.length % 2,
       dr =  d.linknum == half_n ? t * 100 : 2.25 * t * (half_n - 
       Math.abs(d.linknum - half_n)) / links.length,
       sweep = d.linknum <= half_n ? 1 : 0;
       return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0," + sweep + " " + d.target.x + "," + d.target.y;

});

The curve shape is currently hard coded to 2.25, but that's easy to refactor. It should also work generically with different number of links (though I only tested on 3)

like image 30
BigMan73 Avatar answered Nov 06 '22 01:11

BigMan73