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 : /
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.
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/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.
Share Improve this question edited May 23, 2017 at 11:58 CommunityBot 11 silver badge asked May 24, 2016 at 15:09 thatOneGuythatOneGuy 10.7k9 gold badges58 silver badges92 bronze badges2 Answers
Reset to default 6The 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/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).
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/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)