bbox.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import Vector2 from './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,
  10. 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,
  56. minY,
  57. maxX,
  58. maxY
  59. };
  60. }
  61. function getBBoxFromPoints(points, lineWidth) {
  62. if (points.length === 0) {
  63. return;
  64. }
  65. var p = points[0];
  66. var left = p.x;
  67. var right = p.x;
  68. var top = p.y;
  69. var bottom = p.y;
  70. var len = points.length;
  71. for (var i = 1; i < len; i++) {
  72. p = points[i];
  73. left = Math.min(left, p.x);
  74. right = Math.max(right, p.x);
  75. top = Math.min(top, p.y);
  76. bottom = Math.max(bottom, p.y);
  77. }
  78. lineWidth = lineWidth / 2 || 0;
  79. return {
  80. minX: left - lineWidth,
  81. minY: top - lineWidth,
  82. maxX: right + lineWidth,
  83. maxY: bottom + lineWidth
  84. };
  85. }
  86. function getBBoxFromLine(x0, y0, x1, y1, lineWidth) {
  87. lineWidth = lineWidth / 2 || 0;
  88. return {
  89. minX: Math.min(x0, x1) - lineWidth,
  90. minY: Math.min(y0, y1) - lineWidth,
  91. maxX: Math.max(x0, x1) + lineWidth,
  92. maxY: Math.max(y0, y1) + lineWidth
  93. };
  94. }
  95. function getBBoxFromArc(x, y, r, startAngle, endAngle, anticlockwise) {
  96. var diff = Math.abs(startAngle - endAngle);
  97. if (diff % (Math.PI * 2) < 1e-4 && diff > 1e-4) {
  98. // Is a circle
  99. return {
  100. minX: x - r,
  101. minY: y - r,
  102. maxX: x + r,
  103. maxY: y + r
  104. };
  105. }
  106. start[0] = Math.cos(startAngle) * r + x;
  107. start[1] = Math.sin(startAngle) * r + y;
  108. end[0] = Math.cos(endAngle) * r + x;
  109. end[1] = Math.sin(endAngle) * r + y;
  110. var min = [0, 0];
  111. var max = [0, 0];
  112. Vector2.min(min, start, end);
  113. Vector2.max(max, start, end); // Thresh to [0, Math.PI * 2]
  114. startAngle = startAngle % (Math.PI * 2);
  115. if (startAngle < 0) {
  116. startAngle = startAngle + Math.PI * 2;
  117. }
  118. endAngle = endAngle % (Math.PI * 2);
  119. if (endAngle < 0) {
  120. endAngle = endAngle + Math.PI * 2;
  121. }
  122. if (startAngle > endAngle && !anticlockwise) {
  123. endAngle += Math.PI * 2;
  124. } else if (startAngle < endAngle && anticlockwise) {
  125. startAngle += Math.PI * 2;
  126. }
  127. if (anticlockwise) {
  128. var tmp = endAngle;
  129. endAngle = startAngle;
  130. startAngle = tmp;
  131. }
  132. for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {
  133. if (angle > startAngle) {
  134. extremity[0] = Math.cos(angle) * r + x;
  135. extremity[1] = Math.sin(angle) * r + y;
  136. Vector2.min(min, extremity, min);
  137. Vector2.max(max, extremity, max);
  138. }
  139. }
  140. return {
  141. minX: min[0],
  142. minY: min[1],
  143. maxX: max[0],
  144. maxY: max[1]
  145. };
  146. }
  147. function getBBoxFromBezierGroup(points, lineWidth) {
  148. var minX = Infinity;
  149. var maxX = -Infinity;
  150. var minY = Infinity;
  151. var maxY = -Infinity;
  152. for (var i = 0, len = points.length; i < len; i++) {
  153. var bbox = cubicBezierBounds(points[i]);
  154. if (bbox.minX < minX) {
  155. minX = bbox.minX;
  156. }
  157. if (bbox.maxX > maxX) {
  158. maxX = bbox.maxX;
  159. }
  160. if (bbox.minY < minY) {
  161. minY = bbox.minY;
  162. }
  163. if (bbox.maxY > maxY) {
  164. maxY = bbox.maxY;
  165. }
  166. }
  167. lineWidth = lineWidth / 2 || 0;
  168. return {
  169. minX: minX - lineWidth,
  170. minY: minY - lineWidth,
  171. maxX: maxX + lineWidth,
  172. maxY: maxY + lineWidth
  173. };
  174. }
  175. export { getBBoxFromPoints, getBBoxFromLine, getBBoxFromArc, getBBoxFromBezierGroup };