1
function d3_svg_line(projection) {
4
interpolate = d3_svg_lineInterpolatorDefault,
5
interpolator = d3_svg_lineInterpolators.get(interpolate),
9
return d.length < 1 ? null : "M" + interpolator(projection(d3_svg_linePoints(this, d, x, y)), tension);
12
line.x = function(v) {
13
if (!arguments.length) return x;
18
line.y = function(v) {
19
if (!arguments.length) return y;
24
line.interpolate = function(v) {
25
if (!arguments.length) return interpolate;
26
if (!d3_svg_lineInterpolators.has(v += "")) v = d3_svg_lineInterpolatorDefault;
27
interpolator = d3_svg_lineInterpolators.get(interpolate = v);
31
line.tension = function(v) {
32
if (!arguments.length) return tension;
40
d3.svg.line = function() {
41
return d3_svg_line(Object);
44
// Converts the specified array of data into an array of points
45
// (x-y tuples), by evaluating the specified `x` and `y` functions on each
46
// data point. The `this` context of the evaluated functions is the specified
47
// "self" object; each function is passed the current datum and index.
48
function d3_svg_linePoints(self, d, x, y) {
52
fx = typeof x === "function",
53
fy = typeof y === "function",
56
while (++i < n) points.push([
57
x.call(self, value = d[i], i),
58
y.call(self, value, i)
61
while (++i < n) points.push([x.call(self, d[i], i), y]);
63
while (++i < n) points.push([x, y.call(self, d[i], i)]);
65
while (++i < n) points.push([x, y]);
70
// The default `x` property, which references d[0].
71
function d3_svg_lineX(d) {
75
// The default `y` property, which references d[1].
76
function d3_svg_lineY(d) {
80
var d3_svg_lineInterpolatorDefault = "linear";
82
// The various interpolators supported by the `line` class.
83
var d3_svg_lineInterpolators = d3.map({
84
"linear": d3_svg_lineLinear,
85
"step-before": d3_svg_lineStepBefore,
86
"step-after": d3_svg_lineStepAfter,
87
"basis": d3_svg_lineBasis,
88
"basis-open": d3_svg_lineBasisOpen,
89
"basis-closed": d3_svg_lineBasisClosed,
90
"bundle": d3_svg_lineBundle,
91
"cardinal": d3_svg_lineCardinal,
92
"cardinal-open": d3_svg_lineCardinalOpen,
93
"cardinal-closed": d3_svg_lineCardinalClosed,
94
"monotone": d3_svg_lineMonotone
97
// Linear interpolation; generates "L" commands.
98
function d3_svg_lineLinear(points) {
102
path = [p[0], ",", p[1]];
103
while (++i < n) path.push("L", (p = points[i])[0], ",", p[1]);
104
return path.join("");
107
// Step interpolation; generates "H" and "V" commands.
108
function d3_svg_lineStepBefore(points) {
112
path = [p[0], ",", p[1]];
113
while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
114
return path.join("");
117
// Step interpolation; generates "H" and "V" commands.
118
function d3_svg_lineStepAfter(points) {
122
path = [p[0], ",", p[1]];
123
while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
124
return path.join("");
127
// Open cardinal spline interpolation; generates "C" commands.
128
function d3_svg_lineCardinalOpen(points, tension) {
129
return points.length < 4
130
? d3_svg_lineLinear(points)
131
: points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1),
132
d3_svg_lineCardinalTangents(points, tension));
135
// Closed cardinal spline interpolation; generates "C" commands.
136
function d3_svg_lineCardinalClosed(points, tension) {
137
return points.length < 3
138
? d3_svg_lineLinear(points)
139
: points[0] + d3_svg_lineHermite((points.push(points[0]), points),
140
d3_svg_lineCardinalTangents([points[points.length - 2]]
141
.concat(points, [points[1]]), tension));
144
// Cardinal spline interpolation; generates "C" commands.
145
function d3_svg_lineCardinal(points, tension, closed) {
146
return points.length < 3
147
? d3_svg_lineLinear(points)
148
: points[0] + d3_svg_lineHermite(points,
149
d3_svg_lineCardinalTangents(points, tension));
152
// Hermite spline construction; generates "C" commands.
153
function d3_svg_lineHermite(points, tangents) {
154
if (tangents.length < 1
155
|| (points.length != tangents.length
156
&& points.length != tangents.length + 2)) {
157
return d3_svg_lineLinear(points);
160
var quad = points.length != tangents.length,
169
path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3)
170
+ "," + p[0] + "," + p[1];
175
if (tangents.length > 1) {
179
path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1])
180
+ "," + (p[0] - t[0]) + "," + (p[1] - t[1])
181
+ "," + p[0] + "," + p[1];
182
for (var i = 2; i < tangents.length; i++, pi++) {
185
path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1])
186
+ "," + p[0] + "," + p[1];
192
path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3)
193
+ "," + lp[0] + "," + lp[1];
199
// Generates tangents for a cardinal spline.
200
function d3_svg_lineCardinalTangents(points, tension) {
202
a = (1 - tension) / 2,
212
tangents.push([a * (p2[0] - p0[0]), a * (p2[1] - p0[1])]);
217
// B-spline interpolation; generates "C" commands.
218
function d3_svg_lineBasis(points) {
219
if (points.length < 3) return d3_svg_lineLinear(points);
225
px = [x0, x0, x0, (pi = points[1])[0]],
226
py = [y0, y0, y0, pi[1]],
227
path = [x0, ",", y0];
228
d3_svg_lineBasisBezier(path, px, py);
231
px.shift(); px.push(pi[0]);
232
py.shift(); py.push(pi[1]);
233
d3_svg_lineBasisBezier(path, px, py);
237
px.shift(); px.push(pi[0]);
238
py.shift(); py.push(pi[1]);
239
d3_svg_lineBasisBezier(path, px, py);
241
return path.join("");
244
// Open B-spline interpolation; generates "C" commands.
245
function d3_svg_lineBasisOpen(points) {
246
if (points.length < 4) return d3_svg_lineLinear(points);
258
path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px)
259
+ "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
260
--i; while (++i < n) {
262
px.shift(); px.push(pi[0]);
263
py.shift(); py.push(pi[1]);
264
d3_svg_lineBasisBezier(path, px, py);
266
return path.join("");
269
// Closed B-spline interpolation; generates "C" commands.
270
function d3_svg_lineBasisClosed(points) {
284
d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",",
285
d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)
287
--i; while (++i < m) {
289
px.shift(); px.push(pi[0]);
290
py.shift(); py.push(pi[1]);
291
d3_svg_lineBasisBezier(path, px, py);
293
return path.join("");
296
function d3_svg_lineBundle(points, tension) {
297
var n = points.length - 1,
300
dx = points[n][0] - x0,
301
dy = points[n][1] - y0,
308
p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
309
p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
311
return d3_svg_lineBasis(points);
314
// Returns the dot product of the given four-element vectors.
315
function d3_svg_lineDot4(a, b) {
316
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
319
// Matrix to transform basis (b-spline) control points to bezier
320
// control points. Derived from FvD 11.2.8.
321
var d3_svg_lineBasisBezier1 = [0, 2/3, 1/3, 0],
322
d3_svg_lineBasisBezier2 = [0, 1/3, 2/3, 0],
323
d3_svg_lineBasisBezier3 = [0, 1/6, 2/3, 1/6];
325
// Pushes a "C" Bézier curve onto the specified path array, given the
326
// two specified four-element arrays which define the control points.
327
function d3_svg_lineBasisBezier(path, x, y) {
329
"C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x),
330
",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y),
331
",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x),
332
",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y),
333
",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x),
334
",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y));
337
// Computes the slope from points p0 to p1.
338
function d3_svg_lineSlope(p0, p1) {
339
return (p1[1] - p0[1]) / (p1[0] - p0[0]);
342
// Compute three-point differences for the given points.
343
// http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Finite_difference
344
function d3_svg_lineFiniteDifferences(points) {
346
j = points.length - 1,
350
d = m[0] = d3_svg_lineSlope(p0, p1);
352
m[i] = d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]));
358
// Interpolates the given points using Fritsch-Carlson Monotone cubic Hermite
359
// interpolation. Returns an array of tangent vectors. For details, see
360
// http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
361
function d3_svg_lineMonotoneTangents(points) {
367
m = d3_svg_lineFiniteDifferences(points),
369
j = points.length - 1;
371
// The first two steps are done by computing finite-differences:
372
// 1. Compute the slopes of the secant lines between successive points.
373
// 2. Initialize the tangents at every point as the average of the secants.
375
// Then, for each segment…
377
d = d3_svg_lineSlope(points[i], points[i + 1]);
379
// 3. If two successive yk = y{k + 1} are equal (i.e., d is zero), then set
380
// mk = m{k + 1} = 0 as the spline connecting these points must be flat to
381
// preserve monotonicity. Ignore step 4 and 5 for those k.
383
if (Math.abs(d) < 1e-6) {
386
// 4. Let ak = mk / dk and bk = m{k + 1} / dk.
390
// 5. Prevent overshoot and ensure monotonicity by restricting the
391
// magnitude of vector <ak, bk> to a circle of radius 3.
394
s = d * 3 / Math.sqrt(s);
401
// Compute the normalized tangent vector from the slopes. Note that if x is
402
// not monotonic, it's possible that the slope will be infinite, so we protect
403
// against NaN by setting the coordinate to zero.
404
i = -1; while (++i <= j) {
405
s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0])
406
/ (6 * (1 + m[i] * m[i]));
407
tangents.push([s || 0, m[i] * s || 0]);
413
function d3_svg_lineMonotone(points) {
414
return points.length < 3
415
? d3_svg_lineLinear(points)
417
d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));