diff --git a/README.md b/README.md
index 7677d42..e2aa106 100644
--- a/README.md
+++ b/README.md
@@ -802,7 +802,7 @@ Indicates a new point in the current line segment with the given *x*- and *y*-va
[](http://bl.ocks.org/mbostock/9d0899acb5d3b8d839d9d613a9e1fe04)
-The **link** shape generates a smooth cubic Bézier curve from a source point to a target point. The tangents of the curve at the start and end are either [vertical](#linkVertical) or [horizontal](#linkHorizontal).
+The **link** shape generates a smooth cubic Bézier curve from a source point to a target point. The tangents of the curve at the start and end are either [vertical](#linkVertical), [horizontal](#linkHorizontal) or [radial](#linkRadial).
# d3.linkVertical() [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L64 "Source")
@@ -879,6 +879,24 @@ function y(d) {
If *context* is specified, sets the context and returns this link generator. If *context* is not specified, returns the current context, which defaults to null. If the context is not null, then the [generated link](#_link) is rendered to this context as a sequence of [path method](http://www.w3.org/TR/2dcontext/#canvaspathmethods) calls. Otherwise, a [path data](http://www.w3.org/TR/SVG/paths.html#PathData) string representing the generated link is returned. See also [d3-path](https://github.com/d3/d3-path).
+# d3.linkRadial() [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L73 "Source")
+
+Returns a new [link generator](#_link) with radial tangents. For example, to visualize [links](https://github.com/d3/d3-hierarchy/blob/master/README.md#node_links) in a [tree diagram](https://github.com/d3/d3-hierarchy/blob/master/README.md#tree) rooted in the center of the display, you might say:
+
+```js
+var link = d3.linkRadial()
+ .angle(function(d) { return d.x; })
+ .radius(function(d) { return d.y; });
+```
+
+# radialLink.angle([angle]) [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L104 "Source")
+
+Equivalent to [*link*.x](#link_x), except the accessor returns the angle in radians, with 0 at -*y* (12 o’clock).
+
+# radialLink.radius([radius]) [<>](https://github.com/d3/d3-shape/blob/master/src/link/index.js#L108 "Source")
+
+Equivalent to [*link*.y](#link_y), except the accessor returns the radius: the distance from the origin ⟨0,0⟩.
+
### Symbols
diff --git a/index.js b/index.js
index 7b8bae4..6965fcd 100644
--- a/index.js
+++ b/index.js
@@ -4,7 +4,7 @@ export {default as line} from "./src/line";
export {default as pie} from "./src/pie";
export {default as radialArea} from "./src/radialArea";
export {default as radialLine} from "./src/radialLine";
-export {linkHorizontal, linkVertical} from "./src/link/index";
+export {linkHorizontal, linkVertical, linkRadial} from "./src/link/index";
export {default as symbol, symbols} from "./src/symbol";
export {default as symbolCircle} from "./src/symbol/circle";
diff --git a/src/link/index.js b/src/link/index.js
index 35b34d9..bc8f218 100644
--- a/src/link/index.js
+++ b/src/link/index.js
@@ -29,8 +29,8 @@ function link(horizontal) {
y1 = +y.apply(this, argv);
if (!context) context = buffer = path();
context.moveTo(x0, y0);
- if (horizontal) context.bezierCurveTo((x0 + x1) / 2, y0, (x0 + x1) / 2, y1, x1, y1);
- else context.bezierCurveTo(x0, (y0 + y1) / 2, x1, (y0 + y1) / 2, x1, y1);
+ if (horizontal) context.bezierCurveTo(x0 = (x0 + x1) / 2, y0, x0, y1, x1, y1);
+ else context.bezierCurveTo(x0, y0 = (y0 + y1) / 2, x1, y0, x1, y1);
if (buffer) return context = null, buffer + "" || null;
}
@@ -64,3 +64,54 @@ export function linkHorizontal() {
export function linkVertical() {
return link(false);
}
+
+function project(x, y) {
+ var angle = (x - 90) / 180 * Math.PI, radius = y;
+ return [radius * Math.cos(angle), radius * Math.sin(angle)];
+}
+
+export function linkRadial() {
+ var source = linkSource,
+ target = linkTarget,
+ angle = pointX,
+ radius = pointY,
+ context = null;
+
+ function link() {
+ var buffer,
+ argv = slice.call(arguments),
+ s = source.apply(this, argv),
+ t = target.apply(this, argv),
+ a0 = +angle.apply(this, (argv[0] = s, argv)),
+ r0 = +radius.apply(this, argv),
+ a1 = +angle.apply(this, (argv[0] = t, argv)),
+ r1 = +radius.apply(this, argv),
+ r2 = (r0 + r1) / 2, p;
+ if (!context) context = buffer = path();
+ context.moveTo((p = project(a0, r0))[0], p[1]);
+ context.bezierCurveTo((p = project(a0, r2))[0], p[1], (p = project(a1, r2))[0], p[1], (p = project(a1, r1))[0], p[1]);
+ if (buffer) return context = null, buffer + "" || null;
+ }
+
+ link.source = function(_) {
+ return arguments.length ? (source = _, link) : source;
+ };
+
+ link.target = function(_) {
+ return arguments.length ? (target = _, link) : target;
+ };
+
+ link.angle = function(_) {
+ return arguments.length ? (angle = typeof _ === "function" ? _ : constant(+_), link) : angle;
+ };
+
+ link.radius = function(_) {
+ return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), link) : radius;
+ };
+
+ link.context = function(_) {
+ return arguments.length ? ((context = _ == null ? null : _), link) : context;
+ };
+
+ return link;
+}