attach-view.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. import {
  2. eqPoint,
  3. getDiffPolygons,
  4. getVectorLine,
  5. isPolygonPointInner,
  6. lineCenter,
  7. lineInner,
  8. lineIntersection,
  9. lineLen,
  10. lineVector,
  11. lineVerticalVector,
  12. Pos,
  13. vector2IncludedAngle,
  14. verticalVector,
  15. } from "@/utils/math";
  16. import { LineData } from ".";
  17. import { getJoinLine } from "./attach-server";
  18. import { MathUtils } from "three";
  19. import { diffArrayChange, rangMod } from "@/utils/shared";
  20. import { globalWatch, installGlobalVar } from "@/core/hook/use-global-vars";
  21. import { useStore } from "@/core/store";
  22. import { computed, reactive, Ref, toRaw, watchEffect } from "vue";
  23. import { Transform } from "konva/lib/Util";
  24. import { sortFn } from "@/core/store/store";
  25. import { getLineIconEndpoints, getSnapLine } from "../line-icon";
  26. import { useDrawIngData } from "@/core/hook/use-draw";
  27. import { useTestPoints } from "@/core/hook/use-debugger";
  28. const minAngle = MathUtils.degToRad(0.1);
  29. const palAngle = MathUtils.degToRad(20);
  30. const getLineRect = (points: Pos[], strokeWidth: number) => {
  31. const v = lineVector(points);
  32. const vv = verticalVector(v);
  33. const offset = vv.clone().multiplyScalar(strokeWidth / 2);
  34. const top = points.map((p) => offset.clone().add(p));
  35. offset.multiplyScalar(-1);
  36. const bottom = points.map((p) => offset.clone().add(p));
  37. return [...top, bottom[1], bottom[0]];
  38. };
  39. export const useGetExtendPolygon = installGlobalVar(() => {
  40. return (data: LineData, line: LineData["lines"][0], useJoin = true) => {
  41. const getJoinInfo = (
  42. joinLine: LineData["lines"][0],
  43. joinPoints: Pos[],
  44. getNdx: number
  45. ) => {
  46. const jNdx = joinPoints.indexOf(linePoints[getNdx]);
  47. if ((getNdx === 0 && jNdx === 0) || (getNdx === 1 && jNdx === 1)) {
  48. joinPoints.reverse();
  49. }
  50. const direInv = getNdx === 0 && jNdx === 0;
  51. const joinv = lineVector(joinPoints).multiplyScalar(-1);
  52. const angle = vector2IncludedAngle(
  53. direInv ? joinv : linev,
  54. direInv ? linev : joinv
  55. );
  56. const checkAngle = rangMod(Math.abs(angle), Math.PI);
  57. if (checkAngle < minAngle || checkAngle > Math.PI - minAngle) return;
  58. const join = lineIntersection(linePoints, joinPoints);
  59. if (!join) return;
  60. const center = lineCenter([...linePoints, ...joinPoints]);
  61. const joinRect = getLineRect(joinPoints, joinLine.strokeWidth);
  62. const cheJoinInnerNdxs = getNdx === 0 ? [0, 3] : [1, 2];
  63. const cheLineInnerNdxs = getNdx === 0 ? [1, 2] : [0, 3];
  64. if (
  65. cheJoinInnerNdxs.some((ndx) =>
  66. isPolygonPointInner(lineRect, joinRect[ndx])
  67. ) ||
  68. cheLineInnerNdxs.some((ndx) =>
  69. isPolygonPointInner(joinRect, lineRect[ndx])
  70. )
  71. ) {
  72. return;
  73. }
  74. let outerLine1, innerLine1, outerLine2, innerLine2;
  75. const rectJust =
  76. lineLen(center, lineRect[0]) > lineLen(center, lineRect[3]);
  77. if (rectJust) {
  78. outerLine1 = [lineRect[0], lineRect[1]];
  79. innerLine1 = [lineRect[3], lineRect[2]];
  80. } else {
  81. outerLine1 = [lineRect[3], lineRect[2]];
  82. innerLine1 = [lineRect[0], lineRect[1]];
  83. }
  84. if (lineLen(center, joinRect[0]) > lineLen(center, joinRect[3])) {
  85. outerLine2 = [joinRect[0], joinRect[1]];
  86. innerLine2 = [joinRect[3], joinRect[2]];
  87. } else {
  88. outerLine2 = [joinRect[3], joinRect[2]];
  89. innerLine2 = [joinRect[0], joinRect[1]];
  90. }
  91. const outer = lineIntersection(outerLine1, outerLine2);
  92. if (!outer) return;
  93. let inside: Pos = lineIntersection(innerLine1, innerLine2)!;
  94. if (!inside) return;
  95. const insideLineInner1 = lineInner(innerLine1, inside);
  96. const insideLineInner2 = lineInner(innerLine2, inside);
  97. if (!insideLineInner1 && !insideLineInner2) return;
  98. let insides = [inside];
  99. // 如果角度过于尖锐则使用平行线
  100. let outers = [outer];
  101. if (checkAngle < palAngle) {
  102. const jov = getVectorLine(lineVerticalVector([join, outer]), join);
  103. const outer1 = lineIntersection(jov, outerLine1)!;
  104. outers = [outer1, join];
  105. }
  106. const repsResult: { rep: number; points: Pos[] }[] = [];
  107. if (getNdx === 0) {
  108. repsResult.push({
  109. rep: 0,
  110. points: rectJust ? outers.reverse() : insides,
  111. });
  112. repsResult.push({ rep: 3, points: rectJust ? insides : outers });
  113. } else {
  114. repsResult.push({ rep: 1, points: rectJust ? outers : insides });
  115. repsResult.push({
  116. rep: 2,
  117. points: rectJust ? insides : outers.reverse(),
  118. });
  119. }
  120. // testPoints.value.push(...insides, ...outers);
  121. return repsResult;
  122. };
  123. const linePoints = [line.a, line.b].map(
  124. (id) => data.points.find((item) => item.id === id)!
  125. );
  126. const lineRect: Pos[] = getLineRect(linePoints, line.strokeWidth);
  127. const polygon = [...lineRect];
  128. const linev = lineVector(linePoints);
  129. if (!useJoin) {
  130. return polygon;
  131. }
  132. linePoints.forEach((point, ndx) => {
  133. const joinLines = getJoinLine(data, line, point.id);
  134. if (joinLines.length !== 1) return;
  135. const repsResult = getJoinInfo(joinLines[0], joinLines[0].points, ndx);
  136. if (!repsResult) return;
  137. for (const rep of repsResult) {
  138. const ndx = polygon.indexOf(lineRect[rep.rep]);
  139. polygon.splice(ndx, 1, ...rep.points);
  140. }
  141. });
  142. return polygon;
  143. };
  144. });
  145. // 计算与icon相差的多边形
  146. export const useGetDiffIconPolygons = installGlobalVar(() => {
  147. const store = useStore();
  148. const iconPolygons = reactive({}) as { [key in string]: Pos[] };
  149. const icons = computed(() => store.getTypeItems("icon"));
  150. const line = computed(() => store.getTypeItems("line")[0]);
  151. const watchIcon = (id: string) => {
  152. const stopWatch = watchEffect(() => {
  153. const icon = icons.value.find((item) => item.id === id);
  154. if (!icon) {
  155. stopWatch();
  156. delete iconPolygons[id];
  157. return;
  158. }
  159. if (!line.value || sortFn(line.value, icon) > 0) {
  160. delete iconPolygons[id];
  161. return;
  162. }
  163. const rect = [
  164. { x: -icon.width / 2, y: -icon.height / 2 },
  165. { x: icon.width / 2, y: -icon.height / 2 },
  166. { x: icon.width / 2, y: icon.height / 2 },
  167. { x: -icon.width / 2, y: icon.height / 2 },
  168. ];
  169. const mat = new Transform(icon.mat);
  170. iconPolygons[id] = rect.map((p) => mat.point(p));
  171. });
  172. };
  173. const stopWatch = globalWatch(
  174. () => {
  175. return icons.value.map((item) => item.id);
  176. },
  177. (ids, oIds = []) => {
  178. const { added, deleted } = diffArrayChange(ids, oIds);
  179. deleted.forEach((id) => {
  180. delete iconPolygons[id];
  181. });
  182. added.forEach(watchIcon);
  183. },
  184. { immediate: true }
  185. );
  186. return {
  187. var: (polygon: Pos[]) => {
  188. const targets = Object.values(iconPolygons);
  189. if (!targets.length) return [polygon];
  190. return getDiffPolygons(polygon, targets);
  191. },
  192. onDestroy: stopWatch,
  193. };
  194. });
  195. // 计算与icon相差的多边形
  196. export const useGetDiffLineIconPolygons = (line: LineData["lines"][0], linePoints: Ref<Pos[]>) => {
  197. const store = useStore();
  198. const drawStore = useDrawIngData();
  199. const linevv = computed(() => verticalVector(lineVector(linePoints.value!)));
  200. const icons = computed(() => {
  201. const icons = store
  202. .getTypeItems("lineIcon")
  203. .concat(drawStore.lineIcon || []);
  204. return icons.filter((item) => !item.hide);
  205. });
  206. const lineIcons = computed(() =>
  207. icons.value.filter((icon) => icon.lineId === line.id)
  208. );
  209. const interSteps = computed(() => {
  210. const lineSteps = lineIcons.value
  211. .map((icon) => {
  212. const openSide = icon.openSide;
  213. const endLen = icon.endLen || 0.001;
  214. const startLen = icon.startLen || 0.001;
  215. const inv = endLen < startLen;
  216. return {
  217. lens: inv ? [endLen, startLen] : [startLen, endLen],
  218. openSide: inv ? (openSide === "LEFT" ? "RIGHT" : "LEFT") : openSide,
  219. };
  220. })
  221. .sort((line1, line2) => line1.lens[0] - line2.lens[0]);
  222. if (!lineSteps.length) return [];
  223. const interSteps: { lens: number[]; openSide: "LEFT" | "RIGHT" }[] = [];
  224. let i = 0;
  225. do {
  226. const startStep = lineSteps[i].lens[0];
  227. const openSide = lineSteps[i].openSide;
  228. let endStep = lineSteps[i].lens[1];
  229. for (i++; i < lineSteps.length; i++) {
  230. if (lineSteps[i].lens[0] <= endStep) {
  231. if (lineSteps[i].lens[1] > endStep) {
  232. endStep = lineSteps[i].lens[1];
  233. }
  234. } else {
  235. break;
  236. }
  237. }
  238. interSteps.push({
  239. lens: [startStep, endStep],
  240. openSide,
  241. });
  242. } while (i < lineSteps.length);
  243. return interSteps;
  244. });
  245. const interLines = computed(() =>
  246. interSteps.value.map((steps) => ({
  247. openSide: steps.openSide,
  248. points: getLineIconEndpoints(linePoints.value!, {
  249. startLen: steps.lens[0],
  250. endLen: steps.lens[1],
  251. }),
  252. }))
  253. );
  254. const stepLines = computed(() => {
  255. if (!linePoints.value?.length || !interLines.value.length) return [];
  256. const steps: Pos[][] = [];
  257. if (!eqPoint(linePoints.value[0], interLines.value[0].points[0])) {
  258. steps.push([linePoints.value[0], interLines.value[0].points[0]]);
  259. }
  260. let start = interLines.value[0].points[1];
  261. let i = 1;
  262. for (; i < interLines.value.length; i++) {
  263. const iLine = interLines.value[i];
  264. steps.push([start, iLine.points[0]]);
  265. start = iLine.points[1];
  266. }
  267. if (!eqPoint(start, linePoints.value[1])) {
  268. steps.push([start, linePoints.value[1]]);
  269. }
  270. return steps;
  271. });
  272. const subStepsLines = computed(() => {
  273. return interLines.value.map((il) => {
  274. return il.openSide === "RIGHT" ? il.points : [...il.points].reverse();
  275. });
  276. });
  277. const interPolygons = computed(() => {
  278. return interLines.value.map((il) => {
  279. const interLine = il.points;
  280. const topOffset = linevv.value.clone().multiplyScalar(line.strokeWidth);
  281. const botOffset = topOffset.clone().multiplyScalar(-1);
  282. return [
  283. topOffset.clone().add(interLine[0]),
  284. topOffset.clone().add(interLine[1]),
  285. botOffset.clone().add(interLine[1]),
  286. botOffset.clone().add(interLine[0]),
  287. ];
  288. });
  289. });
  290. return {
  291. diff: (polygon: Pos[]) => {
  292. const result = interPolygons.value.length
  293. ? getDiffPolygons(polygon, interPolygons.value)
  294. : [polygon];
  295. return result;
  296. },
  297. subSteps: subStepsLines,
  298. steps: stepLines,
  299. };
  300. };