1

In this D3 example:

enter image description here

nodes have variable radius. I managed to move node labels so that they are always just next to their circles.

However, how to move arrows? (You can see that for Microsoft, Apple etc, they are almost covered by the circle)

Related questions:

here

here

here

here

Community
  • 1
  • 1
VividD
  • 10,456
  • 6
  • 64
  • 111
  • 1
    The arrows are offset by moving the reference point (relative to which the marker is drawn into each path) away from 0,0. This is done via .attr("refX", 15). Increasing that number will move the arrowhead away. Note that this technique is imperfect: First, it applies the same refX to all the markers. Since you need it to depend on the node radius, you'll have to do extra work create and create one mark per circle. Second, the further you move the arrow, the less its rotation matches the path tangent. Making the links straight lines avoids this issue. – meetamit Jan 23 '15 at 03:54

1 Answers1

2

I search online, none of the answer worked, so I made my own:

Here is the code:

   //arrows
svg.append("defs").selectAll("marker")
    .data(["suit", "licensing", "resolved"])
    .enter().append("marker")
    .attr("id", function(d) { return d; })
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 9)
    .attr("refY", 0)
    .attr("markerWidth", 10)
    .attr("markerHeight", 10)
    .attr("orient", "auto")
    .append("path")
    .attr("d", "M0,-5L10,0L0,5 L10,0 L0, -5")
    .style("stroke", "#4679BD")
    .style("opacity", "0.6"); 

  //Create all the line svgs but without locations yet
var link = svg.selectAll(".link")
   .data(forceData.links)
   .enter().append("line")
   .attr("class", "link")
   .style("marker-end", "url(#suit)");

//Set up the force layout
var force = d3.layout.force()
    .nodes(forceData.nodes)
    .links(forceData.links)
    .charge(-120)
    .linkDistance(200)
    .size([width, height])
    .on("tick", tick)
    .start();

function tick(){
    link.attr("x1", function (d) { return d.source.x; })
        .attr("y1", function (d) { return d.source.y; })
        .attr("x2", function (d) { 
            return calculateX(d.target.x, d.target.y, d.source.x, d.source.y, d.target.radius); 
        })
        .attr("y2", function (d) { 
            return calculateY(d.target.x, d.target.y, d.source.x, d.source.y, d.target.radius);
        });

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

    d3.select("#forcelayoutGraph").selectAll("text")
        .attr("x", function (d) { return d.x; })
        .attr("y", function (d) { return d.y; });
}
function calculateX(tx, ty, sx, sy, radius){
    if(tx == sx) return tx;                 //if the target x == source x, no need to change the target x.
    var xLength = Math.abs(tx - sx);    //calculate the difference of x
    var yLength = Math.abs(ty - sy);    //calculate the difference of y
    //calculate the ratio using the trigonometric function
    var ratio = radius / Math.sqrt(xLength * xLength + yLength * yLength);
    if(tx > sx)  return tx - xLength * ratio;    //if target x > source x return target x - radius
    if(tx < sx) return  tx + xLength * ratio;    //if target x < source x return target x + radius
}
function calculateY(tx, ty, sx, sy, radius){
    if(ty == sy) return ty;                 //if the target y == source y, no need to change the target y.
    var xLength = Math.abs(tx - sx);    //calculate the difference of x
    var yLength = Math.abs(ty - sy);    //calculate the difference of y
    //calculate the ratio using the trigonometric function
    var ratio = radius / Math.sqrt(xLength * xLength + yLength * yLength);
    if(ty > sy) return ty - yLength * ratio;   //if target y > source y return target x - radius
    if(ty < sy) return ty + yLength * ratio;   //if target y > source y return target x - radius
}
Kreedz Zhen
  • 380
  • 2
  • 12