Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 SVG positioning arrowhead on path [duplicate]

I'm visualizing a force lay-out network with D3 and I have a problem with positioning the arrowheads along the edges between my nodes. As you can see in the picture, I am scaling the size of my nodes according to the value of a property of each node. Basically I need some sort of method to dynamically caculate/change the position of my arrowhead on my edges (according to same value used for scaling the nodes) to make them visible and prevent them from overlapping with the nodes. Actually I want my arrowheads to "touch" the outer edge of my nodes. Does anyone a way to do this? These code fragments show how I create my arrowheads. Maybe I should use another way?

p.s. I know I can change to order of my drawings to draw the arrowheads on top of my nodes but that's not what I want.

enter image description here

...

svg.append("defs").append("marker")
.attr("id", "arrowhead")
.attr("refX", 5) /*must be smarter way to calculate shift*/
.attr("refY", 2)
.attr("markerWidth", 6)
.attr("markerHeight", 4)
.attr("orient", "auto")
.append("path")
    .attr("d", "M 0,0 V 4 L6,2 Z");

...

 path.enter()
        .append("svg:path")
        .attr("class", function (d) {
            return "link " + d.type;
        })
        .attr("id", function (d) {
            return "path_" + d.id;
        })
        .attr("marker-end", "url(#arrowhead)")
        .transition().duration(8000)
        .style("stroke-width", stylePathStrokeWidth)
...
like image 449
Bavo Devocht Avatar asked Mar 09 '23 17:03

Bavo Devocht


1 Answers

I solved the problem based on Mark's answer to this question Align Marker on node edges D3 Force Layout. This is my code inside the tick function:

function tick() {

// fit path like you've been doing
path.attr("d", function (d, i) {
    dr = 550 / d.linknum;
    var result = "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
    return result;
});

// recalculate and back off the distance
path.attr("d", function (d, i) {  
    var pl = this.getTotalLength();
    //this is the magic
    var r = d.target.size + 3 * d.size; // Multiply the marker size (3) with the size of the edge (d.size) because the markers are scaling with the edge which they are attached to!!
    var m = this.getPointAtLength(pl - r);
    dr = 550 / d.linknum;
    var result = "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + m.x + "," + m.y;
    return result;
});
like image 83
Bavo Devocht Avatar answered Mar 20 '23 19:03

Bavo Devocht