|
@@ -1,5 +1,10 @@
|
|
import {
|
|
import {
|
|
|
|
+ getVectorLine,
|
|
|
|
+ isPolygonPointInner,
|
|
|
|
+ lineCenter,
|
|
|
|
+ lineInner,
|
|
lineIntersection,
|
|
lineIntersection,
|
|
|
|
+ lineLen,
|
|
lineVector,
|
|
lineVector,
|
|
lineVerticalVector,
|
|
lineVerticalVector,
|
|
Pos,
|
|
Pos,
|
|
@@ -11,111 +16,137 @@ import { getJoinLine, getLinePoints } from "./attach-server";
|
|
import { MathUtils, Vector2 } from "three";
|
|
import { MathUtils, Vector2 } from "three";
|
|
import { computed, reactive, ref, Ref, watch, watchEffect } from "vue";
|
|
import { computed, reactive, ref, Ref, watch, watchEffect } from "vue";
|
|
import { flatPositions, mergeFuns, rangMod } from "@/utils/shared";
|
|
import { flatPositions, mergeFuns, rangMod } from "@/utils/shared";
|
|
|
|
+import { installGlobalVar } from "@/core/hook/use-global-vars";
|
|
|
|
+import { useTestPoints } from "@/core/hook/use-debugger";
|
|
|
|
|
|
-const eloCacle = new WeakMap<LineData, string[]>();
|
|
|
|
-export const extendLinesOverlap = (
|
|
|
|
- data: LineData,
|
|
|
|
- line1: LineData["lines"][0]
|
|
|
|
-) => {
|
|
|
|
- let cacle = eloCacle.get(data);
|
|
|
|
- if (!cacle) {
|
|
|
|
- eloCacle.set(data, (cacle = []));
|
|
|
|
- }
|
|
|
|
- const has = (ids: string[]) => {
|
|
|
|
- const key = [...ids].sort().join("---");
|
|
|
|
- return cacle.includes(key);
|
|
|
|
- };
|
|
|
|
- const set = (ids: string[]) => {
|
|
|
|
- cacle.push([...ids].sort().join("---"));
|
|
|
|
- };
|
|
|
|
|
|
+const minAngle = MathUtils.degToRad(0.1);
|
|
|
|
+const palAngle = MathUtils.degToRad(20);
|
|
|
|
|
|
- type ELine = LineData["lines"][0] & { points: LineData["points"] };
|
|
|
|
- const minAngle = MathUtils.degToRad(20);
|
|
|
|
- const getExtendPolygons = (line1: ELine, line2: ELine) => {
|
|
|
|
- const v1 = lineVector(line1.points);
|
|
|
|
- const v2 = lineVector(line2.points);
|
|
|
|
- const vv1 = verticalVector(v1);
|
|
|
|
- const vv2 = verticalVector(v2);
|
|
|
|
- const angle = vector2IncludedAngle(v1, v2);
|
|
|
|
- const checkAngle = Math.abs(rangMod(angle, Math.PI));
|
|
|
|
- if (checkAngle < minAngle || checkAngle > Math.PI - minAngle) return [];
|
|
|
|
- const mul = angle > 0 ? -1 : 1;
|
|
|
|
- const offset1 = vv1.multiplyScalar((line1.strokeWidth / 2) * mul);
|
|
|
|
- const offset2 = vv2.multiplyScalar((line2.strokeWidth / 2) * mul);
|
|
|
|
- const le1 = line1.points.map((p) => offset1.clone().add(p));
|
|
|
|
- const le2 = line2.points.map((p) => offset2.clone().add(p));
|
|
|
|
- const join = lineIntersection(le1, le2)!;
|
|
|
|
-
|
|
|
|
- return [
|
|
|
|
- {
|
|
|
|
- points: flatPositions([le1[1], line1.points[1], join]),
|
|
|
|
- fill: line1.stroke,
|
|
|
|
- strokeWidth: 0.05,
|
|
|
|
- stroke: line1.stroke,
|
|
|
|
- closed: true,
|
|
|
|
- listening: false,
|
|
|
|
- },
|
|
|
|
- {
|
|
|
|
- points: flatPositions([le2[0], line1.points[1], join]),
|
|
|
|
- fill: line2.stroke,
|
|
|
|
- strokeWidth: 0.05,
|
|
|
|
- stroke: line2.stroke,
|
|
|
|
- closed: true,
|
|
|
|
- listening: false,
|
|
|
|
- },
|
|
|
|
- ];
|
|
|
|
- };
|
|
|
|
|
|
+const getLineRect = (points: Pos[], strokeWidth: number) => {
|
|
|
|
+ const v = lineVector(points);
|
|
|
|
+ const vv = verticalVector(v);
|
|
|
|
+ const offset = vv.clone().multiplyScalar(strokeWidth / 2);
|
|
|
|
+ const top = points.map((p) => offset.clone().add(p));
|
|
|
|
+ offset.multiplyScalar(-1);
|
|
|
|
+ const bottom = points.map((p) => offset.clone().add(p));
|
|
|
|
+ return [...top, bottom[1], bottom[0]];
|
|
|
|
+};
|
|
|
|
|
|
- type ResultJoin = ReturnType<typeof getExtendPolygons>;
|
|
|
|
- const joins = ref<ResultJoin[]>([]);
|
|
|
|
|
|
+export const useGetExtendPolygon = installGlobalVar(() => {
|
|
|
|
+ const testPoints = useTestPoints();
|
|
|
|
|
|
- const updateJoins = (rev = false) => {
|
|
|
|
- const points = computed(() => {
|
|
|
|
- const pointIds = rev ? [line1.b, line1.a] : [line1.a, line1.b]
|
|
|
|
- return pointIds.map(
|
|
|
|
- (id) => data.points.find((item) => item.id === id)!
|
|
|
|
- )
|
|
|
|
|
|
+ return (data: LineData, line: LineData["lines"][0]) => {
|
|
|
|
+ const getConfig = (points: Pos[], stroke?: string) => ({
|
|
|
|
+ points: flatPositions(points),
|
|
|
|
+ fill: line.stroke,
|
|
|
|
+ closed: true,
|
|
|
|
+ listening: false,
|
|
});
|
|
});
|
|
|
|
+ const getJoinInfo = (
|
|
|
|
+ joinLine: LineData["lines"][0],
|
|
|
|
+ joinPoints: Pos[],
|
|
|
|
+ getNdx: number
|
|
|
|
+ ) => {
|
|
|
|
+ const jNdx = joinPoints.indexOf(linePoints[getNdx]);
|
|
|
|
+ if ((getNdx === 0 && jNdx === 0) || (getNdx === 1 && jNdx === 1)) {
|
|
|
|
+ joinPoints.reverse();
|
|
|
|
+ }
|
|
|
|
+ const direInv = getNdx === 0 && jNdx === 0;
|
|
|
|
+ const joinv = lineVector(joinPoints).multiplyScalar(-1);
|
|
|
|
+ const angle = vector2IncludedAngle(
|
|
|
|
+ direInv ? joinv : linev,
|
|
|
|
+ direInv ? linev : joinv
|
|
|
|
+ );
|
|
|
|
+ const checkAngle = rangMod(Math.abs(angle), Math.PI);
|
|
|
|
+ if (checkAngle < minAngle || checkAngle > Math.PI - minAngle) return;
|
|
|
|
+ const join = lineIntersection(linePoints, joinPoints);
|
|
|
|
+ if (!join) return;
|
|
|
|
+
|
|
|
|
+ const center = lineCenter([...linePoints, ...joinPoints]);
|
|
|
|
+ const joinRect = getLineRect(joinPoints, joinLine.strokeWidth);
|
|
|
|
+ const cheJoinInnerNdxs = getNdx === 0 ? [0, 3] : [1, 2];
|
|
|
|
+ const cheLineInnerNdxs = getNdx === 0 ? [1, 2] : [0, 3];
|
|
|
|
+ if (
|
|
|
|
+ cheJoinInnerNdxs.some((ndx) =>
|
|
|
|
+ isPolygonPointInner(lineRect, joinRect[ndx])
|
|
|
|
+ ) ||
|
|
|
|
+ cheLineInnerNdxs.some((ndx) =>
|
|
|
|
+ isPolygonPointInner(joinRect, lineRect[ndx])
|
|
|
|
+ )
|
|
|
|
+ ) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let outerLine1, innerLine1, outerLine2, innerLine2;
|
|
|
|
+ const rectJust =
|
|
|
|
+ lineLen(center, lineRect[0]) > lineLen(center, lineRect[3]);
|
|
|
|
+ if (rectJust) {
|
|
|
|
+ outerLine1 = [lineRect[0], lineRect[1]];
|
|
|
|
+ innerLine1 = [lineRect[3], lineRect[2]];
|
|
|
|
+ } else {
|
|
|
|
+ outerLine1 = [lineRect[3], lineRect[2]];
|
|
|
|
+ innerLine1 = [lineRect[0], lineRect[1]];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (lineLen(center, joinRect[0]) > lineLen(center, joinRect[3])) {
|
|
|
|
+ outerLine2 = [joinRect[0], joinRect[1]];
|
|
|
|
+ innerLine2 = [joinRect[3], joinRect[2]];
|
|
|
|
+ } else {
|
|
|
|
+ outerLine2 = [joinRect[3], joinRect[2]];
|
|
|
|
+ innerLine2 = [joinRect[0], joinRect[1]];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const outer = lineIntersection(outerLine1, outerLine2);
|
|
|
|
+ if (!outer) return;
|
|
|
|
|
|
- watch(
|
|
|
|
- () => getJoinLine(data, line1, points.value[1].id),
|
|
|
|
- (joinLines) => {
|
|
|
|
- for (let j = 0; j < joinLines.length; j++) {
|
|
|
|
- const key = [points.value[0].id, points.value[1].id, joinLines[j].points[1].id];
|
|
|
|
- if (has(key)) continue;
|
|
|
|
- set(key);
|
|
|
|
- watchEffect((onCleanup) => {
|
|
|
|
- const polygons = getExtendPolygons(
|
|
|
|
- { ...line1, points: points.value },
|
|
|
|
- joinLines[j]
|
|
|
|
- );
|
|
|
|
- joins.value.push(polygons);
|
|
|
|
- onCleanup(() => {
|
|
|
|
- const ndx = joins.value.indexOf(polygons);
|
|
|
|
- ~ndx && joins.value.splice(ndx, 1);
|
|
|
|
- });
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
- { immediate: true }
|
|
|
|
|
|
+ let inside: Pos = lineIntersection(innerLine1, innerLine2)!;
|
|
|
|
+ if (!inside) return;
|
|
|
|
+
|
|
|
|
+ const insideLineInner1 = lineInner(innerLine1, inside);
|
|
|
|
+ const insideLineInner2 = lineInner(innerLine2, inside);
|
|
|
|
+
|
|
|
|
+ if (!insideLineInner1 && !insideLineInner2) return;
|
|
|
|
+
|
|
|
|
+ let insides = [inside];
|
|
|
|
+ // 如果角度过于尖锐则使用平行线
|
|
|
|
+ let outers = [outer];
|
|
|
|
+ if (checkAngle < palAngle) {
|
|
|
|
+ const jov = getVectorLine(lineVerticalVector([join, outer]), join);
|
|
|
|
+ const outer1 = lineIntersection(jov, outerLine1)!;
|
|
|
|
+ outers = [outer1, join]
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const repsResult: { rep: number; points: Pos[] }[] = [];
|
|
|
|
+ if (getNdx === 0) {
|
|
|
|
+ repsResult.push({ rep: 0, points: rectJust ? outers.reverse() : insides });
|
|
|
|
+ repsResult.push({ rep: 3, points: rectJust ? insides : outers });
|
|
|
|
+ } else {
|
|
|
|
+ repsResult.push({ rep: 1, points: rectJust ? outers : insides });
|
|
|
|
+ repsResult.push({ rep: 2, points: rectJust ? insides : outers.reverse() });
|
|
|
|
+ }
|
|
|
|
+ // testPoints.value.push(...insides, ...outers);
|
|
|
|
+ return repsResult;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const linePoints = [line.a, line.b].map(
|
|
|
|
+ (id) => data.points.find((item) => item.id === id)!
|
|
);
|
|
);
|
|
- };
|
|
|
|
|
|
+ const lineRect: Pos[] = getLineRect(linePoints, line.strokeWidth);
|
|
|
|
+ const polygon = [...lineRect];
|
|
|
|
+ const linev = lineVector(linePoints);
|
|
|
|
|
|
- watchEffect((onCleanup) => {
|
|
|
|
- console.log(data.lines.indexOf(line1))
|
|
|
|
- onCleanup(() => 'has?')
|
|
|
|
- })
|
|
|
|
|
|
+ linePoints.forEach((point, ndx) => {
|
|
|
|
+ const joinLines = getJoinLine(data, line, point.id);
|
|
|
|
+ if (joinLines.length !== 1) return;
|
|
|
|
+ const repsResult = getJoinInfo(joinLines[0], joinLines[0].points, ndx);
|
|
|
|
+ if (!repsResult) return;
|
|
|
|
|
|
- const stopWatch = watch(
|
|
|
|
- () => [line1, line1.a, line1.b],
|
|
|
|
- () => {
|
|
|
|
- if (!data.lines.includes(line1)) {
|
|
|
|
- return stopWatch();
|
|
|
|
|
|
+ for (const rep of repsResult) {
|
|
|
|
+ const ndx = polygon.indexOf(lineRect[rep.rep]);
|
|
|
|
+ polygon.splice(ndx, 1, ...rep.points);
|
|
}
|
|
}
|
|
- updateJoins(false);
|
|
|
|
- updateJoins(true);
|
|
|
|
- },
|
|
|
|
- { immediate: true }
|
|
|
|
- );
|
|
|
|
- return joins;
|
|
|
|
-};
|
|
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ return getConfig(polygon);
|
|
|
|
+ };
|
|
|
|
+});
|