score:12

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:

1. Find your A-B midpoint, call it J.
2. Do some trig to find the point at the end of line segment M, call it K
3. Use the SVG Q or T path commands to draw the quadratic Bezier curve, starting from A, going to B, with the control point K. (note that this won't look exactly like your diagram, but that can be tuned by changing the length of M). 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: ,
3: ,
};

var nodes = d3.values(adjacencyList),
links = d3.merge(nodes.map(function(source) {
return source.map(function(target) {
return {
source: source,
};
});
}));

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)
.size([w, h])
.charge(-100)
.start();

.enter().append("svg:path")

var node = vis.selectAll("circle.node")
.data(nodes)
.enter().append("svg:circle")
.attr("r", 5)
.call(force.drag);

force.on("tick", function() {
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;
}

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>``````