generate.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. import { SLine, SPoint } from "@persagy-web/draw/lib";
  2. // @ts-ignore
  3. import { intersect, polygon, segments, combine, selectUnion, selectDifference, selectDifferenceRev } from "polybooljs";
  4. export class generate {
  5. /**
  6. * 计算角平分线上的距离这两条直线l的点
  7. *
  8. * @param p1 第一个点
  9. * @param p2 第二个点 也是2条线的交点
  10. * @param p3 第三个点
  11. * @param l 距离2条线的距离
  12. * @return number Array(4) 两个点
  13. */
  14. static getBisector(p1: SPoint, p2: SPoint, p3: SPoint, l: number): [number, number, number, number] {
  15. const dy1 = p1.y - p2.y;
  16. const dx1 = p1.x - p2.x;
  17. const dy2 = p3.y - p2.y;
  18. const dx2 = p3.x - p2.x;
  19. // 线1的斜率
  20. const k1 = dy1 / dx1;
  21. // 线2的斜率
  22. const k2 = dy2 / dx2;
  23. // 线1与x轴的夹角
  24. const temp1 = Math.atan(k1)
  25. let a1 = dx1 >= 0 ? temp1 : temp1 + Math.PI;
  26. // 线2与x轴的夹角
  27. const temp2 = Math.atan(k2)
  28. let a2 = dx2 >= 0 ? temp2 : temp2 + Math.PI;
  29. // 角平分线斜率
  30. const k = Math.tan((a1 + a2) / 2);
  31. // 角平分线b
  32. const b = p2.y - k * p2.x;
  33. // 距离两条线l的点 到交点p2的距离
  34. const Lb2 = l / Math.sin(Math.abs(a1 - a2) / 2);
  35. // 特殊情况-角分线垂直或平行X轴
  36. if (k1 / k2 == -1) {
  37. if (dx2 * dx1 > 0 && dy2 * dy1 < 0) { // 上下开角
  38. return [Number((p2.x - Lb2).toFixed(6)), Number(p2.y.toFixed(6)), Number((p2.x + Lb2).toFixed(6)), Number(p2.y.toFixed(6))]
  39. } else if (dx2 * dx1 < 0 && dy2 * dy1 > 0) { // 左右开角
  40. return [Number(p2.x.toFixed(6)), Number((p2.y - Lb2).toFixed(6)), Number(p2.x.toFixed(6)), Number((p2.y + Lb2).toFixed(6))]
  41. }
  42. }
  43. // 将距离公式与直线方程连立,并且将直线方程代入,得到一元二次方程 Ax^2 + Bx + C = 0;然后根据得根公式得到2个解
  44. const A = k * k + 1;
  45. const B = 2 * k * b - 2 * p2.x - 2 * k * p2.y;
  46. const C = b * b - Lb2 * Lb2 + p2.x * p2.x + p2.y * p2.y - 2 * b * p2.y;
  47. // 求解
  48. const X1 = (-B + Math.sqrt(B * B - 4 * A * C)) / (2 * A);
  49. const X2 = (-B - Math.sqrt(B * B - 4 * A * C)) / (2 * A);
  50. const Y1 = k * X1 + b;
  51. const Y2 = k * X2 + b;
  52. return [Number(X1.toFixed(6)), Number(Y1.toFixed(6)), Number(X2.toFixed(6)), Number(Y2.toFixed(6))]
  53. }
  54. /**
  55. * 计算一条线的过p1垂线上距离线l的2个点
  56. *
  57. * @param p1 点1
  58. * @param p2 点2
  59. * @param l 距离这条线的距离
  60. * @return number Array(4) 两个点
  61. */
  62. static getVertical(p1: SPoint, p2: SPoint, l: number): [number, number, number, number] {
  63. const dy1 = p1.y - p2.y;
  64. const dx1 = p1.x - p2.x;
  65. if (dy1 == 0) {
  66. return [Number(p1.x.toFixed(6)), Number((p1.y - l).toFixed(6)), Number(p1.x.toFixed(6)), Number((p1.y + l).toFixed(6))]
  67. }
  68. if (dx1 == 0) {
  69. return [Number((p1.x - l).toFixed(6)), Number(p1.y.toFixed(6)), Number((p1.x + l).toFixed(6)), Number(p1.y.toFixed(6))]
  70. }
  71. // 线1的斜率
  72. const k1 = dy1 / dx1;
  73. // 垂线的斜率
  74. const k = -1 / k1;
  75. // 垂线的b
  76. const b = p1.y - k * p1.x;
  77. // 将距离公式与直线方程连立,并且将直线方程代入,得到一元二次方程 Ax^2 + Bx + C = 0;然后根据得根公式得到2个解
  78. const A = k * k + 1;
  79. const B = 2 * k * b - 2 * p1.x - 2 * k * p1.y;
  80. const C = b * b - l * l + p1.x * p1.x + p1.y * p1.y - 2 * b * p1.y;
  81. // 求解
  82. const X1 = (-B + Math.sqrt(B * B - 4 * A * C)) / (2 * A);
  83. const X2 = (-B - Math.sqrt(B * B - 4 * A * C)) / (2 * A);
  84. const Y1 = k * X1 + b;
  85. const Y2 = k * X2 + b;
  86. return [Number(X1.toFixed(6)), Number(Y1.toFixed(6)), Number(X2.toFixed(6)), Number(Y2.toFixed(6))]
  87. }
  88. /**
  89. * 计算线段交点
  90. *
  91. * @param line1 线段1
  92. * @param line2 线段2
  93. * @return SPoint 交点 null 平行但不重合 'repeat' 重合
  94. */
  95. static lineIntersection(
  96. p1: SPoint, p2: SPoint, p3: SPoint, p4: SPoint
  97. ): SPoint | null | string {
  98. let k1 = (p2.y - p1.y) / (p2.x - p1.x);
  99. let b1 = p2.y - k1 * p2.x;
  100. let k2 = (p4.y - p3.y) / (p4.x - p3.x);
  101. let b2 = p3.y - k2 * p3.x;
  102. if (k1 == k2) {
  103. if (b1 == b2) {
  104. return "repeat";
  105. }
  106. return null;
  107. }
  108. let intersectionX = (b2 - b1) / (k1 - k2);
  109. let intersectionY = k1 * intersectionX + b1;
  110. // 取线段上的最大最小值可以上下换
  111. let minX = Math.min(p1.x, p2.x);
  112. let maxX = Math.max(p3.x, p4.x);
  113. if (intersectionX >= minX && intersectionX <= maxX) {
  114. return new SPoint(intersectionX, intersectionY);
  115. }
  116. return null;
  117. }
  118. /**
  119. * 一条线生成的4个点,根据这4个点和这条线,将轮廓线排序
  120. *
  121. * @description 计算3条线的斜率,差值小的一组
  122. *
  123. * @param [number, number, number, number]
  124. * @param [number, number, number, number]
  125. * @param SLine
  126. * @return 两组线的数组
  127. */
  128. static generateOrderLine(arr1: [number, number, number, number], arr2: [number, number, number, number], line: SLine) {
  129. if (arr1.length && arr2.length) {
  130. // 排除特殊情况
  131. if (line.dy == 0) { // 线本身是水平的
  132. if (generate.isEqual(arr1[1], arr2[1])) {
  133. return [
  134. [Number(arr1[0].toFixed(6)), Number(arr1[1].toFixed(6)), Number(arr2[0].toFixed(6)), Number(arr2[1].toFixed(6))],
  135. [Number(arr1[2].toFixed(6)), Number(arr1[3].toFixed(6)), Number(arr2[2].toFixed(6)), Number(arr2[3].toFixed(6))],
  136. ]
  137. } else {
  138. return [
  139. [Number(arr1[0].toFixed(6)), Number(arr1[1].toFixed(6)), Number(arr2[2].toFixed(6)), Number(arr2[3].toFixed(6))],
  140. [Number(arr1[2].toFixed(6)), Number(arr1[3].toFixed(6)), Number(arr2[0].toFixed(6)), Number(arr2[1].toFixed(6))],
  141. ]
  142. }
  143. }
  144. if (line.dx == 0) { // 线本身是垂直的
  145. if (generate.isEqual(arr1[0], arr2[0])) { // 与另外一组的第一个点的连线是水平的
  146. return [
  147. [Number(arr1[0].toFixed(6)), Number(arr1[1].toFixed(6)), Number(arr2[0].toFixed(6)), Number(arr2[1].toFixed(6))],
  148. [Number(arr1[2].toFixed(6)), Number(arr1[3].toFixed(6)), Number(arr2[2].toFixed(6)), Number(arr2[3].toFixed(6))],
  149. ]
  150. } else { // 与另外一组的第二个点的连线是竖直的
  151. return [
  152. [Number(arr1[0].toFixed(6)), Number(arr1[1].toFixed(6)), Number(arr2[2].toFixed(6)), Number(arr2[3].toFixed(6))],
  153. [Number(arr1[2].toFixed(6)), Number(arr1[3].toFixed(6)), Number(arr2[0].toFixed(6)), Number(arr2[1].toFixed(6))],
  154. ]
  155. }
  156. }
  157. // 标准线的斜率
  158. const k = line.dy / line.dx;
  159. // 第一条线
  160. const l1 = new SLine(arr1[0], arr1[1], arr2[0], arr2[1]);
  161. // 第一条线的斜率
  162. const k1 = l1.dy / l1.dx;
  163. // 第一条线斜率差值
  164. const dk1 = Math.abs(k1 - k);
  165. // 第二条线
  166. const l2 = new SLine(arr1[0], arr1[1], arr2[2], arr2[3]);
  167. // 第二条线的斜率
  168. const k2 = l2.dy / l2.dx;
  169. // 第二条线斜率差值
  170. const dk2 = Math.abs(k2 - k);
  171. // 判断差值小的放一组
  172. if (dk1 < dk2) {
  173. return [
  174. [Number(arr1[0].toFixed(6)), Number(arr1[1].toFixed(6)), Number(arr2[0].toFixed(6)), Number(arr2[1].toFixed(6))],
  175. [Number(arr1[2].toFixed(6)), Number(arr1[3].toFixed(6)), Number(arr2[2].toFixed(6)), Number(arr2[3].toFixed(6))],
  176. ]
  177. } else {
  178. return [
  179. [Number(arr1[0].toFixed(6)), Number(arr1[1].toFixed(6)), Number(arr2[2].toFixed(6)), Number(arr2[3].toFixed(6))],
  180. [Number(arr1[2].toFixed(6)), Number(arr1[3].toFixed(6)), Number(arr2[0].toFixed(6)), Number(arr2[1].toFixed(6))],
  181. ]
  182. }
  183. }
  184. return []
  185. }
  186. /**
  187. * 删除轮廓线中共线的点
  188. *
  189. * @description 连续2条线的斜率相等则这3点共线,删除中间的一个点
  190. * @param outline 轮廓线数据
  191. * @return 简化后的轮廓线数据
  192. */
  193. static simplyOutline(outline: SPoint[]): SPoint[] {
  194. for (let i = 0; i < outline.length - 1; i++) {
  195. if (outline[i].x - outline[i + 1].x == 0 && outline[i].y - outline[i + 1].y == 0) {
  196. outline.splice(i, 1);
  197. i--;
  198. }
  199. }
  200. if (outline.length > 2) {
  201. for (let i = 1; i < outline.length - 1; i++) {
  202. if (outline[i].y - outline[i - 1].y == 0 && outline[i + 1].y - outline[i].y == 0) {
  203. outline.splice(i, 1);
  204. i--;
  205. } else if (outline[i].x - outline[i - 1].x == 0 && outline[i + 1].x - outline[i].x == 0) {
  206. outline.splice(i, 1);
  207. i--;
  208. } else if (
  209. outline[i].y - outline[i - 1].y == outline[i + 1].y - outline[i].y &&
  210. outline[i].x - outline[i - 1].x == outline[i + 1].x - outline[i].x
  211. ) {
  212. outline.splice(i, 1);
  213. i--;
  214. }
  215. }
  216. }
  217. return outline
  218. }
  219. /**
  220. * 解决0.1+0.2!=0.3的问题
  221. *
  222. * @param a 第一个数
  223. * @param b 第二个数
  224. * @return 是否相等
  225. */
  226. static isEqual(a: number, b: number) {
  227. return Math.abs(a - b) < Math.pow(2, -52);
  228. }
  229. static llll() {
  230. const a = {
  231. regions: [
  232. [[0, 0], [0, 200], [200, 200], [200, 0]],
  233. // [[150, 150], [250, 150], [250, 250], [150, 250]],
  234. ],
  235. inverted: false
  236. }
  237. const b = {
  238. regions: [
  239. [[150, 150], [250, 150], [250, 250], [150, 250]],
  240. ],
  241. inverted: false
  242. }
  243. const start = +new Date()
  244. const sa = segments(a)
  245. const sb = segments(b)
  246. const comb = combine(sa, sb)
  247. // const start = +new Date()
  248. // const poly = segments(a)
  249. // console.log(segments(a))
  250. // const end = +new Date();
  251. // console.log(end - start);
  252. // console.log(polygon(poly));
  253. console.log(comb);
  254. const selecomb = selectDifference(comb)
  255. console.log(selecomb)
  256. console.log(polygon(selecomb))
  257. const end = +new Date();
  258. console.log(end - start);
  259. }
  260. }