|
@@ -13,10 +13,21 @@ import { useOperMode } from "@/core/hook/use-status";
|
|
|
import { installGlobalVar, useCursor } from "@/core/hook/use-global-vars";
|
|
|
import { useInteractiveDots } from "@/core/hook/use-interactive";
|
|
|
import { computed, reactive, ref, watch } from "vue";
|
|
|
-import { copy, mergeFuns } from "@/utils/shared";
|
|
|
-import { eqPoint, Pos } from "@/utils/math";
|
|
|
-import { getSnapInfos, type LineData } from "./";
|
|
|
+import { copy, mergeFuns, onlyId, rangMod } from "@/utils/shared";
|
|
|
+import {
|
|
|
+ eqPoint,
|
|
|
+ getVectorLine,
|
|
|
+ linePointProjection,
|
|
|
+ lineVector,
|
|
|
+ Pos,
|
|
|
+ vector2IncludedAngle,
|
|
|
+ verticalVector,
|
|
|
+ zeroEq,
|
|
|
+} from "@/utils/math";
|
|
|
+import { defaultStyle, getSnapInfos, type LineData } from "./";
|
|
|
import { useCustomSnapInfos } from "@/core/hook/use-snap";
|
|
|
+import { MathUtils, Vector2 } from "three";
|
|
|
+import { getBaseItem } from "../util";
|
|
|
|
|
|
type PayData = Pos;
|
|
|
|
|
@@ -65,19 +76,18 @@ export const useDraw = () => {
|
|
|
if (!drawItems[0]) return;
|
|
|
|
|
|
if (isTempDraw) {
|
|
|
- console.log(drawItems[0].points)
|
|
|
+ console.log(drawItems[0].points);
|
|
|
drawItems[0].lines.pop();
|
|
|
drawItems[0].points.pop();
|
|
|
drawItems[0].polygon.pop();
|
|
|
}
|
|
|
- const lastP = drawItems[0].points[drawItems[0].points.length - 1]
|
|
|
+ const lastP = drawItems[0].points[drawItems[0].points.length - 1];
|
|
|
if (lastP) {
|
|
|
- console.log(lastP)
|
|
|
- const ctx = getInitCtx()
|
|
|
- ctx.add.points[lastP.id] = lastP
|
|
|
- normalLineData(drawItems[0], ctx)
|
|
|
+ console.log(lastP);
|
|
|
+ const ctx = getInitCtx();
|
|
|
+ ctx.add.points[lastP.id] = lastP;
|
|
|
+ normalLineData(drawItems[0], ctx);
|
|
|
}
|
|
|
-
|
|
|
|
|
|
snapInfos?.forEach(customSnapInfos.remove);
|
|
|
drawSnapInfos?.forEach(customSnapInfos.remove);
|
|
@@ -289,9 +299,6 @@ export const useDraw = () => {
|
|
|
return drawItems;
|
|
|
};
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
export type NLineDataCtx = {
|
|
|
del: {
|
|
|
points: Record<string, LineData["points"][0]>;
|
|
@@ -321,22 +328,30 @@ export const getInitCtx = (): NLineDataCtx => ({
|
|
|
},
|
|
|
});
|
|
|
|
|
|
-export const repPointRef = (data: LineData, delId: string, repId: string) => {
|
|
|
+export const repPointRef = (data: LineData, delId: string, repId: string, queUpdate = true) => {
|
|
|
for (let i = 0; i < data.lines.length; i++) {
|
|
|
const line = data.lines[i];
|
|
|
if (line.a === delId) {
|
|
|
- data.lines[i] = { ...line, a: repId };
|
|
|
+ if (queUpdate) {
|
|
|
+ data.lines[i] = { ...line, a: repId };
|
|
|
+ } else {
|
|
|
+ data.lines[i].a = repId
|
|
|
+ }
|
|
|
}
|
|
|
if (line.b === delId) {
|
|
|
- data.lines[i] = { ...line, b: repId };
|
|
|
+ if (queUpdate) {
|
|
|
+ data.lines[i] = { ...line, b: repId };
|
|
|
+ } else {
|
|
|
+ data.lines[i].b = repId
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- return data
|
|
|
+ return data;
|
|
|
};
|
|
|
|
|
|
export const deduplicateLines = (data: LineData) => {
|
|
|
- const seen = new Map<string, LineData['lines'][0]>();
|
|
|
- let isChange = false
|
|
|
+ const seen = new Map<string, LineData["lines"][0]>();
|
|
|
+ let isChange = false;
|
|
|
for (const line of data.lines) {
|
|
|
if (line.a === line.b) continue;
|
|
|
// 生成标准化键:确保 (a,b) 和 (b,a) 被视为相同,并且 a === b 时也去重
|
|
@@ -350,17 +365,17 @@ export const deduplicateLines = (data: LineData) => {
|
|
|
// 如果存在重复键,覆盖旧值(保留尾部元素)
|
|
|
seen.delete(existingKey);
|
|
|
seen.set(key1, line); // 统一存储为 key1 格式
|
|
|
- isChange = true
|
|
|
+ isChange = true;
|
|
|
} else {
|
|
|
// 新记录,直接存储
|
|
|
seen.set(key1, line);
|
|
|
}
|
|
|
}
|
|
|
if (isChange) {
|
|
|
- data.lines = Array.from(seen.values())
|
|
|
+ data.lines = Array.from(seen.values());
|
|
|
}
|
|
|
|
|
|
- return data
|
|
|
+ return data;
|
|
|
};
|
|
|
|
|
|
export const normalLineData = (data: LineData, ctx: NLineDataCtx) => {
|
|
@@ -369,7 +384,6 @@ export const normalLineData = (data: LineData, ctx: NLineDataCtx) => {
|
|
|
...Object.values(ctx.update.points),
|
|
|
];
|
|
|
|
|
|
-
|
|
|
// 合并相同点
|
|
|
for (const p2 of changePoints) {
|
|
|
const ndx = data.points.findIndex((item) => item.id === p2.id);
|
|
@@ -387,22 +401,171 @@ export const normalLineData = (data: LineData, ctx: NLineDataCtx) => {
|
|
|
|
|
|
// 删除线a b 点一样的线段
|
|
|
for (let i = 0; i < data.lines.length; i++) {
|
|
|
- const line = data.lines[i]
|
|
|
+ const line = data.lines[i];
|
|
|
if (line.a === line.b) {
|
|
|
- data.lines.splice(i--, 1)
|
|
|
+ data.lines.splice(i--, 1);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
// 删除游离点
|
|
|
- const pointIds = Object.values(ctx.del.lines).flatMap(item => [item.a, item.b])
|
|
|
- pointIds.push(...Object.keys(ctx.add.points))
|
|
|
- const linePointIds = data.lines.flatMap(item => [item.a, item.b])
|
|
|
+ const pointIds = Object.values(ctx.del.lines).flatMap((item) => [
|
|
|
+ item.a,
|
|
|
+ item.b,
|
|
|
+ ]);
|
|
|
+ pointIds.push(...Object.keys(ctx.add.points));
|
|
|
+ const linePointIds = data.lines.flatMap((item) => [item.a, item.b]);
|
|
|
for (let id of pointIds) {
|
|
|
if (!linePointIds.includes(id)) {
|
|
|
- const ndx = data.points.findIndex(p => p.id === id)
|
|
|
- ~ndx && data.points.splice(ndx, 1)
|
|
|
+ const ndx = data.points.findIndex((p) => p.id === id);
|
|
|
+ ~ndx && data.points.splice(ndx, 1);
|
|
|
}
|
|
|
}
|
|
|
return deduplicateLines(data);
|
|
|
};
|
|
|
+
|
|
|
+export const genMoveLineHandler = (data: LineData, lineId: string, ctx = getInitCtx()) => {
|
|
|
+ const line = data.lines.find((line) => line.id === lineId)!;
|
|
|
+ const pointIds = [line.a, line.b];
|
|
|
+ const points = pointIds.map((id) => data.points.find((p) => p.id === id)!);
|
|
|
+ const angleRange = [MathUtils.degToRad(10), MathUtils.degToRad(170)];
|
|
|
+ const getJoinLine = (pId: string) =>
|
|
|
+ data.lines
|
|
|
+ .filter(
|
|
|
+ (item) =>
|
|
|
+ (item.a === pId || item.b === pId) &&
|
|
|
+ !(pointIds.includes(item.a) && pointIds.includes(item.b))
|
|
|
+ )
|
|
|
+ .map((line) => {
|
|
|
+ const pointIds = pId === line.a ? [line.a, line.b] : [line.b, line.a];
|
|
|
+ return {
|
|
|
+ id: line.id,
|
|
|
+ points: pointIds.map((id) => data.points.find((p) => p.id === id)!),
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ const getRefInfo = (moveDire: Vector2, ndx: number) => {
|
|
|
+ const joinLines = getJoinLine(pointIds[ndx]);
|
|
|
+ const joinLineDires: Vector2[] = [];
|
|
|
+ const line = [points[ndx], points[Number(!ndx)]];
|
|
|
+ const lineDire = lineVector(line);
|
|
|
+
|
|
|
+ let invAngle = Number.MAX_VALUE;
|
|
|
+ let invSelectLineId: string;
|
|
|
+ let invSelectLineDire: Vector2 | null = null;
|
|
|
+
|
|
|
+ let alongAngle = -Number.MAX_VALUE;
|
|
|
+ let alongSelectLineId: string;
|
|
|
+ let alongSelectLineDire: Vector2 | null = null;
|
|
|
+
|
|
|
+ for (const line of joinLines) {
|
|
|
+ const joinDire = lineVector(line.points);
|
|
|
+ joinLineDires.push(joinDire);
|
|
|
+
|
|
|
+ const angle = vector2IncludedAngle(lineDire, joinDire);
|
|
|
+ if (angle > 0) {
|
|
|
+ if (angle < invAngle) {
|
|
|
+ invAngle = angle;
|
|
|
+ invSelectLineId = line.id;
|
|
|
+ invSelectLineDire = joinDire;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (angle > alongAngle) {
|
|
|
+ alongAngle = angle;
|
|
|
+ alongSelectLineId = line.id;
|
|
|
+ alongSelectLineDire = joinDire;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!invSelectLineDire && !alongSelectLineDire) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let isAlong = !invSelectLineDire;
|
|
|
+ if (!isAlong && alongSelectLineDire) {
|
|
|
+ const invMoveAngle = Math.abs(
|
|
|
+ vector2IncludedAngle(moveDire, invSelectLineDire!)
|
|
|
+ );
|
|
|
+ const alongMoveAngle = Math.abs(
|
|
|
+ vector2IncludedAngle(moveDire, alongSelectLineDire!)
|
|
|
+ );
|
|
|
+ isAlong = alongMoveAngle! < invMoveAngle!;
|
|
|
+ }
|
|
|
+ let info = isAlong
|
|
|
+ ? {
|
|
|
+ lineDire,
|
|
|
+ selectLineDire: alongSelectLineDire!,
|
|
|
+ selectLineId: alongSelectLineId!,
|
|
|
+ angle: alongAngle!,
|
|
|
+ }
|
|
|
+ : {
|
|
|
+ lineDire,
|
|
|
+ selectLineDire: invSelectLineDire!,
|
|
|
+ selectLineId: invSelectLineId!,
|
|
|
+ angle: invAngle!,
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ info.angle = rangMod(info.angle, Math.PI)
|
|
|
+ const needVertical = info.angle > angleRange[1] || info.angle < angleRange[0];
|
|
|
+ const needSplit = needVertical || joinLineDires.some(
|
|
|
+ (dire) =>
|
|
|
+ dire !== info.selectLineDire &&
|
|
|
+ !zeroEq(
|
|
|
+ rangMod(vector2IncludedAngle(dire, info.selectLineDire), Math.PI)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ return { ...info, needSplit, needVertical }
|
|
|
+ };
|
|
|
+
|
|
|
+ let refInfos: ReturnType<typeof getRefInfo>[];
|
|
|
+ let snapLines: (null | Pos[])[]
|
|
|
+ let inited = false
|
|
|
+
|
|
|
+ const init = (moveDires: Vector2[]) => {
|
|
|
+ refInfos = [getRefInfo(moveDires[0], 0), getRefInfo(moveDires[0], 1)]
|
|
|
+ snapLines = []
|
|
|
+ for (let i = 0; i < refInfos.length; i++) {
|
|
|
+ const refInfo = refInfos[i]
|
|
|
+ if (!refInfo) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if (refInfo.needSplit) {
|
|
|
+ // 拆分点
|
|
|
+ const point = points[i]
|
|
|
+ const newPoint = { ...point, id: onlyId() }
|
|
|
+ data.points.push(newPoint)
|
|
|
+ repPointRef(data, point.id, newPoint.id, false)
|
|
|
+ const newLine = { ...getBaseItem(), ...defaultStyle, a: point.id, b: newPoint.id }
|
|
|
+ data.lines.push(newLine)
|
|
|
+ ctx.add.lines[newLine.id] = newLine
|
|
|
+ ctx.add.points[newPoint.id] = newPoint
|
|
|
+
|
|
|
+ if (i) {
|
|
|
+ line.b = point.id
|
|
|
+ } else {
|
|
|
+ line.a = point.id
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const dire = refInfo.needVertical ? verticalVector(refInfo.selectLineDire) : refInfo.selectLineDire
|
|
|
+ snapLines[i] = getVectorLine(dire, copy(points[i]), 10)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const handler = (finalPoss: Pos[]) => {
|
|
|
+ const moveDires = finalPoss.map((pos, ndx) => lineVector([points[ndx], pos]))
|
|
|
+ if (!inited) {
|
|
|
+ inited = true
|
|
|
+ init(moveDires)
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let i = 0; i < snapLines.length; i++) {
|
|
|
+ const snapLine = snapLines[i]
|
|
|
+ const fpos = !snapLine ? finalPoss[i] : linePointProjection(snapLine, finalPoss[i])
|
|
|
+ console.log(snapLines[i], fpos, i)
|
|
|
+ Object.assign(points[i], fpos)
|
|
|
+ ctx.update.points[points[i].id] = points[i]
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ return handler
|
|
|
+};
|