8723a3b72cacaad31b0c9cb5e86e59c1d66d8cb37af95b29fb4112393d6aba72d92dd07fb4fe69e7a9910e7055ede8d0d1164f1c902fbbddfa7ec304be05c5 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /**
  2. * @author Yi Shen(https://github.com/pissang)
  3. */
  4. import * as vec2 from './vector';
  5. import * as curve from './curve';
  6. const mathMin = Math.min;
  7. const mathMax = Math.max;
  8. const mathSin = Math.sin;
  9. const mathCos = Math.cos;
  10. const PI2 = Math.PI * 2;
  11. const start = vec2.create();
  12. const end = vec2.create();
  13. const extremity = vec2.create();
  14. /**
  15. * 从顶点数组中计算出最小包围盒,写入`min`和`max`中
  16. */
  17. export function fromPoints(points: ArrayLike<number>[], min: vec2.VectorArray, max: vec2.VectorArray) {
  18. if (points.length === 0) {
  19. return;
  20. }
  21. let p = points[0];
  22. let left = p[0];
  23. let right = p[0];
  24. let top = p[1];
  25. let bottom = p[1];
  26. for (let i = 1; i < points.length; i++) {
  27. p = points[i];
  28. left = mathMin(left, p[0]);
  29. right = mathMax(right, p[0]);
  30. top = mathMin(top, p[1]);
  31. bottom = mathMax(bottom, p[1]);
  32. }
  33. min[0] = left;
  34. min[1] = top;
  35. max[0] = right;
  36. max[1] = bottom;
  37. }
  38. export function fromLine(
  39. x0: number, y0: number, x1: number, y1: number,
  40. min: vec2.VectorArray, max: vec2.VectorArray
  41. ) {
  42. min[0] = mathMin(x0, x1);
  43. min[1] = mathMin(y0, y1);
  44. max[0] = mathMax(x0, x1);
  45. max[1] = mathMax(y0, y1);
  46. }
  47. const xDim: number[] = [];
  48. const yDim: number[] = [];
  49. /**
  50. * 从三阶贝塞尔曲线(p0, p1, p2, p3)中计算出最小包围盒,写入`min`和`max`中
  51. */
  52. export function fromCubic(
  53. x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number,
  54. min: vec2.VectorArray, max: vec2.VectorArray
  55. ) {
  56. const cubicExtrema = curve.cubicExtrema;
  57. const cubicAt = curve.cubicAt;
  58. let n = cubicExtrema(x0, x1, x2, x3, xDim);
  59. min[0] = Infinity;
  60. min[1] = Infinity;
  61. max[0] = -Infinity;
  62. max[1] = -Infinity;
  63. for (let i = 0; i < n; i++) {
  64. const x = cubicAt(x0, x1, x2, x3, xDim[i]);
  65. min[0] = mathMin(x, min[0]);
  66. max[0] = mathMax(x, max[0]);
  67. }
  68. n = cubicExtrema(y0, y1, y2, y3, yDim);
  69. for (let i = 0; i < n; i++) {
  70. const y = cubicAt(y0, y1, y2, y3, yDim[i]);
  71. min[1] = mathMin(y, min[1]);
  72. max[1] = mathMax(y, max[1]);
  73. }
  74. min[0] = mathMin(x0, min[0]);
  75. max[0] = mathMax(x0, max[0]);
  76. min[0] = mathMin(x3, min[0]);
  77. max[0] = mathMax(x3, max[0]);
  78. min[1] = mathMin(y0, min[1]);
  79. max[1] = mathMax(y0, max[1]);
  80. min[1] = mathMin(y3, min[1]);
  81. max[1] = mathMax(y3, max[1]);
  82. }
  83. /**
  84. * 从二阶贝塞尔曲线(p0, p1, p2)中计算出最小包围盒,写入`min`和`max`中
  85. */
  86. export function fromQuadratic(
  87. x0: number, y0: number, x1: number, y1: number, x2: number, y2: number,
  88. min: vec2.VectorArray, max: vec2.VectorArray
  89. ) {
  90. const quadraticExtremum = curve.quadraticExtremum;
  91. const quadraticAt = curve.quadraticAt;
  92. // Find extremities, where derivative in x dim or y dim is zero
  93. const tx =
  94. mathMax(
  95. mathMin(quadraticExtremum(x0, x1, x2), 1), 0
  96. );
  97. const ty =
  98. mathMax(
  99. mathMin(quadraticExtremum(y0, y1, y2), 1), 0
  100. );
  101. const x = quadraticAt(x0, x1, x2, tx);
  102. const y = quadraticAt(y0, y1, y2, ty);
  103. min[0] = mathMin(x0, x2, x);
  104. min[1] = mathMin(y0, y2, y);
  105. max[0] = mathMax(x0, x2, x);
  106. max[1] = mathMax(y0, y2, y);
  107. }
  108. /**
  109. * 从圆弧中计算出最小包围盒,写入`min`和`max`中
  110. */
  111. export function fromArc(
  112. x: number, y: number, rx: number, ry: number, startAngle: number, endAngle: number, anticlockwise: boolean,
  113. min: vec2.VectorArray, max: vec2.VectorArray
  114. ) {
  115. const vec2Min = vec2.min;
  116. const vec2Max = vec2.max;
  117. const diff = Math.abs(startAngle - endAngle);
  118. if (diff % PI2 < 1e-4 && diff > 1e-4) {
  119. // Is a circle
  120. min[0] = x - rx;
  121. min[1] = y - ry;
  122. max[0] = x + rx;
  123. max[1] = y + ry;
  124. return;
  125. }
  126. start[0] = mathCos(startAngle) * rx + x;
  127. start[1] = mathSin(startAngle) * ry + y;
  128. end[0] = mathCos(endAngle) * rx + x;
  129. end[1] = mathSin(endAngle) * ry + y;
  130. vec2Min(min, start, end);
  131. vec2Max(max, start, end);
  132. // Thresh to [0, Math.PI * 2]
  133. startAngle = startAngle % (PI2);
  134. if (startAngle < 0) {
  135. startAngle = startAngle + PI2;
  136. }
  137. endAngle = endAngle % (PI2);
  138. if (endAngle < 0) {
  139. endAngle = endAngle + PI2;
  140. }
  141. if (startAngle > endAngle && !anticlockwise) {
  142. endAngle += PI2;
  143. }
  144. else if (startAngle < endAngle && anticlockwise) {
  145. startAngle += PI2;
  146. }
  147. if (anticlockwise) {
  148. const tmp = endAngle;
  149. endAngle = startAngle;
  150. startAngle = tmp;
  151. }
  152. // const number = 0;
  153. // const step = (anticlockwise ? -Math.PI : Math.PI) / 2;
  154. for (let angle = 0; angle < endAngle; angle += Math.PI / 2) {
  155. if (angle > startAngle) {
  156. extremity[0] = mathCos(angle) * rx + x;
  157. extremity[1] = mathSin(angle) * ry + y;
  158. vec2Min(min, extremity, min);
  159. vec2Max(max, extremity, max);
  160. }
  161. }
  162. }