attach-view.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. import {
  2. eqPoint,
  3. getLEJJoinNdxs,
  4. getLEJLineAngle,
  5. getLineEdgeJoinInfo,
  6. getLineEdges,
  7. LEJInfo,
  8. LEJLine,
  9. lineVector,
  10. Pos,
  11. verticalVector,
  12. } from "@/utils/math";
  13. import { LineData, LineDataLine } from ".";
  14. import { getJoinLine, getLinePoints } from "./attach-server";
  15. import { MathUtils } from "three";
  16. import { diffArrayChange, round } from "@/utils/shared";
  17. import { globalWatch, installGlobalVar } from "@/core/hook/use-global-vars";
  18. import { useStore } from "@/core/store";
  19. import { computed, nextTick, reactive, Ref, watch, watchEffect } from "vue";
  20. import { Transform } from "konva/lib/Util";
  21. import { sortFn } from "@/core/store/store";
  22. import { getLineIconEndpoints } from "../line-icon";
  23. import { useDrawIngData } from "@/core/hook/use-draw";
  24. import { polygonDifference, polygonDifferenceOnly } from "@/utils/math-clip";
  25. export const useGetExtendPolygon = (lineData: Ref<LineData | undefined>) => {
  26. const minAngle = MathUtils.degToRad(0.1);
  27. const palAngle = MathUtils.degToRad(20);
  28. type JInfo = { lej: LEJInfo | undefined; diffPolygons?: Pos[][] };
  29. const joinInfos = reactive({}) as Record<string, Record<string, JInfo>>;
  30. const getInfoKey = (line: LEJLine) =>
  31. line.points.reduce((t, p) => round(p.x, 3) + round(p.y, 3) + t, "") + line.width;
  32. const setLEJInfo = (
  33. data: LineData,
  34. originLine: LineDataLine,
  35. targetLine: LineDataLine
  36. ) => {
  37. const origin = {
  38. points: getLinePoints(data, originLine),
  39. width: originLine.strokeWidth,
  40. };
  41. const target = {
  42. points: getLinePoints(data, targetLine),
  43. width: targetLine.strokeWidth,
  44. };
  45. const { originNdx } = getLEJJoinNdxs(origin.points, target.points);
  46. const lej = getLineEdgeJoinInfo(origin, target, minAngle, palAngle);
  47. const originKey = getInfoKey(origin);
  48. if (!(originKey in joinInfos)) {
  49. joinInfos[originKey] = {};
  50. }
  51. if (!(originNdx in joinInfos[originKey])) {
  52. joinInfos[originKey][originNdx] = { lej };
  53. }
  54. return joinInfos[originKey][originNdx];
  55. };
  56. const getLEJPolygon = (data: LineData, originLine: LineDataLine) => {
  57. const origin = {
  58. points: getLinePoints(data, originLine),
  59. width: originLine.strokeWidth,
  60. };
  61. if (!origin.points[0] || !origin.points[1]) return [];
  62. const key = getInfoKey(origin);
  63. let originEdges: Pos[] = getLineEdges(origin.points, origin.width);
  64. const initOriginEdges = [...originEdges];
  65. const jInfos: (JInfo | undefined)[] = [
  66. joinInfos[key]?.[0],
  67. joinInfos[key]?.[1],
  68. ];
  69. if (!(key in joinInfos)) {
  70. return originEdges;
  71. }
  72. for (const info of jInfos) {
  73. if (!info?.lej) continue;
  74. for (const rep of info.lej) {
  75. const ndx = originEdges.indexOf(initOriginEdges[rep.rep]);
  76. originEdges.splice(ndx, 1, ...rep.points);
  77. }
  78. }
  79. for (const info of jInfos) {
  80. if (!info?.diffPolygons) continue;
  81. originEdges = polygonDifferenceOnly(originEdges, info.diffPolygons);
  82. }
  83. return originEdges;
  84. };
  85. const setManyJoinInfo = (data: LineData, lines: LineDataLine[]) => {
  86. type Select = ReturnType<typeof getLEJLineAngle> & {
  87. origin: LineDataLine;
  88. target: LineDataLine;
  89. };
  90. const selectLEJLines = (lines: LineDataLine[]) => {
  91. let select: Select;
  92. let maxAngle = -999;
  93. for (let i = 0; i < lines.length; i++) {
  94. const line1 = getLinePoints(data, lines[i]);
  95. for (let j = i + 1; j < lines.length; j++) {
  96. const line2 = getLinePoints(data, lines[j]);
  97. const ejlAngle = getLEJLineAngle(line1, line2);
  98. if (ejlAngle.norAngle > maxAngle) {
  99. maxAngle = ejlAngle.norAngle;
  100. select = {
  101. ...ejlAngle,
  102. origin: lines[i],
  103. target: lines[j],
  104. };
  105. }
  106. }
  107. }
  108. return select!;
  109. };
  110. let diffPolygons: Pos[][] = [];
  111. const pointIds = lines.flatMap(l => [l.a, l.b])
  112. const lineCount = lines.length
  113. while (lines.length) {
  114. if (lines.length > 1) {
  115. const select = selectLEJLines(lines)!;
  116. const origin = setLEJInfo(data, select.origin, select.target);
  117. const target = setLEJInfo(data, select.target, select.origin);
  118. lines = lines.filter(
  119. (line) => line !== select.origin && line !== select.target
  120. );
  121. origin.diffPolygons = diffPolygons
  122. target.diffPolygons = diffPolygons
  123. diffPolygons = [
  124. ...diffPolygons,
  125. getLEJPolygon(data, select.origin),
  126. getLEJPolygon(data, select.target),
  127. ];
  128. } else {
  129. const key = getInfoKey({
  130. points: getLinePoints(data, lines[0]),
  131. width: lines[0].strokeWidth,
  132. });
  133. if (!(key in joinInfos)) {
  134. joinInfos[key] = {};
  135. }
  136. const ndx = [lines[0].a, lines[0].b].findIndex(
  137. (id) => pointIds.filter(pid => id === pid).length === lineCount
  138. );
  139. joinInfos[key][ndx] = { lej: undefined, diffPolygons };
  140. lines = [];
  141. }
  142. }
  143. };
  144. const genLEJLine = (lineData: LineData, line: LineDataLine) => ({
  145. points: getLinePoints(lineData, line),
  146. width: line.strokeWidth,
  147. });
  148. const init = (data: LineData) => {
  149. const watchLine = (line: LineDataLine) => {
  150. const joina = computed(() => getJoinLine(data, line, line.a));
  151. const joinb = computed(() => getJoinLine(data, line, line.b));
  152. const self = computed(() => genLEJLine(data, line));
  153. const getWatchKey = () => {
  154. const lines = [
  155. ...joina.value.map((l) => genLEJLine(data, l)),
  156. ...joinb.value.map((l) => genLEJLine(data, l)),
  157. self.value,
  158. ];
  159. if (lines.some((l) => !l.points[0] || !l.points[1])) {
  160. return null;
  161. } else {
  162. return lines.map(getInfoKey).join(",");
  163. }
  164. };
  165. return watch(
  166. getWatchKey,
  167. (wkey, _2, onCleanup) => {
  168. if (!wkey) return;
  169. const key = getInfoKey(self.value);
  170. const calcNdxs: number[] = [];
  171. if (!(key in joinInfos)) {
  172. joinInfos[key] = {};
  173. calcNdxs.push(0, 1);
  174. } else {
  175. "0" in joinInfos[key] || calcNdxs.push(0);
  176. "1" in joinInfos[key] || calcNdxs.push(1);
  177. }
  178. for (const ndx of calcNdxs) {
  179. const joins = ndx === 0 ? joina.value : joinb.value;
  180. if (joins.length === 0) {
  181. joinInfos[key][ndx] = { lej: undefined };
  182. } else if (joins.length !== 1) {
  183. setManyJoinInfo(data, [line, ...joins]);
  184. } else {
  185. setLEJInfo(data, line, joins[0]);
  186. setLEJInfo(data, joins[0], line);
  187. }
  188. }
  189. onCleanup(() => {
  190. delete joinInfos[key];
  191. });
  192. },
  193. { immediate: true, flush: 'post' }
  194. );
  195. };
  196. let isStop = false;
  197. const stopMap = new WeakMap<LineDataLine, () => void>();
  198. const stopWatch = watch(
  199. () => [...data.lines],
  200. async (newLines, oldLines = []) => {
  201. const { added, deleted } = diffArrayChange(newLines, oldLines);
  202. deleted.forEach((line) => {
  203. const fn = stopMap.get(line);
  204. fn && fn();
  205. });
  206. await nextTick();
  207. if (!isStop) {
  208. added.forEach((line) => {
  209. stopMap.set(line, watchLine(line));
  210. });
  211. }
  212. deleted;
  213. },
  214. { immediate: true }
  215. );
  216. return () => {
  217. isStop = true;
  218. stopWatch();
  219. data.lines.forEach((line) => {
  220. const fn = stopMap.get(line);
  221. fn && fn();
  222. });
  223. };
  224. };
  225. watch(
  226. lineData,
  227. (data, _, onCleanup) => {
  228. data && onCleanup(init(data));
  229. },
  230. { immediate: true }
  231. );
  232. return (line: LineDataLine) => {
  233. const polygon = lineData.value ? getLEJPolygon(lineData.value, line) : [];
  234. return polygon
  235. };
  236. };
  237. // 计算与icon相差的多边形
  238. export const useGetDiffIconPolygons = installGlobalVar(() => {
  239. const store = useStore();
  240. const iconPolygons = reactive({}) as { [key in string]: Pos[] };
  241. const icons = computed(() => store.getTypeItems("icon"));
  242. const line = computed(() => store.getTypeItems("line")[0]);
  243. const watchIcon = (id: string) => {
  244. const stopWatch = watchEffect(() => {
  245. const icon = icons.value.find((item) => item.id === id);
  246. if (!icon) {
  247. stopWatch();
  248. delete iconPolygons[id];
  249. return;
  250. }
  251. if (!line.value || sortFn(line.value, icon) > 0) {
  252. delete iconPolygons[id];
  253. return;
  254. }
  255. const rect = [
  256. { x: -icon.width / 2, y: -icon.height / 2 },
  257. { x: icon.width / 2, y: -icon.height / 2 },
  258. { x: icon.width / 2, y: icon.height / 2 },
  259. { x: -icon.width / 2, y: icon.height / 2 },
  260. ];
  261. const mat = new Transform(icon.mat);
  262. iconPolygons[id] = rect.map((p) => mat.point(p));
  263. });
  264. };
  265. const stopWatch = globalWatch(
  266. () => {
  267. return icons.value.map((item) => item.id);
  268. },
  269. (ids, oIds = []) => {
  270. const { added, deleted } = diffArrayChange(ids, oIds);
  271. deleted.forEach((id) => {
  272. delete iconPolygons[id];
  273. });
  274. added.forEach(watchIcon);
  275. },
  276. { immediate: true }
  277. );
  278. return {
  279. var: (polygon: Pos[]) => {
  280. const targets = Object.values(iconPolygons);
  281. if (!targets.length) return [polygon];
  282. return polygonDifference(polygon, targets);
  283. },
  284. onDestroy: stopWatch,
  285. };
  286. });
  287. // 计算与icon相差的多边形
  288. export const useGetDiffLineIconPolygons = (
  289. line: LineDataLine,
  290. linePoints: Ref<Pos[]>
  291. ) => {
  292. const store = useStore();
  293. const drawStore = useDrawIngData();
  294. const linevv = computed(() => verticalVector(lineVector(linePoints.value!)));
  295. const icons = computed(() => {
  296. const icons = store
  297. .getTypeItems("lineIcon")
  298. .concat(drawStore.lineIcon || []);
  299. return icons.filter((item) => !item.hide);
  300. });
  301. const lineIcons = computed(() =>
  302. icons.value.filter((icon) => icon.lineId === line.id)
  303. );
  304. const interSteps = computed(() => {
  305. const lineSteps = lineIcons.value
  306. .map((icon) => {
  307. const openSide = icon.openSide;
  308. const endLen = icon.endLen || 0.001;
  309. const startLen = icon.startLen || 0.001;
  310. const inv = endLen < startLen;
  311. return {
  312. lens: inv ? [endLen, startLen] : [startLen, endLen],
  313. openSide: inv ? (openSide === "LEFT" ? "RIGHT" : "LEFT") : openSide,
  314. };
  315. })
  316. .sort((line1, line2) => line1.lens[0] - line2.lens[0]);
  317. if (!lineSteps.length) return [];
  318. const interSteps: { lens: number[]; openSide: "LEFT" | "RIGHT" }[] = [];
  319. let i = 0;
  320. do {
  321. const startStep = lineSteps[i].lens[0];
  322. const openSide = lineSteps[i].openSide;
  323. let endStep = lineSteps[i].lens[1];
  324. for (i++; i < lineSteps.length; i++) {
  325. if (lineSteps[i].lens[0] <= endStep) {
  326. if (lineSteps[i].lens[1] > endStep) {
  327. endStep = lineSteps[i].lens[1];
  328. }
  329. } else {
  330. break;
  331. }
  332. }
  333. interSteps.push({
  334. lens: [startStep, endStep],
  335. openSide,
  336. });
  337. } while (i < lineSteps.length);
  338. return interSteps;
  339. });
  340. const interLines = computed(() =>
  341. interSteps.value.map((steps) => ({
  342. openSide: steps.openSide,
  343. points: getLineIconEndpoints(linePoints.value!, {
  344. startLen: steps.lens[0],
  345. endLen: steps.lens[1],
  346. }),
  347. }))
  348. );
  349. const stepLines = computed(() => {
  350. if (!linePoints.value?.length || !interLines.value.length) return [];
  351. const steps: Pos[][] = [];
  352. if (!eqPoint(linePoints.value[0], interLines.value[0].points[0])) {
  353. steps.push([linePoints.value[0], interLines.value[0].points[0]]);
  354. }
  355. let start = interLines.value[0].points[1];
  356. let i = 1;
  357. for (; i < interLines.value.length; i++) {
  358. const iLine = interLines.value[i];
  359. steps.push([start, iLine.points[0]]);
  360. start = iLine.points[1];
  361. }
  362. if (!eqPoint(start, linePoints.value[1])) {
  363. steps.push([start, linePoints.value[1]]);
  364. }
  365. return steps;
  366. });
  367. const subStepsLines = computed(() => {
  368. return interLines.value.map((il) => {
  369. return il.openSide === "RIGHT" ? il.points : [...il.points].reverse();
  370. });
  371. });
  372. const interPolygons = computed(() => {
  373. return interLines.value.map((il) => {
  374. const interLine = il.points;
  375. const topOffset = linevv.value.clone().multiplyScalar(line.strokeWidth);
  376. const botOffset = topOffset.clone().multiplyScalar(-1);
  377. return [
  378. topOffset.clone().add(interLine[0]),
  379. topOffset.clone().add(interLine[1]),
  380. botOffset.clone().add(interLine[1]),
  381. botOffset.clone().add(interLine[0]),
  382. ];
  383. });
  384. });
  385. return {
  386. diff: (polygon: Pos[]) => {
  387. const result = interPolygons.value.length
  388. ? polygonDifference(polygon, interPolygons.value)
  389. : [polygon];
  390. return result;
  391. },
  392. subSteps: subStepsLines,
  393. steps: stepLines,
  394. };
  395. };