You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

166 lines
3.4 KiB

  1. 'use strict';
  2. /**
  3. * Convert a series of points to a monotone cubic spline
  4. * Algorithm based on https://github.com/mbostock/d3
  5. * https://github.com/yr/monotone-cubic-spline
  6. * @copyright Yr
  7. * @license MIT
  8. */
  9. var ε = 1e-6;
  10. module.exports = {
  11. /**
  12. * Convert 'points' to bezier
  13. * @param {Array} points
  14. * @returns {Array}
  15. */
  16. points: function points(_points) {
  17. var tgts = tangents(_points);
  18. var p = _points[1];
  19. var p0 = _points[0];
  20. var pts = [];
  21. var t = tgts[1];
  22. var t0 = tgts[0];
  23. // Add starting 'M' and 'C' points
  24. pts.push(p0, [p0[0] + t0[0], p0[1] + t0[1], p[0] - t[0], p[1] - t[1], p[0], p[1]]);
  25. // Add 'S' points
  26. for (var i = 2, n = tgts.length; i < n; i++) {
  27. var _p = _points[i];
  28. var _t = tgts[i];
  29. pts.push([_p[0] - _t[0], _p[1] - _t[1], _p[0], _p[1]]);
  30. }
  31. return pts;
  32. },
  33. /**
  34. * Slice out a segment of 'points'
  35. * @param {Array} points
  36. * @param {Number} start
  37. * @param {Number} end
  38. * @returns {Array}
  39. */
  40. slice: function slice(points, start, end) {
  41. var pts = points.slice(start, end);
  42. if (start) {
  43. // Add additional 'C' points
  44. if (pts[1].length < 6) {
  45. var n = pts[0].length;
  46. pts[1] = [pts[0][n - 2] * 2 - pts[0][n - 4], pts[0][n - 1] * 2 - pts[0][n - 3]].concat(pts[1]);
  47. }
  48. // Remove control points for 'M'
  49. pts[0] = pts[0].slice(-2);
  50. }
  51. return pts;
  52. },
  53. /**
  54. * Convert 'points' to svg path
  55. * @param {Array} points
  56. * @returns {String}
  57. */
  58. svgPath: function svgPath(points) {
  59. var p = '';
  60. for (var i = 0; i < points.length; i++) {
  61. var point = points[i];
  62. var n = point.length;
  63. if (!i) {
  64. p += 'M' + point[n - 2] + ' ' + point[n - 1];
  65. } else if (n > 4) {
  66. p += 'C' + point[0] + ', ' + point[1];
  67. p += ', ' + point[2] + ', ' + point[3];
  68. p += ', ' + point[4] + ', ' + point[5];
  69. } else {
  70. p += 'S' + point[0] + ', ' + point[1];
  71. p += ', ' + point[2] + ', ' + point[3];
  72. }
  73. }
  74. return p;
  75. }
  76. };
  77. /**
  78. * Generate tangents for 'points'
  79. * @param {Array} points
  80. * @returns {Array}
  81. */
  82. function tangents(points) {
  83. var m = finiteDifferences(points);
  84. var n = points.length - 1;
  85. var tgts = [];
  86. var a = void 0,
  87. b = void 0,
  88. d = void 0,
  89. s = void 0;
  90. for (var i = 0; i < n; i++) {
  91. d = slope(points[i], points[i + 1]);
  92. if (Math.abs(d) < ε) {
  93. m[i] = m[i + 1] = 0;
  94. } else {
  95. a = m[i] / d;
  96. b = m[i + 1] / d;
  97. s = a * a + b * b;
  98. if (s > 9) {
  99. s = d * 3 / Math.sqrt(s);
  100. m[i] = s * a;
  101. m[i + 1] = s * b;
  102. }
  103. }
  104. }
  105. for (var _i = 0; _i <= n; _i++) {
  106. s = (points[Math.min(n, _i + 1)][0] - points[Math.max(0, _i - 1)][0]) / (6 * (1 + m[_i] * m[_i]));
  107. tgts.push([s || 0, m[_i] * s || 0]);
  108. }
  109. return tgts;
  110. }
  111. /**
  112. * Compute slope from point 'p0' to 'p1'
  113. * @param {Array} p0
  114. * @param {Array} p1
  115. * @returns {Number}
  116. */
  117. function slope(p0, p1) {
  118. return (p1[1] - p0[1]) / (p1[0] - p0[0]);
  119. }
  120. /**
  121. * Compute three-point differences for 'points'
  122. * @param {Array} points
  123. * @returns {Array}
  124. */
  125. function finiteDifferences(points) {
  126. var m = [];
  127. var p0 = points[0];
  128. var p1 = points[1];
  129. var d = m[0] = slope(p0, p1);
  130. var i = 1;
  131. for (var n = points.length - 1; i < n; i++) {
  132. p0 = p1;
  133. p1 = points[i + 1];
  134. m[i] = (d + (d = slope(p0, p1))) * 0.5;
  135. }
  136. m[i] = d;
  137. return m;
  138. }