attach-view.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import {
  2. getDiffPolygons,
  3. getVectorLine,
  4. isPolygonPointInner,
  5. lineCenter,
  6. lineInner,
  7. lineIntersection,
  8. lineLen,
  9. lineVector,
  10. lineVerticalVector,
  11. Pos,
  12. vector2IncludedAngle,
  13. verticalVector,
  14. } from "@/utils/math";
  15. import { LineData } from ".";
  16. import { getJoinLine } from "./attach-server";
  17. import { MathUtils } from "three";
  18. import { diffArrayChange, flatPositions, rangMod } from "@/utils/shared";
  19. import { globalWatch, installGlobalVar } from "@/core/hook/use-global-vars";
  20. import { useStore } from "@/core/store";
  21. import { IconData } from "../icon";
  22. import { computed, reactive, watch, watchEffect } from "vue";
  23. import { Transform } from "konva/lib/Util";
  24. import { useTestPoints } from "@/core/hook/use-debugger";
  25. import { sortFn } from "@/core/store/store";
  26. const minAngle = MathUtils.degToRad(0.1);
  27. const palAngle = MathUtils.degToRad(20);
  28. const getLineRect = (points: Pos[], strokeWidth: number) => {
  29. const v = lineVector(points);
  30. const vv = verticalVector(v);
  31. const offset = vv.clone().multiplyScalar(strokeWidth / 2);
  32. const top = points.map((p) => offset.clone().add(p));
  33. offset.multiplyScalar(-1);
  34. const bottom = points.map((p) => offset.clone().add(p));
  35. return [...top, bottom[1], bottom[0]];
  36. };
  37. export const useGetExtendPolygon = installGlobalVar(() => {
  38. // const testPoints = useTestPoints();
  39. return (data: LineData, line: LineData["lines"][0]) => {
  40. const getJoinInfo = (
  41. joinLine: LineData["lines"][0],
  42. joinPoints: Pos[],
  43. getNdx: number
  44. ) => {
  45. const jNdx = joinPoints.indexOf(linePoints[getNdx]);
  46. if ((getNdx === 0 && jNdx === 0) || (getNdx === 1 && jNdx === 1)) {
  47. joinPoints.reverse();
  48. }
  49. const direInv = getNdx === 0 && jNdx === 0;
  50. const joinv = lineVector(joinPoints).multiplyScalar(-1);
  51. const angle = vector2IncludedAngle(
  52. direInv ? joinv : linev,
  53. direInv ? linev : joinv
  54. );
  55. const checkAngle = rangMod(Math.abs(angle), Math.PI);
  56. if (checkAngle < minAngle || checkAngle > Math.PI - minAngle) return;
  57. const join = lineIntersection(linePoints, joinPoints);
  58. if (!join) return;
  59. const center = lineCenter([...linePoints, ...joinPoints]);
  60. const joinRect = getLineRect(joinPoints, joinLine.strokeWidth);
  61. const cheJoinInnerNdxs = getNdx === 0 ? [0, 3] : [1, 2];
  62. const cheLineInnerNdxs = getNdx === 0 ? [1, 2] : [0, 3];
  63. if (
  64. cheJoinInnerNdxs.some((ndx) =>
  65. isPolygonPointInner(lineRect, joinRect[ndx])
  66. ) ||
  67. cheLineInnerNdxs.some((ndx) =>
  68. isPolygonPointInner(joinRect, lineRect[ndx])
  69. )
  70. ) {
  71. return;
  72. }
  73. let outerLine1, innerLine1, outerLine2, innerLine2;
  74. const rectJust =
  75. lineLen(center, lineRect[0]) > lineLen(center, lineRect[3]);
  76. if (rectJust) {
  77. outerLine1 = [lineRect[0], lineRect[1]];
  78. innerLine1 = [lineRect[3], lineRect[2]];
  79. } else {
  80. outerLine1 = [lineRect[3], lineRect[2]];
  81. innerLine1 = [lineRect[0], lineRect[1]];
  82. }
  83. if (lineLen(center, joinRect[0]) > lineLen(center, joinRect[3])) {
  84. outerLine2 = [joinRect[0], joinRect[1]];
  85. innerLine2 = [joinRect[3], joinRect[2]];
  86. } else {
  87. outerLine2 = [joinRect[3], joinRect[2]];
  88. innerLine2 = [joinRect[0], joinRect[1]];
  89. }
  90. const outer = lineIntersection(outerLine1, outerLine2);
  91. if (!outer) return;
  92. let inside: Pos = lineIntersection(innerLine1, innerLine2)!;
  93. if (!inside) return;
  94. const insideLineInner1 = lineInner(innerLine1, inside);
  95. const insideLineInner2 = lineInner(innerLine2, inside);
  96. if (!insideLineInner1 && !insideLineInner2) return;
  97. let insides = [inside];
  98. // 如果角度过于尖锐则使用平行线
  99. let outers = [outer];
  100. if (checkAngle < palAngle) {
  101. const jov = getVectorLine(lineVerticalVector([join, outer]), join);
  102. const outer1 = lineIntersection(jov, outerLine1)!;
  103. outers = [outer1, join];
  104. }
  105. const repsResult: { rep: number; points: Pos[] }[] = [];
  106. if (getNdx === 0) {
  107. repsResult.push({
  108. rep: 0,
  109. points: rectJust ? outers.reverse() : insides,
  110. });
  111. repsResult.push({ rep: 3, points: rectJust ? insides : outers });
  112. } else {
  113. repsResult.push({ rep: 1, points: rectJust ? outers : insides });
  114. repsResult.push({
  115. rep: 2,
  116. points: rectJust ? insides : outers.reverse(),
  117. });
  118. }
  119. // testPoints.value.push(...insides, ...outers);
  120. return repsResult;
  121. };
  122. const linePoints = [line.a, line.b].map(
  123. (id) => data.points.find((item) => item.id === id)!
  124. );
  125. const lineRect: Pos[] = getLineRect(linePoints, line.strokeWidth);
  126. const polygon = [...lineRect];
  127. const linev = lineVector(linePoints);
  128. linePoints.forEach((point, ndx) => {
  129. const joinLines = getJoinLine(data, line, point.id);
  130. if (joinLines.length !== 1) return;
  131. const repsResult = getJoinInfo(joinLines[0], joinLines[0].points, ndx);
  132. if (!repsResult) return;
  133. for (const rep of repsResult) {
  134. const ndx = polygon.indexOf(lineRect[rep.rep]);
  135. polygon.splice(ndx, 1, ...rep.points);
  136. }
  137. });
  138. return polygon;
  139. };
  140. });
  141. // 计算与icon相差的多边形
  142. export const useGetDiffPolygons = installGlobalVar(() => {
  143. const store = useStore();
  144. const iconPolygons = reactive({}) as { [key in string]: Pos[] };
  145. const icons = computed(() => store.getTypeItems("icon"));
  146. const line = computed(() => store.getTypeItems("line")[0]);
  147. const watchIcon = (id: string) => {
  148. const stopWatch = watchEffect(() => {
  149. const icon = icons.value.find((item) => item.id === id);
  150. if (!icon) {
  151. stopWatch();
  152. delete iconPolygons[id];
  153. return;
  154. }
  155. if (!line.value || sortFn(line.value, icon) > 0) {
  156. delete iconPolygons[id];
  157. return;
  158. }
  159. const rect = [
  160. { x: -icon.width / 2, y: -icon.height / 2 },
  161. { x: icon.width / 2, y: -icon.height / 2 },
  162. { x: icon.width / 2, y: icon.height / 2 },
  163. { x: -icon.width / 2, y: icon.height / 2 },
  164. ];
  165. const mat = new Transform(icon.mat);
  166. iconPolygons[id] = rect.map((p) => mat.point(p));
  167. });
  168. };
  169. const stopWatch = globalWatch(
  170. () => {
  171. console.log(icons.value.length);
  172. return icons.value.map((item) => item.id);
  173. },
  174. (ids, oIds = []) => {
  175. const { added, deleted } = diffArrayChange(ids, oIds);
  176. console.log(added, deleted);
  177. deleted.forEach((id) => {
  178. delete iconPolygons[id];
  179. });
  180. added.forEach(watchIcon);
  181. },
  182. { immediate: true }
  183. );
  184. return {
  185. var: (polygon: Pos[]) => {
  186. const targets = Object.values(iconPolygons);
  187. if (!targets.length) return [polygon];
  188. return getDiffPolygons(polygon, targets);
  189. },
  190. onDestroy: stopWatch,
  191. };
  192. });