view.ts 12 KB

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