import { lineVector, Pos, vectorAngle, verticalVector } from "@/utils/math.ts"; import { BaseItem, generateSnapInfos, getBaseItem } from "../util.ts"; import { getMouseColors } from "@/utils/colors.ts"; import { InteractiveFix, InteractiveTo, MatResponseProps } from "../index.ts"; import { inRevise, onlyId, rangMod } from "@/utils/shared.ts"; import { MathUtils } from "three"; import { DrawStore, useStore } from "@/core/store/index.ts"; import { getInitCtx, NLineDataCtx, normalLineData } from "./use-draw.ts"; import { SelectionManageBus, UseGetSelectionManage } from "@/core/hook/use-selection.ts"; import { EntityShape } from "@/deconstruction.js"; import mitt from "mitt"; import { watch } from "vue"; export { default as Component } from "./line.vue"; export { default as TempComponent } from "./temp-line.vue"; export { useDraw } from "./use-draw.ts"; export const shapeName = "线段"; export const defaultStyle = { stroke: "#000000", strokeWidth: 20, dash: [30, 0], }; export const addMode = "single-dots"; export const getMouseStyle = (data: LineData) => { const strokeStatus = getMouseColors(data.stroke || defaultStyle.stroke); const strokeWidth = data.strokeWidth || defaultStyle.strokeWidth; return { default: { stroke: data.stroke || defaultStyle.stroke, strokeWidth }, hover: { stroke: strokeStatus.hover }, select: { stroke: strokeStatus.select }, focus: { stroke: strokeStatus.hover }, press: { stroke: strokeStatus.press }, }; }; export const getSnapInfos = (data: LineData) => { const vh = generateSnapInfos(getSnapPoints(data), true, false, true); data.lines.forEach((item) => { const a = data.points.find((p) => p.id === item.a)!; const b = data.points.find((p) => p.id === item.b)!; const prevVector = lineVector([a, b]); const vLine = verticalVector(prevVector); vh.push({ point: a, links: [b], linkDirections: [prevVector], linkAngle: [rangMod(MathUtils.radToDeg(vectorAngle(vLine)), 180)], }); }); return vh; }; export const getSnapPoints = (data: LineData) => { return data.points; }; export type LineData = Partial & BaseItem & { points: (Pos & { id: string })[]; lines: { id: string; a: string; b: string; strokeWidth: number; stroke: string; dash: number[]; }[]; polygon: { points: string[]; id: string }[]; updateTime?: number; calcTime?: number; }; export const interactiveToData: InteractiveTo<"line"> = ({ info, preset = {}, ...args }) => { if (info.cur) { const baseItem = getBaseItem(); return interactiveFixData({ ...args, info, data: { ...defaultStyle, ...baseItem, ...preset, lines: [], points: [], polygon: [], }, }); } }; export const interactiveFixData: InteractiveFix<"line"> = ({ data, info }) => { const nv = [...info.consumed, info.cur!]; data.points.length = nv.length; for (let i = 0; i < nv.length; i++) { if (inRevise(data.points[i], nv[i])) { if (!data.points[i]) { data.points[i] = { id: onlyId(), ...nv[i], }; } else { data.points[i] = { ...data.points[i], ...nv[i], }; } } } data.lines.length = nv.length - 1; for (let i = 0; i < nv.length - 1; i++) { if (!data.lines[i]) { data.lines[i] = { id: onlyId(), ...defaultStyle, a: data.points[i].id, b: data.points[i + 1].id, }; } } // data.polygon = [{points: [data.lines.map((item) => item.id)], id: onlyId()}]; return data; }; const matResPoints = new Set() let matCtx: NLineDataCtx | null let matData: LineData export const startMatResponse = () => { matCtx = getInitCtx() } export const matResponse = ({ data, mat, operId }: MatResponseProps<"line">) => { matData = data const line = data.lines.find(item => item.id === operId) if (!line) return; const ids = [line.a, line.b] for (const id of ids) { if (matResPoints.has(id)) { continue; } const ndx = data.points.findIndex(item => item.id === id) if (~ndx) { const point = data.points[ndx] data.points[ndx] = { ...point, ...mat.point(point) } matCtx!.update.points[point.id] = data.points[ndx] matResPoints.add(id) } } return data; }; export const endMatResponse = () => { matResPoints.clear() // matCtx && normalLineData(matData, matCtx) // console.log(matData, matCtx) matCtx = null } export const getPredefine = (key: keyof LineData) => { if (key === "strokeWidth") { return { proportion: true }; } }; export const childrenDataGetter = (data: LineData, id: string) => { const line = data.lines.find(item => item.id === id) if (!line) return; const ids = [line.a, line.b] return data.points.filter(p => ids.includes(p.id)) } export const delItem = (store: DrawStore, data: LineData, childId: string) => { if (!childId) { store.delItem("line", data.id); return; } let ndx; if (~(ndx = data.lines.findIndex((item) => item.id === childId))) { const delLine = data.lines[ndx]; const ctx = getInitCtx(); ctx.del.lines[delLine.id] = delLine; data.lines.splice(ndx, 1); normalLineData(data, ctx); store.setItem("line", { value: data, id: data.id }); } else if (~(ndx = data.points.findIndex((item) => item.id === childId))) { const { ctx } = delPoint(data, childId); normalLineData(data, ctx); store.setItem("line", { value: data, id: data.id }); } }; export const delPoint = (data: LineData, id: string, ctx = getInitCtx()) => { const p = data.points.find((item) => item.id === id); if (!p) return { data, ctx }; const checkLines = data.lines.filter( (item) => item.a === p.id || item.b === p.id ); if (checkLines.length > 1) { const joinPoints = new Set(); checkLines.forEach((item) => { joinPoints.add(item.a); joinPoints.add(item.b); }); if (joinPoints.size === 3) { const prev = checkLines.find((item) => item.b === p.id); const next = checkLines.find((item) => item.a === p.id); if (prev && next) { const l = { ...prev, id: onlyId(), b: next.b }; ctx.add.lines[l.id] = l; data.lines.push(l); } else { const l = prev || next || checkLines[0]; const ps = [...joinPoints].filter((item) => item !== p.id); const nl = { ...l, id: onlyId(), a: ps[0], b: ps[1] }; ctx.add.lines[l.id] = nl; data.lines.push(nl); } } } checkLines.forEach((l) => { ctx.del.lines[l.id] = l; const ndx = data.lines.findIndex((ln) => ln.id === l.id); ~ndx && data.lines.splice(ndx, 1); }); ctx.del.points[p.id] = p; const ndx = data.points.findIndex((pn) => pn.id === p.id); ~ndx && data.points.splice(ndx, 1); return { data, ctx }; }; export const useGetSelectionManage: UseGetSelectionManage = () => { const store = useStore(); const canSelect = (shape: EntityShape) => { const id = shape.id(); const line = store.getTypeItems('line')[0] return !!(id && line.lines.some(item => item.id === id)); }; const listener = (shape: EntityShape) => { const bus: SelectionManageBus = mitt(); const stop = watch( () => canSelect(shape), (exixts, _) => { if (!exixts) { bus.emit("del", shape); } }, { immediate: true } ); return { stop, bus }; }; return { canSelect, listener }; };