I have a d3 network where points are connected by lines. I want to replace the lines with curved SVG paths. I have forgotten the math to calculate the control point's coordinates. Does anyone know how to do this?
For example, look at the image below:

There exist points A and B. I have them connected at present by a line L. I want to replace L with a curve, C. To do that I need to find a line that is perpendicular to the mid-point of line L, of length M (length set as a percentage of L), to be the control point of spline C. Then I need to define an SVG path to define C.
How do I do this in d3 with SVG? I've done this before in Raphael/SVG a long time ago, but the math escapes me. And I'm not sure how its done in D3.
Just to be clear for others, what we're talking about is a quadratic Bezier curve. That gives you a smooth curve between two points with one control point.
The basic method is:

Here's a JavaScript function to return the path you'll need:
function draw_curve(Ax, Ay, Bx, By, M) {
    // Find midpoint J
    var Jx = Ax + (Bx - Ax) / 2
    var Jy = Ay + (By - Ay) / 2
    // We need a and b to find theta, and we need to know the sign of each to make sure that the orientation is correct.
    var a = Bx - Ax
    var asign = (a < 0 ? -1 : 1)
    var b = By - Ay
    var bsign = (b < 0 ? -1 : 1)
    var theta = Math.atan(b / a)
    // Find the point that's perpendicular to J on side
    var costheta = asign * Math.cos(theta)
    var sintheta = asign * Math.sin(theta)
    // Find c and d
    var c = M * sintheta
    var d = M * costheta
    // Use c and d to find Kx and Ky
    var Kx = Jx - c
    var Ky = Jy + d
    return "M" + Ax + "," + Ay +
           "Q" + Kx + "," + Ky +
           " " + Bx + "," + By
}
You can see this in action at this jsfiddle or the snippet (below).
Edit: If a quadratic curve doesn't fit, you can pretty easily adapt the function to do cubic Bezier or arc segments.
var adjacencyList = {
  1: [2],
  2: [3],
  3: [1],
};
var nodes = d3.values(adjacencyList),
  links = d3.merge(nodes.map(function(source) {
    return source.map(function(target) {
      return {
        source: source,
        target: adjacencyList[target]
      };
    });
  }));
var w = 960,
  h = 500;
var M = 50;
var vis = d3.select("#svg-container").append("svg")
  .attr("width", w)
  .attr("height", h);
var force = d3.layout.force()
  .nodes(nodes)
  .links(links)
  .size([w, h])
  .linkDistance(100)
  .charge(-100)
  .start();
var link = vis.selectAll(".link")
  .data(links)
  .enter().append("svg:path")
  .attr("class", "link");
console.log(link)
var node = vis.selectAll("circle.node")
  .data(nodes)
  .enter().append("svg:circle")
  .attr("r", 5)
  .call(force.drag);
force.on("tick", function() {
  link.attr("d", function(d) {
    return draw_curve(d.source.x, d.source.y, d.target.x, d.target.y, M);
  });
  node.attr("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    });
});
function draw_curve(Ax, Ay, Bx, By, M) {
  // side is either 1 or -1 depending on which side you want the curve to be on.
  // Find midpoint J
  var Jx = Ax + (Bx - Ax) / 2
  var Jy = Ay + (By - Ay) / 2
  // We need a and b to find theta, and we need to know the sign of each to make sure that the orientation is correct.
  var a = Bx - Ax
  var asign = (a < 0 ? -1 : 1)
  var b = By - Ay
  var bsign = (b < 0 ? -1 : 1)
  var theta = Math.atan(b / a)
  // Find the point that's perpendicular to J on side
  var costheta = asign * Math.cos(theta)
  var sintheta = asign * Math.sin(theta)
  // Find c and d
  var c = M * sintheta
  var d = M * costheta
  // Use c and d to find Kx and Ky
  var Kx = Jx - c
  var Ky = Jy + d
  return "M" + Ax + "," + Ay +
    "Q" + Kx + "," + Ky +
    " " + Bx + "," + By
}.node {
  stroke: #fff;
  stroke-width: 1.5px;
}
.link {
  stroke: #ccc;
  fill: none
}<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.min.js"></script>
<body>
  <div id="svg-container">
  </div>
</body>If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With