|
@@ -1,153 +1,263 @@
|
|
|
import {
|
|
|
eqPoint,
|
|
|
- getDiffPolygons,
|
|
|
+ getLEJJoinNdxs,
|
|
|
getLEJLineAngle,
|
|
|
getLineEdgeJoinInfo,
|
|
|
getLineEdges,
|
|
|
- getVectorLine,
|
|
|
- isPolygonPointInner,
|
|
|
LEJInfo,
|
|
|
LEJLine,
|
|
|
- line2IncludedAngle,
|
|
|
- lineCenter,
|
|
|
- lineInner,
|
|
|
- lineIntersection,
|
|
|
- lineLen,
|
|
|
lineVector,
|
|
|
- lineVerticalVector,
|
|
|
Pos,
|
|
|
- vector2IncludedAngle,
|
|
|
verticalVector,
|
|
|
} from "@/utils/math";
|
|
|
import { LineData, LineDataLine } from ".";
|
|
|
import { getJoinLine, getLinePoints } from "./attach-server";
|
|
|
import { MathUtils } from "three";
|
|
|
-import { diffArrayChange, rangMod } from "@/utils/shared";
|
|
|
+import { diffArrayChange, round } from "@/utils/shared";
|
|
|
import { globalWatch, installGlobalVar } from "@/core/hook/use-global-vars";
|
|
|
import { useStore } from "@/core/store";
|
|
|
-import { computed, reactive, Ref, toRaw, watchEffect } from "vue";
|
|
|
+import { computed, nextTick, reactive, Ref, watch, watchEffect } from "vue";
|
|
|
import { Transform } from "konva/lib/Util";
|
|
|
import { sortFn } from "@/core/store/store";
|
|
|
-import { getLineIconEndpoints, getSnapLine } from "../line-icon";
|
|
|
+import { getLineIconEndpoints } from "../line-icon";
|
|
|
import { useDrawIngData } from "@/core/hook/use-draw";
|
|
|
+import { polygonDifference, polygonDifferenceOnly } from "@/utils/math-clip";
|
|
|
|
|
|
-export const useGetExtendPolygon = installGlobalVar(() => {
|
|
|
+export const useGetExtendPolygon = (lineData: Ref<LineData | undefined>) => {
|
|
|
const minAngle = MathUtils.degToRad(0.1);
|
|
|
const palAngle = MathUtils.degToRad(20);
|
|
|
- const linePolygons: Record<string, Pos[]> = reactive({});
|
|
|
|
|
|
- const lineJoinInfos = reactive({}) as Record<
|
|
|
- string,
|
|
|
- Record<string, LEJInfo | undefined>
|
|
|
- >;
|
|
|
+ type JInfo = { lej: LEJInfo | undefined; diffPolygons?: Pos[][] };
|
|
|
+ const joinInfos = reactive({}) as Record<string, Record<string, JInfo>>;
|
|
|
|
|
|
const getInfoKey = (line: LEJLine) =>
|
|
|
- line.points.reduce((t, p) => p.x + p.y + t, "") + line.width;
|
|
|
+ line.points.reduce((t, p) => round(p.x, 3) + round(p.y, 3) + t, "") + line.width;
|
|
|
|
|
|
- const updateLine2JoinInfo = (
|
|
|
+ const setLEJInfo = (
|
|
|
data: LineData,
|
|
|
- line1: LineDataLine,
|
|
|
- line2: LineDataLine
|
|
|
+ originLine: LineDataLine,
|
|
|
+ targetLine: LineDataLine
|
|
|
) => {
|
|
|
+ const origin = {
|
|
|
+ points: getLinePoints(data, originLine),
|
|
|
+ width: originLine.strokeWidth,
|
|
|
+ };
|
|
|
+ const target = {
|
|
|
+ points: getLinePoints(data, targetLine),
|
|
|
+ width: targetLine.strokeWidth,
|
|
|
+ };
|
|
|
+ const { originNdx } = getLEJJoinNdxs(origin.points, target.points);
|
|
|
+ const lej = getLineEdgeJoinInfo(origin, target, minAngle, palAngle);
|
|
|
+ const originKey = getInfoKey(origin);
|
|
|
|
|
|
+ if (!(originKey in joinInfos)) {
|
|
|
+ joinInfos[originKey] = {};
|
|
|
+ }
|
|
|
+ if (!(originNdx in joinInfos[originKey])) {
|
|
|
+ joinInfos[originKey][originNdx] = { lej };
|
|
|
+ }
|
|
|
+ return joinInfos[originKey][originNdx];
|
|
|
};
|
|
|
|
|
|
- const getLineInfo = (data: LineData, line: LineDataLine) => {
|
|
|
+ const getLEJPolygon = (data: LineData, originLine: LineDataLine) => {
|
|
|
const origin = {
|
|
|
- points: getLinePoints(data, line),
|
|
|
- width: line.strokeWidth,
|
|
|
+ points: getLinePoints(data, originLine),
|
|
|
+ width: originLine.strokeWidth,
|
|
|
};
|
|
|
+ if (!origin.points[0] || !origin.points[1]) return [];
|
|
|
const key = getInfoKey(origin);
|
|
|
- if (!(key in lineJoinInfos)) {
|
|
|
- lineJoinInfos[key] = {};
|
|
|
- }
|
|
|
+ let originEdges: Pos[] = getLineEdges(origin.points, origin.width);
|
|
|
+ const initOriginEdges = [...originEdges];
|
|
|
|
|
|
- const cache = lineJoinInfos[key];
|
|
|
- const exist0 = "0" in cache;
|
|
|
- const exist1 = "1" in cache;
|
|
|
- if (exist0 && exist1) {
|
|
|
- return [cache[0], cache[1]];
|
|
|
+ const jInfos: (JInfo | undefined)[] = [
|
|
|
+ joinInfos[key]?.[0],
|
|
|
+ joinInfos[key]?.[1],
|
|
|
+ ];
|
|
|
+
|
|
|
+ if (!(key in joinInfos)) {
|
|
|
+ return originEdges;
|
|
|
+ }
|
|
|
+ for (const info of jInfos) {
|
|
|
+ if (!info?.lej) continue;
|
|
|
+ for (const rep of info.lej) {
|
|
|
+ const ndx = originEdges.indexOf(initOriginEdges[rep.rep]);
|
|
|
+ originEdges.splice(ndx, 1, ...rep.points);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- const calcNdxs: number[] = [];
|
|
|
- exist0 || calcNdxs.push(0);
|
|
|
- exist1 || calcNdxs.push(1);
|
|
|
+ for (const info of jInfos) {
|
|
|
+ if (!info?.diffPolygons) continue;
|
|
|
+ originEdges = polygonDifferenceOnly(originEdges, info.diffPolygons);
|
|
|
+ }
|
|
|
|
|
|
- const update2Line = (join: LineDataLine, originNdx: number) => {
|
|
|
- const target = {
|
|
|
- points: getLinePoints(data, join),
|
|
|
- width: join.strokeWidth,
|
|
|
- };
|
|
|
- const joinInfo = getLineEdgeJoinInfo(origin, target, minAngle, palAngle);
|
|
|
- cache[originNdx] = joinInfo?.origin;
|
|
|
+ return originEdges;
|
|
|
+ };
|
|
|
|
|
|
- const targetKey = getInfoKey(target);
|
|
|
- if (!(targetKey in lineJoinInfos)) {
|
|
|
- lineJoinInfos[targetKey] = {};
|
|
|
- }
|
|
|
- const p = origin.points[originNdx];
|
|
|
- lineJoinInfos[targetKey][target.points.indexOf(p)] = joinInfo?.target;
|
|
|
+ const setManyJoinInfo = (data: LineData, lines: LineDataLine[]) => {
|
|
|
+ type Select = ReturnType<typeof getLEJLineAngle> & {
|
|
|
+ origin: LineDataLine;
|
|
|
+ target: LineDataLine;
|
|
|
};
|
|
|
-
|
|
|
- const updateManyLine = (joins: LineDataLine[], originNdx: number) => {
|
|
|
- let maxAngle = minAngle;
|
|
|
- let select: ReturnType<typeof getLEJLineAngle> & {
|
|
|
- origin: LineDataLine;
|
|
|
- target: LineDataLine;
|
|
|
- };
|
|
|
- for (let i = 0; i < joins.length; i++) {
|
|
|
- const line1 = getLinePoints(data, joins[i]);
|
|
|
- for (let j = i + 1; j < joins.length; j++) {
|
|
|
- const line2 = getLinePoints(data, joins[j]);
|
|
|
- if (line2IncludedAngle(line1, line2)) {
|
|
|
- const ejlAngle = getLEJLineAngle(line1, line2);
|
|
|
- if (ejlAngle.angle > maxAngle) {
|
|
|
- maxAngle = ejlAngle.angle;
|
|
|
- select = {
|
|
|
- ...ejlAngle,
|
|
|
- origin: joins[i],
|
|
|
- target: joins[j],
|
|
|
- };
|
|
|
- }
|
|
|
+ const selectLEJLines = (lines: LineDataLine[]) => {
|
|
|
+ let select: Select;
|
|
|
+ let maxAngle = -999;
|
|
|
+ for (let i = 0; i < lines.length; i++) {
|
|
|
+ const line1 = getLinePoints(data, lines[i]);
|
|
|
+ for (let j = i + 1; j < lines.length; j++) {
|
|
|
+ const line2 = getLinePoints(data, lines[j]);
|
|
|
+ const ejlAngle = getLEJLineAngle(line1, line2);
|
|
|
+ if (ejlAngle.norAngle > maxAngle) {
|
|
|
+ maxAngle = ejlAngle.norAngle;
|
|
|
+ select = {
|
|
|
+ ...ejlAngle,
|
|
|
+ origin: lines[i],
|
|
|
+ target: lines[j],
|
|
|
+ };
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ return select!;
|
|
|
};
|
|
|
|
|
|
- for (const ndx of calcNdxs) {
|
|
|
- const p = origin.points[ndx];
|
|
|
- const joins = getJoinLine(data, line, p.id);
|
|
|
-
|
|
|
- if (joins.length === 0) {
|
|
|
- cache[ndx] = undefined;
|
|
|
- } else if (joins.length !== 1) {
|
|
|
- updateManyLine(joins, ndx);
|
|
|
+ let diffPolygons: Pos[][] = [];
|
|
|
+ const pointIds = lines.flatMap(l => [l.a, l.b])
|
|
|
+ const lineCount = lines.length
|
|
|
+ while (lines.length) {
|
|
|
+ if (lines.length > 1) {
|
|
|
+ const select = selectLEJLines(lines)!;
|
|
|
+ const origin = setLEJInfo(data, select.origin, select.target);
|
|
|
+ const target = setLEJInfo(data, select.target, select.origin);
|
|
|
+
|
|
|
+ lines = lines.filter(
|
|
|
+ (line) => line !== select.origin && line !== select.target
|
|
|
+ );
|
|
|
+ origin.diffPolygons = diffPolygons
|
|
|
+ target.diffPolygons = diffPolygons
|
|
|
+
|
|
|
+ diffPolygons = [
|
|
|
+ ...diffPolygons,
|
|
|
+ getLEJPolygon(data, select.origin),
|
|
|
+ getLEJPolygon(data, select.target),
|
|
|
+ ];
|
|
|
} else {
|
|
|
- update2Line(joins[0], ndx);
|
|
|
+ const key = getInfoKey({
|
|
|
+ points: getLinePoints(data, lines[0]),
|
|
|
+ width: lines[0].strokeWidth,
|
|
|
+ });
|
|
|
+ if (!(key in joinInfos)) {
|
|
|
+ joinInfos[key] = {};
|
|
|
+ }
|
|
|
+ const ndx = [lines[0].a, lines[0].b].findIndex(
|
|
|
+ (id) => pointIds.filter(pid => id === pid).length === lineCount
|
|
|
+ );
|
|
|
+ joinInfos[key][ndx] = { lej: undefined, diffPolygons };
|
|
|
+ lines = [];
|
|
|
}
|
|
|
}
|
|
|
- return [cache[0], cache[1]];
|
|
|
};
|
|
|
|
|
|
- return (data: LineData, line: LineDataLine, useJoin = true) => {
|
|
|
- const originEdges: Pos[] = getLineEdges(
|
|
|
- getLinePoints(data, line),
|
|
|
- line.strokeWidth
|
|
|
+ const genLEJLine = (lineData: LineData, line: LineDataLine) => ({
|
|
|
+ points: getLinePoints(lineData, line),
|
|
|
+ width: line.strokeWidth,
|
|
|
+ });
|
|
|
+
|
|
|
+ const init = (data: LineData) => {
|
|
|
+ const watchLine = (line: LineDataLine) => {
|
|
|
+ const joina = computed(() => getJoinLine(data, line, line.a));
|
|
|
+ const joinb = computed(() => getJoinLine(data, line, line.b));
|
|
|
+ const self = computed(() => genLEJLine(data, line));
|
|
|
+ const getWatchKey = () => {
|
|
|
+ const lines = [
|
|
|
+ ...joina.value.map((l) => genLEJLine(data, l)),
|
|
|
+ ...joinb.value.map((l) => genLEJLine(data, l)),
|
|
|
+ self.value,
|
|
|
+ ];
|
|
|
+ if (lines.some((l) => !l.points[0] || !l.points[1])) {
|
|
|
+ return null;
|
|
|
+ } else {
|
|
|
+ return lines.map(getInfoKey).join(",");
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ return watch(
|
|
|
+ getWatchKey,
|
|
|
+ (wkey, _2, onCleanup) => {
|
|
|
+ if (!wkey) return;
|
|
|
+ const key = getInfoKey(self.value);
|
|
|
+ const calcNdxs: number[] = [];
|
|
|
+
|
|
|
+ if (!(key in joinInfos)) {
|
|
|
+ joinInfos[key] = {};
|
|
|
+ calcNdxs.push(0, 1);
|
|
|
+ } else {
|
|
|
+ "0" in joinInfos[key] || calcNdxs.push(0);
|
|
|
+ "1" in joinInfos[key] || calcNdxs.push(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (const ndx of calcNdxs) {
|
|
|
+ const joins = ndx === 0 ? joina.value : joinb.value;
|
|
|
+ if (joins.length === 0) {
|
|
|
+ joinInfos[key][ndx] = { lej: undefined };
|
|
|
+ } else if (joins.length !== 1) {
|
|
|
+ setManyJoinInfo(data, [line, ...joins]);
|
|
|
+ } else {
|
|
|
+ setLEJInfo(data, line, joins[0]);
|
|
|
+ setLEJInfo(data, joins[0], line);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ onCleanup(() => {
|
|
|
+ delete joinInfos[key];
|
|
|
+ });
|
|
|
+ },
|
|
|
+ { immediate: true, flush: 'post' }
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ let isStop = false;
|
|
|
+ const stopMap = new WeakMap<LineDataLine, () => void>();
|
|
|
+ const stopWatch = watch(
|
|
|
+ () => [...data.lines],
|
|
|
+ async (newLines, oldLines = []) => {
|
|
|
+ const { added, deleted } = diffArrayChange(newLines, oldLines);
|
|
|
+ deleted.forEach((line) => {
|
|
|
+ const fn = stopMap.get(line);
|
|
|
+ fn && fn();
|
|
|
+ });
|
|
|
+ await nextTick();
|
|
|
+ if (!isStop) {
|
|
|
+ added.forEach((line) => {
|
|
|
+ stopMap.set(line, watchLine(line));
|
|
|
+ });
|
|
|
+ }
|
|
|
+ deleted;
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
);
|
|
|
- const initOriginEdges = [...originEdges];
|
|
|
- if (!useJoin) {
|
|
|
- return originEdges;
|
|
|
- }
|
|
|
- getLineInfo(data, line).forEach((info) => {
|
|
|
- if (!info) return;
|
|
|
- for (const rep of info) {
|
|
|
- const ndx = originEdges.indexOf(initOriginEdges[rep.rep]);
|
|
|
- originEdges.splice(ndx, 1, ...rep.points);
|
|
|
- }
|
|
|
- });
|
|
|
- return originEdges;
|
|
|
+
|
|
|
+ return () => {
|
|
|
+ isStop = true;
|
|
|
+ stopWatch();
|
|
|
+ data.lines.forEach((line) => {
|
|
|
+ const fn = stopMap.get(line);
|
|
|
+ fn && fn();
|
|
|
+ });
|
|
|
+ };
|
|
|
};
|
|
|
-});
|
|
|
+
|
|
|
+ watch(
|
|
|
+ lineData,
|
|
|
+ (data, _, onCleanup) => {
|
|
|
+ data && onCleanup(init(data));
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+ );
|
|
|
+
|
|
|
+ return (line: LineDataLine) => {
|
|
|
+ const polygon = lineData.value ? getLEJPolygon(lineData.value, line) : [];
|
|
|
+ return polygon
|
|
|
+ };
|
|
|
+};
|
|
|
|
|
|
// 计算与icon相差的多边形
|
|
|
export const useGetDiffIconPolygons = installGlobalVar(() => {
|
|
@@ -197,7 +307,7 @@ export const useGetDiffIconPolygons = installGlobalVar(() => {
|
|
|
var: (polygon: Pos[]) => {
|
|
|
const targets = Object.values(iconPolygons);
|
|
|
if (!targets.length) return [polygon];
|
|
|
- return getDiffPolygons(polygon, targets);
|
|
|
+ return polygonDifference(polygon, targets);
|
|
|
},
|
|
|
onDestroy: stopWatch,
|
|
|
};
|
|
@@ -316,7 +426,7 @@ export const useGetDiffLineIconPolygons = (
|
|
|
return {
|
|
|
diff: (polygon: Pos[]) => {
|
|
|
const result = interPolygons.value.length
|
|
|
- ? getDiffPolygons(polygon, interPolygons.value)
|
|
|
+ ? polygonDifference(polygon, interPolygons.value)
|
|
|
: [polygon];
|
|
|
|
|
|
return result;
|