source: Dev/branches/jQueryUI/client/d3/src/svg/line.js @ 249

Last change on this file since 249 was 249, checked in by hendrikvanantwerpen, 13 years ago

This one's for Subversion, because it's so close...

First widget (stripped down sequencer).
Seperated client and server code in two direcotry trees.

File size: 11.5 KB
Line 
1function 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
39d3.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.
47function 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].
70function d3_svg_lineX(d) {
71  return d[0];
72}
73
74// The default `y` property, which references d[1].
75function d3_svg_lineY(d) {
76  return d[1];
77}
78
79// The various interpolators supported by the `line` class.
80var 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.
95function 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.
106function 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.
117function 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.
128function 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.
136function 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.
145function 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.
153function 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.
200function 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.
218function 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.
246function 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.
271function 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
297function 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.
316function 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.
322var 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.
328function 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.
339function 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
345function 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
362function 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
414function 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}
Note: See TracBrowser for help on using the repository browser.