arc.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { distance, piMod } from './util';
  2. import ellipse from './ellipse';
  3. // 偏导数 x
  4. function derivativeXAt(cx, cy, rx, ry, xRotation, startAngle, endAngle, angle) {
  5. return -1 * rx * Math.cos(xRotation) * Math.sin(angle) - ry * Math.sin(xRotation) * Math.cos(angle);
  6. }
  7. // 偏导数 y
  8. function derivativeYAt(cx, cy, rx, ry, xRotation, startAngle, endAngle, angle) {
  9. return -1 * rx * Math.sin(xRotation) * Math.sin(angle) + ry * Math.cos(xRotation) * Math.cos(angle);
  10. }
  11. // x 的极值
  12. function xExtrema(rx, ry, xRotation) {
  13. return Math.atan((-ry / rx) * Math.tan(xRotation));
  14. }
  15. // y 的极值
  16. function yExtrema(rx, ry, xRotation) {
  17. return Math.atan(ry / (rx * Math.tan(xRotation)));
  18. }
  19. // 根据角度求 x 坐标
  20. function xAt(cx, cy, rx, ry, xRotation, angle) {
  21. return rx * Math.cos(xRotation) * Math.cos(angle) - ry * Math.sin(xRotation) * Math.sin(angle) + cx;
  22. }
  23. // 根据角度求 y 坐标
  24. function yAt(cx, cy, rx, ry, xRotation, angle) {
  25. return rx * Math.sin(xRotation) * Math.cos(angle) + ry * Math.cos(xRotation) * Math.sin(angle) + cy;
  26. }
  27. // 获取点在椭圆上的角度
  28. function getAngle(rx, ry, x0, y0) {
  29. var angle = Math.atan2(y0 * rx, x0 * ry);
  30. // 转换到 0 - 2PI 内
  31. return (angle + Math.PI * 2) % (Math.PI * 2);
  32. }
  33. // 根据角度获取,x,y
  34. function getPoint(rx, ry, angle) {
  35. return {
  36. x: rx * Math.cos(angle),
  37. y: ry * Math.sin(angle),
  38. };
  39. }
  40. // 旋转
  41. function rotate(x, y, angle) {
  42. var cos = Math.cos(angle);
  43. var sin = Math.sin(angle);
  44. return [x * cos - y * sin, x * sin + y * cos];
  45. }
  46. export default {
  47. /**
  48. * 计算包围盒
  49. * @param {number} cx 圆心 x
  50. * @param {number} cy 圆心 y
  51. * @param {number} rx x 轴方向的半径
  52. * @param {number} ry y 轴方向的半径
  53. * @param {number} xRotation 旋转角度
  54. * @param {number} startAngle 起始角度
  55. * @param {number} endAngle 结束角度
  56. * @return {object} 包围盒对象
  57. */
  58. box: function (cx, cy, rx, ry, xRotation, startAngle, endAngle) {
  59. var xDim = xExtrema(rx, ry, xRotation);
  60. var minX = Infinity;
  61. var maxX = -Infinity;
  62. var xs = [startAngle, endAngle];
  63. for (var i = -Math.PI * 2; i <= Math.PI * 2; i += Math.PI) {
  64. var xAngle = xDim + i;
  65. if (startAngle < endAngle) {
  66. if (startAngle < xAngle && xAngle < endAngle) {
  67. xs.push(xAngle);
  68. }
  69. }
  70. else {
  71. if (endAngle < xAngle && xAngle < startAngle) {
  72. xs.push(xAngle);
  73. }
  74. }
  75. }
  76. for (var i = 0; i < xs.length; i++) {
  77. var x = xAt(cx, cy, rx, ry, xRotation, xs[i]);
  78. if (x < minX) {
  79. minX = x;
  80. }
  81. if (x > maxX) {
  82. maxX = x;
  83. }
  84. }
  85. var yDim = yExtrema(rx, ry, xRotation);
  86. var minY = Infinity;
  87. var maxY = -Infinity;
  88. var ys = [startAngle, endAngle];
  89. for (var i = -Math.PI * 2; i <= Math.PI * 2; i += Math.PI) {
  90. var yAngle = yDim + i;
  91. if (startAngle < endAngle) {
  92. if (startAngle < yAngle && yAngle < endAngle) {
  93. ys.push(yAngle);
  94. }
  95. }
  96. else {
  97. if (endAngle < yAngle && yAngle < startAngle) {
  98. ys.push(yAngle);
  99. }
  100. }
  101. }
  102. for (var i = 0; i < ys.length; i++) {
  103. var y = yAt(cx, cy, rx, ry, xRotation, ys[i]);
  104. if (y < minY) {
  105. minY = y;
  106. }
  107. if (y > maxY) {
  108. maxY = y;
  109. }
  110. }
  111. return {
  112. x: minX,
  113. y: minY,
  114. width: maxX - minX,
  115. height: maxY - minY,
  116. };
  117. },
  118. /**
  119. * 获取圆弧的长度,计算圆弧长度时不考虑旋转角度,
  120. * 仅跟 rx, ry, startAngle, endAngle 相关
  121. * @param {number} cx 圆心 x
  122. * @param {number} cy 圆心 y
  123. * @param {number} rx x 轴方向的半径
  124. * @param {number} ry y 轴方向的半径
  125. * @param {number} xRotation 旋转角度
  126. * @param {number} startAngle 起始角度
  127. * @param {number} endAngle 结束角度
  128. */
  129. length: function (cx, cy, rx, ry, xRotation, startAngle, endAngle) { },
  130. /**
  131. * 获取指定点到圆弧的最近距离的点
  132. * @param {number} cx 圆心 x
  133. * @param {number} cy 圆心 y
  134. * @param {number} rx x 轴方向的半径
  135. * @param {number} ry y 轴方向的半径
  136. * @param {number} xRotation 旋转角度
  137. * @param {number} startAngle 起始角度
  138. * @param {number} endAngle 结束角度
  139. * @param {number} x0 指定点的 x
  140. * @param {number} y0 指定点的 y
  141. * @return {object} 到指定点最近距离的点
  142. */
  143. nearestPoint: function (cx, cy, rx, ry, xRotation, startAngle, endAngle, x0, y0) {
  144. // 将最近距离问题转换成到椭圆中心 0,0 没有旋转的椭圆问题
  145. var relativeVector = rotate(x0 - cx, y0 - cy, -xRotation);
  146. var x1 = relativeVector[0], y1 = relativeVector[1];
  147. // 计算点到椭圆的最近的点
  148. var relativePoint = ellipse.nearestPoint(0, 0, rx, ry, x1, y1);
  149. // 获取点在椭圆上的角度
  150. var angle = getAngle(rx, ry, relativePoint.x, relativePoint.y);
  151. // 点没有在圆弧上
  152. if (angle < startAngle) {
  153. // 小于起始圆弧
  154. relativePoint = getPoint(rx, ry, startAngle);
  155. }
  156. else if (angle > endAngle) {
  157. // 大于结束圆弧
  158. relativePoint = getPoint(rx, ry, endAngle);
  159. }
  160. // 旋转到 xRotation 的角度
  161. var vector = rotate(relativePoint.x, relativePoint.y, xRotation);
  162. return {
  163. x: vector[0] + cx,
  164. y: vector[1] + cy,
  165. };
  166. },
  167. pointDistance: function (cx, cy, rx, ry, xRotation, startAngle, endAngle, x0, y0) {
  168. var nearestPoint = this.nearestPoint(cx, cy, rx, ry, x0, y0);
  169. return distance(nearestPoint.x, nearestPoint.y, x0, y0);
  170. },
  171. pointAt: function (cx, cy, rx, ry, xRotation, startAngle, endAngle, t) {
  172. var angle = (endAngle - startAngle) * t + startAngle;
  173. return {
  174. x: xAt(cx, cy, rx, ry, xRotation, angle),
  175. y: yAt(cx, cy, rx, ry, xRotation, angle),
  176. };
  177. },
  178. tangentAngle: function (cx, cy, rx, ry, xRotation, startAngle, endAngle, t) {
  179. var angle = (endAngle - startAngle) * t + startAngle;
  180. var dx = derivativeXAt(cx, cy, rx, ry, xRotation, startAngle, endAngle, angle);
  181. var dy = derivativeYAt(cx, cy, rx, ry, xRotation, startAngle, endAngle, angle);
  182. return piMod(Math.atan2(dy, dx));
  183. },
  184. };
  185. //# sourceMappingURL=arc.js.map