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 [Tidy Tree](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; +}