1 | function d3_svg_line(projection) { |
---|
2 | var x = d3_svg_lineX, |
---|
3 | y = d3_svg_lineY, |
---|
4 | interpolate = "linear", |
---|
5 | interpolator = d3_svg_lineInterpolators[interpolate], |
---|
6 | tension = .7; |
---|
7 | |
---|
8 | function line(d) { |
---|
9 | return d.length < 1 ? null : "M" + interpolator(projection(d3_svg_linePoints(this, d, x, y)), tension); |
---|
10 | } |
---|
11 | |
---|
12 | line.x = function(v) { |
---|
13 | if (!arguments.length) return x; |
---|
14 | x = v; |
---|
15 | return line; |
---|
16 | }; |
---|
17 | |
---|
18 | line.y = function(v) { |
---|
19 | if (!arguments.length) return y; |
---|
20 | y = v; |
---|
21 | return line; |
---|
22 | }; |
---|
23 | |
---|
24 | line.interpolate = function(v) { |
---|
25 | if (!arguments.length) return interpolate; |
---|
26 | interpolator = d3_svg_lineInterpolators[interpolate = v]; |
---|
27 | return line; |
---|
28 | }; |
---|
29 | |
---|
30 | line.tension = function(v) { |
---|
31 | if (!arguments.length) return tension; |
---|
32 | tension = v; |
---|
33 | return line; |
---|
34 | }; |
---|
35 | |
---|
36 | return line; |
---|
37 | } |
---|
38 | |
---|
39 | d3.svg.line = function() { |
---|
40 | return d3_svg_line(Object); |
---|
41 | }; |
---|
42 | |
---|
43 | // Converts the specified array of data into an array of points |
---|
44 | // (x-y tuples), by evaluating the specified `x` and `y` functions on each |
---|
45 | // data point. The `this` context of the evaluated functions is the specified |
---|
46 | // "self" object; each function is passed the current datum and index. |
---|
47 | function d3_svg_linePoints(self, d, x, y) { |
---|
48 | var points = [], |
---|
49 | i = -1, |
---|
50 | n = d.length, |
---|
51 | fx = typeof x === "function", |
---|
52 | fy = typeof y === "function", |
---|
53 | value; |
---|
54 | if (fx && fy) { |
---|
55 | while (++i < n) points.push([ |
---|
56 | x.call(self, value = d[i], i), |
---|
57 | y.call(self, value, i) |
---|
58 | ]); |
---|
59 | } else if (fx) { |
---|
60 | while (++i < n) points.push([x.call(self, d[i], i), y]); |
---|
61 | } else if (fy) { |
---|
62 | while (++i < n) points.push([x, y.call(self, d[i], i)]); |
---|
63 | } else { |
---|
64 | while (++i < n) points.push([x, y]); |
---|
65 | } |
---|
66 | return points; |
---|
67 | } |
---|
68 | |
---|
69 | // The default `x` property, which references d[0]. |
---|
70 | function d3_svg_lineX(d) { |
---|
71 | return d[0]; |
---|
72 | } |
---|
73 | |
---|
74 | // The default `y` property, which references d[1]. |
---|
75 | function d3_svg_lineY(d) { |
---|
76 | return d[1]; |
---|
77 | } |
---|
78 | |
---|
79 | // The various interpolators supported by the `line` class. |
---|
80 | var d3_svg_lineInterpolators = { |
---|
81 | "linear": d3_svg_lineLinear, |
---|
82 | "step-before": d3_svg_lineStepBefore, |
---|
83 | "step-after": d3_svg_lineStepAfter, |
---|
84 | "basis": d3_svg_lineBasis, |
---|
85 | "basis-open": d3_svg_lineBasisOpen, |
---|
86 | "basis-closed": d3_svg_lineBasisClosed, |
---|
87 | "bundle": d3_svg_lineBundle, |
---|
88 | "cardinal": d3_svg_lineCardinal, |
---|
89 | "cardinal-open": d3_svg_lineCardinalOpen, |
---|
90 | "cardinal-closed": d3_svg_lineCardinalClosed, |
---|
91 | "monotone": d3_svg_lineMonotone |
---|
92 | }; |
---|
93 | |
---|
94 | // Linear interpolation; generates "L" commands. |
---|
95 | function d3_svg_lineLinear(points) { |
---|
96 | var path = [], |
---|
97 | i = 0, |
---|
98 | n = points.length, |
---|
99 | p = points[0]; |
---|
100 | path.push(p[0], ",", p[1]); |
---|
101 | while (++i < n) path.push("L", (p = points[i])[0], ",", p[1]); |
---|
102 | return path.join(""); |
---|
103 | } |
---|
104 | |
---|
105 | // Step interpolation; generates "H" and "V" commands. |
---|
106 | function d3_svg_lineStepBefore(points) { |
---|
107 | var path = [], |
---|
108 | i = 0, |
---|
109 | n = points.length, |
---|
110 | p = points[0]; |
---|
111 | path.push(p[0], ",", p[1]); |
---|
112 | while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); |
---|
113 | return path.join(""); |
---|
114 | } |
---|
115 | |
---|
116 | // Step interpolation; generates "H" and "V" commands. |
---|
117 | function d3_svg_lineStepAfter(points) { |
---|
118 | var path = [], |
---|
119 | i = 0, |
---|
120 | n = points.length, |
---|
121 | p = points[0]; |
---|
122 | path.push(p[0], ",", p[1]); |
---|
123 | while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); |
---|
124 | return path.join(""); |
---|
125 | } |
---|
126 | |
---|
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)); |
---|
133 | } |
---|
134 | |
---|
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)); |
---|
142 | } |
---|
143 | |
---|
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)); |
---|
150 | } |
---|
151 | |
---|
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); |
---|
158 | } |
---|
159 | |
---|
160 | var quad = points.length != tangents.length, |
---|
161 | path = "", |
---|
162 | p0 = points[0], |
---|
163 | p = points[1], |
---|
164 | t0 = tangents[0], |
---|
165 | t = t0, |
---|
166 | pi = 1; |
---|
167 | |
---|
168 | if (quad) { |
---|
169 | path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) |
---|
170 | + "," + p[0] + "," + p[1]; |
---|
171 | p0 = points[1]; |
---|
172 | pi = 2; |
---|
173 | } |
---|
174 | |
---|
175 | if (tangents.length > 1) { |
---|
176 | t = tangents[1]; |
---|
177 | p = points[pi]; |
---|
178 | pi++; |
---|
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++) { |
---|
183 | p = points[pi]; |
---|
184 | t = tangents[i]; |
---|
185 | path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) |
---|
186 | + "," + p[0] + "," + p[1]; |
---|
187 | } |
---|
188 | } |
---|
189 | |
---|
190 | if (quad) { |
---|
191 | var lp = points[pi]; |
---|
192 | path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) |
---|
193 | + "," + lp[0] + "," + lp[1]; |
---|
194 | } |
---|
195 | |
---|
196 | return path; |
---|
197 | } |
---|
198 | |
---|
199 | // Generates tangents for a cardinal spline. |
---|
200 | function d3_svg_lineCardinalTangents(points, tension) { |
---|
201 | var tangents = [], |
---|
202 | a = (1 - tension) / 2, |
---|
203 | p0, |
---|
204 | p1 = points[0], |
---|
205 | p2 = points[1], |
---|
206 | i = 1, |
---|
207 | n = points.length; |
---|
208 | while (++i < n) { |
---|
209 | p0 = p1; |
---|
210 | p1 = p2; |
---|
211 | p2 = points[i]; |
---|
212 | tangents.push([a * (p2[0] - p0[0]), a * (p2[1] - p0[1])]); |
---|
213 | } |
---|
214 | return tangents; |
---|
215 | } |
---|
216 | |
---|
217 | // B-spline interpolation; generates "C" commands. |
---|
218 | function d3_svg_lineBasis(points) { |
---|
219 | if (points.length < 3) return d3_svg_lineLinear(points); |
---|
220 | var path = [], |
---|
221 | i = 1, |
---|
222 | n = points.length, |
---|
223 | pi = points[0], |
---|
224 | x0 = pi[0], |
---|
225 | y0 = pi[1], |
---|
226 | px = [x0, x0, x0, (pi = points[1])[0]], |
---|
227 | py = [y0, y0, y0, pi[1]]; |
---|
228 | path.push(x0, ",", y0); |
---|
229 | d3_svg_lineBasisBezier(path, px, py); |
---|
230 | while (++i < n) { |
---|
231 | pi = points[i]; |
---|
232 | px.shift(); px.push(pi[0]); |
---|
233 | py.shift(); py.push(pi[1]); |
---|
234 | d3_svg_lineBasisBezier(path, px, py); |
---|
235 | } |
---|
236 | i = -1; |
---|
237 | while (++i < 2) { |
---|
238 | px.shift(); px.push(pi[0]); |
---|
239 | py.shift(); py.push(pi[1]); |
---|
240 | d3_svg_lineBasisBezier(path, px, py); |
---|
241 | } |
---|
242 | return path.join(""); |
---|
243 | } |
---|
244 | |
---|
245 | // Open B-spline interpolation; generates "C" commands. |
---|
246 | function d3_svg_lineBasisOpen(points) { |
---|
247 | if (points.length < 4) return d3_svg_lineLinear(points); |
---|
248 | var path = [], |
---|
249 | i = -1, |
---|
250 | n = points.length, |
---|
251 | pi, |
---|
252 | px = [0], |
---|
253 | py = [0]; |
---|
254 | while (++i < 3) { |
---|
255 | pi = points[i]; |
---|
256 | px.push(pi[0]); |
---|
257 | py.push(pi[1]); |
---|
258 | } |
---|
259 | path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) |
---|
260 | + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); |
---|
261 | --i; while (++i < n) { |
---|
262 | pi = points[i]; |
---|
263 | px.shift(); px.push(pi[0]); |
---|
264 | py.shift(); py.push(pi[1]); |
---|
265 | d3_svg_lineBasisBezier(path, px, py); |
---|
266 | } |
---|
267 | return path.join(""); |
---|
268 | } |
---|
269 | |
---|
270 | // Closed B-spline interpolation; generates "C" commands. |
---|
271 | function d3_svg_lineBasisClosed(points) { |
---|
272 | var path, |
---|
273 | i = -1, |
---|
274 | n = points.length, |
---|
275 | m = n + 4, |
---|
276 | pi, |
---|
277 | px = [], |
---|
278 | py = []; |
---|
279 | while (++i < 4) { |
---|
280 | pi = points[i % n]; |
---|
281 | px.push(pi[0]); |
---|
282 | py.push(pi[1]); |
---|
283 | } |
---|
284 | path = [ |
---|
285 | d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", |
---|
286 | d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) |
---|
287 | ]; |
---|
288 | --i; while (++i < m) { |
---|
289 | pi = points[i % n]; |
---|
290 | px.shift(); px.push(pi[0]); |
---|
291 | py.shift(); py.push(pi[1]); |
---|
292 | d3_svg_lineBasisBezier(path, px, py); |
---|
293 | } |
---|
294 | return path.join(""); |
---|
295 | } |
---|
296 | |
---|
297 | function d3_svg_lineBundle(points, tension) { |
---|
298 | var n = points.length - 1, |
---|
299 | x0 = points[0][0], |
---|
300 | y0 = points[0][1], |
---|
301 | dx = points[n][0] - x0, |
---|
302 | dy = points[n][1] - y0, |
---|
303 | i = -1, |
---|
304 | p, |
---|
305 | t; |
---|
306 | while (++i <= n) { |
---|
307 | p = points[i]; |
---|
308 | t = i / n; |
---|
309 | p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); |
---|
310 | p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); |
---|
311 | } |
---|
312 | return d3_svg_lineBasis(points); |
---|
313 | } |
---|
314 | |
---|
315 | // Returns the dot product of the given four-element vectors. |
---|
316 | function d3_svg_lineDot4(a, b) { |
---|
317 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; |
---|
318 | } |
---|
319 | |
---|
320 | // Matrix to transform basis (b-spline) control points to bezier |
---|
321 | // control points. Derived from FvD 11.2.8. |
---|
322 | var d3_svg_lineBasisBezier1 = [0, 2/3, 1/3, 0], |
---|
323 | d3_svg_lineBasisBezier2 = [0, 1/3, 2/3, 0], |
---|
324 | d3_svg_lineBasisBezier3 = [0, 1/6, 2/3, 1/6]; |
---|
325 | |
---|
326 | // Pushes a "C" Bézier curve onto the specified path array, given the |
---|
327 | // two specified four-element arrays which define the control points. |
---|
328 | function d3_svg_lineBasisBezier(path, x, y) { |
---|
329 | path.push( |
---|
330 | "C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), |
---|
331 | ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), |
---|
332 | ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), |
---|
333 | ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), |
---|
334 | ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), |
---|
335 | ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); |
---|
336 | } |
---|
337 | |
---|
338 | // Computes the slope from points p0 to p1. |
---|
339 | function d3_svg_lineSlope(p0, p1) { |
---|
340 | return (p1[1] - p0[1]) / (p1[0] - p0[0]); |
---|
341 | } |
---|
342 | |
---|
343 | // Compute three-point differences for the given points. |
---|
344 | // http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Finite_difference |
---|
345 | function d3_svg_lineFiniteDifferences(points) { |
---|
346 | var i = 0, |
---|
347 | j = points.length - 1, |
---|
348 | m = [], |
---|
349 | p0 = points[0], |
---|
350 | p1 = points[1], |
---|
351 | d = m[0] = d3_svg_lineSlope(p0, p1); |
---|
352 | while (++i < j) { |
---|
353 | m[i] = d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1])); |
---|
354 | } |
---|
355 | m[i] = d; |
---|
356 | return m; |
---|
357 | } |
---|
358 | |
---|
359 | // Interpolates the given points using Fritsch-Carlson Monotone cubic Hermite |
---|
360 | // interpolation. Returns an array of tangent vectors. For details, see |
---|
361 | // http://en.wikipedia.org/wiki/Monotone_cubic_interpolation |
---|
362 | function d3_svg_lineMonotoneTangents(points) { |
---|
363 | var tangents = [], |
---|
364 | d, |
---|
365 | a, |
---|
366 | b, |
---|
367 | s, |
---|
368 | m = d3_svg_lineFiniteDifferences(points), |
---|
369 | i = -1, |
---|
370 | j = points.length - 1; |
---|
371 | |
---|
372 | // The first two steps are done by computing finite-differences: |
---|
373 | // 1. Compute the slopes of the secant lines between successive points. |
---|
374 | // 2. Initialize the tangents at every point as the average of the secants. |
---|
375 | |
---|
376 | // Then, for each segment⊠|
---|
377 | while (++i < j) { |
---|
378 | d = d3_svg_lineSlope(points[i], points[i + 1]); |
---|
379 | |
---|
380 | // 3. If two successive yk = y{k + 1} are equal (i.e., d is zero), then set |
---|
381 | // mk = m{k + 1} = 0 as the spline connecting these points must be flat to |
---|
382 | // preserve monotonicity. Ignore step 4 and 5 for those k. |
---|
383 | |
---|
384 | if (Math.abs(d) < 1e-6) { |
---|
385 | m[i] = m[i + 1] = 0; |
---|
386 | } else { |
---|
387 | // 4. Let ak = mk / dk and bk = m{k + 1} / dk. |
---|
388 | a = m[i] / d; |
---|
389 | b = m[i + 1] / d; |
---|
390 | |
---|
391 | // 5. Prevent overshoot and ensure monotonicity by restricting the |
---|
392 | // magnitude of vector <ak, bk> to a circle of radius 3. |
---|
393 | s = a * a + b * b; |
---|
394 | if (s > 9) { |
---|
395 | s = d * 3 / Math.sqrt(s); |
---|
396 | m[i] = s * a; |
---|
397 | m[i + 1] = s * b; |
---|
398 | } |
---|
399 | } |
---|
400 | } |
---|
401 | |
---|
402 | // Compute the normalized tangent vector from the slopes. Note that if x is |
---|
403 | // not monotonic, it's possible that the slope will be infinite, so we protect |
---|
404 | // against NaN by setting the coordinate to zero. |
---|
405 | i = -1; while (++i <= j) { |
---|
406 | s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) |
---|
407 | / (6 * (1 + m[i] * m[i])); |
---|
408 | tangents.push([s || 0, m[i] * s || 0]); |
---|
409 | } |
---|
410 | |
---|
411 | return tangents; |
---|
412 | } |
---|
413 | |
---|
414 | function d3_svg_lineMonotone(points) { |
---|
415 | return points.length < 3 |
---|
416 | ? d3_svg_lineLinear(points) |
---|
417 | : points[0] + |
---|
418 | d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); |
---|
419 | } |
---|