bbox.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. var Vector2 = require('./vector2');
  2. var start = Vector2.create();
  3. var end = Vector2.create();
  4. var extremity = Vector2.create();
  5. function getCubicBezierXYatT(startPt, controlPt1, controlPt2, endPt, T) {
  6. var x = CubicN(T, startPt.x, controlPt1.x, controlPt2.x, endPt.x);
  7. var y = CubicN(T, startPt.y, controlPt1.y, controlPt2.y, endPt.y);
  8. return {
  9. x: x,
  10. y: y
  11. };
  12. } // cubic helper formula at T distance
  13. function CubicN(T, a, b, c, d) {
  14. var t2 = T * T;
  15. var t3 = t2 * T;
  16. return a + (-a * 3 + T * (3 * a - a * T)) * T + (3 * b + T * (-6 * b + b * 3 * T)) * T + (c * 3 - c * 3 * T) * t2 + d * t3;
  17. }
  18. function cubicBezierBounds(c) {
  19. var minX = Infinity;
  20. var maxX = -Infinity;
  21. var minY = Infinity;
  22. var maxY = -Infinity;
  23. var s = {
  24. x: c[0],
  25. y: c[1]
  26. };
  27. var c1 = {
  28. x: c[2],
  29. y: c[3]
  30. };
  31. var c2 = {
  32. x: c[4],
  33. y: c[5]
  34. };
  35. var e = {
  36. x: c[6],
  37. y: c[7]
  38. };
  39. for (var t = 0; t < 100; t++) {
  40. var pt = getCubicBezierXYatT(s, c1, c2, e, t / 100);
  41. if (pt.x < minX) {
  42. minX = pt.x;
  43. }
  44. if (pt.x > maxX) {
  45. maxX = pt.x;
  46. }
  47. if (pt.y < minY) {
  48. minY = pt.y;
  49. }
  50. if (pt.y > maxY) {
  51. maxY = pt.y;
  52. }
  53. }
  54. return {
  55. minX: minX,
  56. minY: minY,
  57. maxX: maxX,
  58. maxY: maxY
  59. };
  60. }
  61. module.exports = {
  62. getBBoxFromPoints: function getBBoxFromPoints(points, lineWidth) {
  63. if (points.length === 0) {
  64. return;
  65. }
  66. var p = points[0];
  67. var left = p.x;
  68. var right = p.x;
  69. var top = p.y;
  70. var bottom = p.y;
  71. var len = points.length;
  72. for (var i = 1; i < len; i++) {
  73. p = points[i];
  74. left = Math.min(left, p.x);
  75. right = Math.max(right, p.x);
  76. top = Math.min(top, p.y);
  77. bottom = Math.max(bottom, p.y);
  78. }
  79. lineWidth = lineWidth / 2 || 0;
  80. return {
  81. minX: left - lineWidth,
  82. minY: top - lineWidth,
  83. maxX: right + lineWidth,
  84. maxY: bottom + lineWidth
  85. };
  86. },
  87. getBBoxFromLine: function getBBoxFromLine(x0, y0, x1, y1, lineWidth) {
  88. lineWidth = lineWidth / 2 || 0;
  89. return {
  90. minX: Math.min(x0, x1) - lineWidth,
  91. minY: Math.min(y0, y1) - lineWidth,
  92. maxX: Math.max(x0, x1) + lineWidth,
  93. maxY: Math.max(y0, y1) + lineWidth
  94. };
  95. },
  96. getBBoxFromArc: function getBBoxFromArc(x, y, r, startAngle, endAngle, anticlockwise) {
  97. var diff = Math.abs(startAngle - endAngle);
  98. if (diff % Math.PI * 2 < 1e-4 && diff > 1e-4) {
  99. // Is a circle
  100. return {
  101. minX: x - r,
  102. minY: y - r,
  103. maxX: x + r,
  104. maxY: y + r
  105. };
  106. }
  107. start[0] = Math.cos(startAngle) * r + x;
  108. start[1] = Math.sin(startAngle) * r + y;
  109. end[0] = Math.cos(endAngle) * r + x;
  110. end[1] = Math.sin(endAngle) * r + y;
  111. var min = [0, 0];
  112. var max = [0, 0];
  113. Vector2.min(min, start, end);
  114. Vector2.max(max, start, end); // Thresh to [0, Math.PI * 2]
  115. startAngle = startAngle % (Math.PI * 2);
  116. if (startAngle < 0) {
  117. startAngle = startAngle + Math.PI * 2;
  118. }
  119. endAngle = endAngle % (Math.PI * 2);
  120. if (endAngle < 0) {
  121. endAngle = endAngle + Math.PI * 2;
  122. }
  123. if (startAngle > endAngle && !anticlockwise) {
  124. endAngle += Math.PI * 2;
  125. } else if (startAngle < endAngle && anticlockwise) {
  126. startAngle += Math.PI * 2;
  127. }
  128. if (anticlockwise) {
  129. var tmp = endAngle;
  130. endAngle = startAngle;
  131. startAngle = tmp;
  132. }
  133. for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {
  134. if (angle > startAngle) {
  135. extremity[0] = Math.cos(angle) * r + x;
  136. extremity[1] = Math.sin(angle) * r + y;
  137. Vector2.min(min, extremity, min);
  138. Vector2.max(max, extremity, max);
  139. }
  140. }
  141. return {
  142. minX: min[0],
  143. minY: min[1],
  144. maxX: max[0],
  145. maxY: max[1]
  146. };
  147. },
  148. getBBoxFromBezierGroup: function getBBoxFromBezierGroup(points, lineWidth) {
  149. var minX = Infinity;
  150. var maxX = -Infinity;
  151. var minY = Infinity;
  152. var maxY = -Infinity;
  153. for (var i = 0, len = points.length; i < len; i++) {
  154. var bbox = cubicBezierBounds(points[i]);
  155. if (bbox.minX < minX) {
  156. minX = bbox.minX;
  157. }
  158. if (bbox.maxX > maxX) {
  159. maxX = bbox.maxX;
  160. }
  161. if (bbox.minY < minY) {
  162. minY = bbox.minY;
  163. }
  164. if (bbox.maxY > maxY) {
  165. maxY = bbox.maxY;
  166. }
  167. }
  168. lineWidth = lineWidth / 2 || 0;
  169. return {
  170. minX: minX - lineWidth,
  171. minY: minY - lineWidth,
  172. maxX: maxX + lineWidth,
  173. maxY: maxY + lineWidth
  174. };
  175. }
  176. };