bbox.js 4.5 KB

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