score:9

Accepted answer

The Problem

This jsFiddle demonstrates the problem in question. The values of the marker def are in relation to the stoke width of the element it is attached to (the link lines in this case). See the markerUnits spec. By using strokeWidth for the markerUnits attribute the coordinates for the different size arrows will be slightly different. In short the appropriate values for one size arrow will not correctly translate to the other sizes.

Solution 1: Multiple Markers

As mentioned in the comments one solution would be to create a different marker for each strokeWidth that's needed. This would work only if you knew up front what sizes were needed.

Solution 2: Modify the Lines

Another option would be to modify the end point for the lines. Instead of allowing the lines to terminate in the center of a node, terminate it on the outside edge of the node. This jsFiddle demonstrates this. This alleviates the need to shift the arrow head, as we can now just draw it at the end of the line.

This solution involves some math to figure out what the x2 and y2 values of the line should be. Thus, it may not be ideal for systems with a large number of edges.

var nodeRadius = 10;
var lineX2 = function (d) {
    var length = Math.sqrt(Math.pow(d.target.y - d.source.y, 2) + Math.pow(d.target.x - d.source.x, 2));
    var scale = (length - nodeRadius) / length;
    var offset = (d.target.x - d.source.x) - (d.target.x - d.source.x) * scale;
    return d.target.x - offset;
};
var lineY2 = function (d) {
    var length = Math.sqrt(Math.pow(d.target.y - d.source.y, 2) + Math.pow(d.target.x - d.source.x, 2));
    var scale = (length - nodeRadius) / length;
    var offset = (d.target.y - d.source.y) - (d.target.y - d.source.y) * scale;
    return d.target.y - offset;
};

var link = svg.selectAll("line.link")
    .data(graph.links)
    .enter().append("svg:line")
    .attr("class", "link")
    .style("stroke-width", function (d) {
      return Math.sqrt(d.value);
    })
    .attr("x1", function (d) {
       return d.source.x;
    })
    .attr("y1", function (d) {
        return d.source.y;
    })
    .attr("x2", lineX2)
    .attr("y2", lineY2)
    .attr("marker-end", "url(#arrowGray)")
    .on("click", function (d) {
        link.style("stroke", "#dddddd");
        node.style("stroke", "#FFFFFF");
        d3.select(this).style("stroke", "red");
        link.attr("marker-end", null);
        link.attr("marker-end", "url(#arrowGray)");
        d3.select(this).attr("marker-end", null);
        d3.select(this).attr("marker-end", "url(#arrowRed)");
    });

var defs = svg.append('defs');
defs.append("svg:marker")
    .attr("id", "arrowGray")
    .attr("viewBox", "0 0 10 10")
    .attr("refX", "10")
    .attr("refY", "5")
    .attr("markerUnits", "strokeWidth")
    .attr("markerWidth", "10")
    .attr("markerHeight", "5")
    .attr("orient", "auto")
    .append("svg:path")
    .attr("d", "M 0 0 L 10 5 L 0 10 z")
    .attr("fill", "#000");

var node = svg.selectAll(".node")
    .data(graph.nodes)
    .enter().append("circle")
    .attr("class", "node")
    .attr("r", nodeRadius)
    .style("fill", function (d) {
        return color(d.group);
    })
    .call(force.drag);

node.append("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", lineX2)
    .attr("y2", lineY2)

    node.attr("cx", function (d) {
        return d.x;
    })
    .attr("cy", function (d) {
        return d.y;
    });
});

score:1

Your refX is probably too large. Try setting it to something around 1.


Related Query