toolkit/exemples/couple/javascript/d3/src/svg/line.js
changeset 47 c0b4a8b5a012
equal deleted inserted replaced
46:efd9c589177a 47:c0b4a8b5a012
       
     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 i = 0,
       
    97       n = points.length,
       
    98       p = points[0],
       
    99       path = [p[0], ",", p[1]];
       
   100   while (++i < n) path.push("L", (p = points[i])[0], ",", p[1]);
       
   101   return path.join("");
       
   102 }
       
   103 
       
   104 // Step interpolation; generates "H" and "V" commands.
       
   105 function d3_svg_lineStepBefore(points) {
       
   106   var i = 0,
       
   107       n = points.length,
       
   108       p = points[0],
       
   109       path = [p[0], ",", p[1]];
       
   110   while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
       
   111   return path.join("");
       
   112 }
       
   113 
       
   114 // Step interpolation; generates "H" and "V" commands.
       
   115 function d3_svg_lineStepAfter(points) {
       
   116   var i = 0,
       
   117       n = points.length,
       
   118       p = points[0],
       
   119       path = [p[0], ",", p[1]];
       
   120   while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
       
   121   return path.join("");
       
   122 }
       
   123 
       
   124 // Open cardinal spline interpolation; generates "C" commands.
       
   125 function d3_svg_lineCardinalOpen(points, tension) {
       
   126   return points.length < 4
       
   127       ? d3_svg_lineLinear(points)
       
   128       : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1),
       
   129         d3_svg_lineCardinalTangents(points, tension));
       
   130 }
       
   131 
       
   132 // Closed cardinal spline interpolation; generates "C" commands.
       
   133 function d3_svg_lineCardinalClosed(points, tension) {
       
   134   return points.length < 3
       
   135       ? d3_svg_lineLinear(points)
       
   136       : points[0] + d3_svg_lineHermite((points.push(points[0]), points),
       
   137         d3_svg_lineCardinalTangents([points[points.length - 2]]
       
   138         .concat(points, [points[1]]), tension));
       
   139 }
       
   140 
       
   141 // Cardinal spline interpolation; generates "C" commands.
       
   142 function d3_svg_lineCardinal(points, tension, closed) {
       
   143   return points.length < 3
       
   144       ? d3_svg_lineLinear(points)
       
   145       : points[0] + d3_svg_lineHermite(points,
       
   146         d3_svg_lineCardinalTangents(points, tension));
       
   147 }
       
   148 
       
   149 // Hermite spline construction; generates "C" commands.
       
   150 function d3_svg_lineHermite(points, tangents) {
       
   151   if (tangents.length < 1
       
   152       || (points.length != tangents.length
       
   153       && points.length != tangents.length + 2)) {
       
   154     return d3_svg_lineLinear(points);
       
   155   }
       
   156 
       
   157   var quad = points.length != tangents.length,
       
   158       path = "",
       
   159       p0 = points[0],
       
   160       p = points[1],
       
   161       t0 = tangents[0],
       
   162       t = t0,
       
   163       pi = 1;
       
   164 
       
   165   if (quad) {
       
   166     path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3)
       
   167         + "," + p[0] + "," + p[1];
       
   168     p0 = points[1];
       
   169     pi = 2;
       
   170   }
       
   171 
       
   172   if (tangents.length > 1) {
       
   173     t = tangents[1];
       
   174     p = points[pi];
       
   175     pi++;
       
   176     path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1])
       
   177         + "," + (p[0] - t[0]) + "," + (p[1] - t[1])
       
   178         + "," + p[0] + "," + p[1];
       
   179     for (var i = 2; i < tangents.length; i++, pi++) {
       
   180       p = points[pi];
       
   181       t = tangents[i];
       
   182       path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1])
       
   183           + "," + p[0] + "," + p[1];
       
   184     }
       
   185   }
       
   186 
       
   187   if (quad) {
       
   188     var lp = points[pi];
       
   189     path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3)
       
   190         + "," + lp[0] + "," + lp[1];
       
   191   }
       
   192 
       
   193   return path;
       
   194 }
       
   195 
       
   196 // Generates tangents for a cardinal spline.
       
   197 function d3_svg_lineCardinalTangents(points, tension) {
       
   198   var tangents = [],
       
   199       a = (1 - tension) / 2,
       
   200       p0,
       
   201       p1 = points[0],
       
   202       p2 = points[1],
       
   203       i = 1,
       
   204       n = points.length;
       
   205   while (++i < n) {
       
   206     p0 = p1;
       
   207     p1 = p2;
       
   208     p2 = points[i];
       
   209     tangents.push([a * (p2[0] - p0[0]), a * (p2[1] - p0[1])]);
       
   210   }
       
   211   return tangents;
       
   212 }
       
   213 
       
   214 // B-spline interpolation; generates "C" commands.
       
   215 function d3_svg_lineBasis(points) {
       
   216   if (points.length < 3) return d3_svg_lineLinear(points);
       
   217   var i = 1,
       
   218       n = points.length,
       
   219       pi = points[0],
       
   220       x0 = pi[0],
       
   221       y0 = pi[1],
       
   222       px = [x0, x0, x0, (pi = points[1])[0]],
       
   223       py = [y0, y0, y0, pi[1]],
       
   224       path = [x0, ",", y0];
       
   225   d3_svg_lineBasisBezier(path, px, py);
       
   226   while (++i < n) {
       
   227     pi = points[i];
       
   228     px.shift(); px.push(pi[0]);
       
   229     py.shift(); py.push(pi[1]);
       
   230     d3_svg_lineBasisBezier(path, px, py);
       
   231   }
       
   232   i = -1;
       
   233   while (++i < 2) {
       
   234     px.shift(); px.push(pi[0]);
       
   235     py.shift(); py.push(pi[1]);
       
   236     d3_svg_lineBasisBezier(path, px, py);
       
   237   }
       
   238   return path.join("");
       
   239 }
       
   240 
       
   241 // Open B-spline interpolation; generates "C" commands.
       
   242 function d3_svg_lineBasisOpen(points) {
       
   243   if (points.length < 4) return d3_svg_lineLinear(points);
       
   244   var path = [],
       
   245       i = -1,
       
   246       n = points.length,
       
   247       pi,
       
   248       px = [0],
       
   249       py = [0];
       
   250   while (++i < 3) {
       
   251     pi = points[i];
       
   252     px.push(pi[0]);
       
   253     py.push(pi[1]);
       
   254   }
       
   255   path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px)
       
   256     + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
       
   257   --i; while (++i < n) {
       
   258     pi = points[i];
       
   259     px.shift(); px.push(pi[0]);
       
   260     py.shift(); py.push(pi[1]);
       
   261     d3_svg_lineBasisBezier(path, px, py);
       
   262   }
       
   263   return path.join("");
       
   264 }
       
   265 
       
   266 // Closed B-spline interpolation; generates "C" commands.
       
   267 function d3_svg_lineBasisClosed(points) {
       
   268   var path,
       
   269       i = -1,
       
   270       n = points.length,
       
   271       m = n + 4,
       
   272       pi,
       
   273       px = [],
       
   274       py = [];
       
   275   while (++i < 4) {
       
   276     pi = points[i % n];
       
   277     px.push(pi[0]);
       
   278     py.push(pi[1]);
       
   279   }
       
   280   path = [
       
   281     d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",",
       
   282     d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)
       
   283   ];
       
   284   --i; while (++i < m) {
       
   285     pi = points[i % n];
       
   286     px.shift(); px.push(pi[0]);
       
   287     py.shift(); py.push(pi[1]);
       
   288     d3_svg_lineBasisBezier(path, px, py);
       
   289   }
       
   290   return path.join("");
       
   291 }
       
   292 
       
   293 function d3_svg_lineBundle(points, tension) {
       
   294   var n = points.length - 1,
       
   295       x0 = points[0][0],
       
   296       y0 = points[0][1],
       
   297       dx = points[n][0] - x0,
       
   298       dy = points[n][1] - y0,
       
   299       i = -1,
       
   300       p,
       
   301       t;
       
   302   while (++i <= n) {
       
   303     p = points[i];
       
   304     t = i / n;
       
   305     p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
       
   306     p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
       
   307   }
       
   308   return d3_svg_lineBasis(points);
       
   309 }
       
   310 
       
   311 // Returns the dot product of the given four-element vectors.
       
   312 function d3_svg_lineDot4(a, b) {
       
   313   return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
       
   314 }
       
   315 
       
   316 // Matrix to transform basis (b-spline) control points to bezier
       
   317 // control points. Derived from FvD 11.2.8.
       
   318 var d3_svg_lineBasisBezier1 = [0, 2/3, 1/3, 0],
       
   319     d3_svg_lineBasisBezier2 = [0, 1/3, 2/3, 0],
       
   320     d3_svg_lineBasisBezier3 = [0, 1/6, 2/3, 1/6];
       
   321 
       
   322 // Pushes a "C" Bézier curve onto the specified path array, given the
       
   323 // two specified four-element arrays which define the control points.
       
   324 function d3_svg_lineBasisBezier(path, x, y) {
       
   325   path.push(
       
   326       "C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x),
       
   327       ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y),
       
   328       ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x),
       
   329       ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y),
       
   330       ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x),
       
   331       ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y));
       
   332 }
       
   333 
       
   334 // Computes the slope from points p0 to p1.
       
   335 function d3_svg_lineSlope(p0, p1) {
       
   336   return (p1[1] - p0[1]) / (p1[0] - p0[0]);
       
   337 }
       
   338 
       
   339 // Compute three-point differences for the given points.
       
   340 // http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Finite_difference
       
   341 function d3_svg_lineFiniteDifferences(points) {
       
   342   var i = 0,
       
   343       j = points.length - 1,
       
   344       m = [],
       
   345       p0 = points[0],
       
   346       p1 = points[1],
       
   347       d = m[0] = d3_svg_lineSlope(p0, p1);
       
   348   while (++i < j) {
       
   349     m[i] = d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]));
       
   350   }
       
   351   m[i] = d;
       
   352   return m;
       
   353 }
       
   354 
       
   355 // Interpolates the given points using Fritsch-Carlson Monotone cubic Hermite
       
   356 // interpolation. Returns an array of tangent vectors. For details, see
       
   357 // http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
       
   358 function d3_svg_lineMonotoneTangents(points) {
       
   359   var tangents = [],
       
   360       d,
       
   361       a,
       
   362       b,
       
   363       s,
       
   364       m = d3_svg_lineFiniteDifferences(points),
       
   365       i = -1,
       
   366       j = points.length - 1;
       
   367 
       
   368   // The first two steps are done by computing finite-differences:
       
   369   // 1. Compute the slopes of the secant lines between successive points.
       
   370   // 2. Initialize the tangents at every point as the average of the secants.
       
   371 
       
   372   // Then, for each segment…
       
   373   while (++i < j) {
       
   374     d = d3_svg_lineSlope(points[i], points[i + 1]);
       
   375 
       
   376     // 3. If two successive yk = y{k + 1} are equal (i.e., d is zero), then set
       
   377     // mk = m{k + 1} = 0 as the spline connecting these points must be flat to
       
   378     // preserve monotonicity. Ignore step 4 and 5 for those k.
       
   379 
       
   380     if (Math.abs(d) < 1e-6) {
       
   381       m[i] = m[i + 1] = 0;
       
   382     } else {
       
   383       // 4. Let ak = mk / dk and bk = m{k + 1} / dk.
       
   384       a = m[i] / d;
       
   385       b = m[i + 1] / d;
       
   386 
       
   387       // 5. Prevent overshoot and ensure monotonicity by restricting the
       
   388       // magnitude of vector <ak, bk> to a circle of radius 3.
       
   389       s = a * a + b * b;
       
   390       if (s > 9) {
       
   391         s = d * 3 / Math.sqrt(s);
       
   392         m[i] = s * a;
       
   393         m[i + 1] = s * b;
       
   394       }
       
   395     }
       
   396   }
       
   397 
       
   398   // Compute the normalized tangent vector from the slopes. Note that if x is
       
   399   // not monotonic, it's possible that the slope will be infinite, so we protect
       
   400   // against NaN by setting the coordinate to zero.
       
   401   i = -1; while (++i <= j) {
       
   402     s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0])
       
   403       / (6 * (1 + m[i] * m[i]));
       
   404     tangents.push([s || 0, m[i] * s || 0]);
       
   405   }
       
   406 
       
   407   return tangents;
       
   408 }
       
   409 
       
   410 function d3_svg_lineMonotone(points) {
       
   411   return points.length < 3
       
   412       ? d3_svg_lineLinear(points)
       
   413       : points[0] +
       
   414         d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
       
   415 }