123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- import {
- MessageAction,
- penUpdatePoints,
- useDrawRunning,
- useInteractiveDrawShapeAPI,
- usePointBeforeHandler,
- } from "@/core/hook/use-draw";
- import { components, ComponentSnapInfo, SnapPoint } from "..";
- import { useHistory, useHistoryAttach } from "@/core/hook/use-history";
- import { useStore } from "@/core/store";
- import { useViewerTransform } from "@/core/hook/use-viewer";
- 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 { useCustomSnapInfos } from "@/core/hook/use-snap";
- type PayData = Pos;
- export let initData: LineData | undefined;
- export const useInitData = installGlobalVar(() => ref<LineData>());
- // 单例钢笔添加
- export const useDraw = () => {
- const type = "line";
- const { quitDrawShape } = useInteractiveDrawShapeAPI();
- const isRuning = useDrawRunning(type);
- const obj = components[type];
- const beforeHandler = usePointBeforeHandler(true, true);
- const history = useHistory();
- const store = useStore();
- const viewTransform = useViewerTransform();
- const operMode = useOperMode();
- const hInitData = useInitData();
- const customSnapInfos = useCustomSnapInfos();
- // 可能历史空间会撤销 重做更改到正在绘制的组件
- const currentCursor = ref("./icons/m_add.png");
- const cursor = useCursor();
- let cursorPop: ReturnType<typeof cursor.push> | null = null;
- let stopWatch: (() => void) | null = null;
- let prev: SnapPoint;
- let currentIsDel = false;
- let isTempDraw = false;
- let snapInfos: ComponentSnapInfo[] | null = null;
- let drawSnapInfos: ComponentSnapInfo[] | null = null;
- const ia = useInteractiveDots({
- shapeType: type,
- isRuning,
- enter() {
- cursorPop = cursor.push(currentCursor.value);
- watch(currentCursor, () => {
- cursorPop?.set(currentCursor.value);
- });
- },
- quit: () => {
- quitDrawShape();
- beforeHandler.clear();
- cursorPop && cursorPop();
- stopWatch && stopWatch();
- if (!drawItems[0]) return;
- if (isTempDraw) {
- 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]
- if (lastP) {
- console.log(lastP)
- const ctx = getInitCtx()
- ctx.add.points[lastP.id] = lastP
- normalLineData(drawItems[0], ctx)
- }
-
- snapInfos?.forEach(customSnapInfos.remove);
- drawSnapInfos?.forEach(customSnapInfos.remove);
- initData = hInitData.value = void 0;
- },
- beforeHandler: (p) => {
- beforeHandler.clear();
- const pa = beforeHandler.transform(p, prev && [prev, p]);
- currentIsDel && beforeHandler.clear();
- return pa;
- },
- });
- const shapeId = computed(
- () => ia.isRunning.value && store.getTypeItems(type)[0]?.id
- );
- const drawItems = reactive([]) as LineData[];
- watch(
- shapeId,
- () => {
- const data = shapeId.value
- ? (store.getItemById(shapeId.value) as LineData)
- : undefined;
- if (data) {
- initData = hInitData.value = {
- ...data,
- points: [...data.points],
- lines: [...data.lines],
- polygon: [...data.polygon],
- };
- drawItems[0] = {
- ...data,
- createTime: initData.createTime + 1,
- points: [],
- lines: [],
- polygon: [],
- };
- snapInfos = getSnapInfos(initData);
- snapInfos.forEach(customSnapInfos.add);
- } else {
- drawItems.pop();
- initData = hInitData.value = undefined;
- }
- },
- { immediate: true }
- );
- const messages = useHistoryAttach<Pos[]>(
- `${type}-pen`,
- isRuning,
- () => [],
- true
- );
- const getAddMessage = (cur: Pos) => {
- let consumed = messages.value;
- currentCursor.value = "./icons/m_add.png";
- let pen: null | ReturnType<typeof penUpdatePoints> = null;
- return {
- pen,
- consumed,
- cur,
- action: MessageAction.add,
- } as any;
- };
- const setMessage = (cur: Pos) => {
- const { pen, ...msg } = getAddMessage(cur);
- if ((currentIsDel = pen?.oper === "del")) {
- currentCursor.value = "./icons/m_reduce.png";
- beforeHandler.clear();
- }
- return msg;
- };
- const pushMessages = (cur: Pos) => {
- const { pen } = getAddMessage(cur);
- if (pen) {
- if (!pen.unchanged) {
- messages.value = pen.points;
- cur = pen.cur;
- messages.value.push(cur);
- }
- } else {
- messages.value.push(cur);
- }
- return !pen?.unchanged;
- };
- const addItem = (cur: PayData) => {
- if (!drawItems[0]) {
- const data = obj.interactiveToData({
- preset: ia.preset as any,
- info: setMessage(cur),
- viewTransform: viewTransform.value,
- history,
- store,
- });
- if (!data) {
- drawItems.pop();
- return;
- }
- if (initData?.id) {
- data.id = initData?.id;
- }
- drawItems[0] = reactive(data);
- }
- let prevItemIds: string[] = [];
- const storeAddItem = (cItem: LineData) => {
- drawSnapInfos?.forEach(customSnapInfos.remove);
- drawSnapInfos = getSnapInfos(cItem);
- const ctx = getInitCtx();
- cItem.points.forEach((p) => {
- if (!prevItemIds.includes(p.id)) {
- prevItemIds.push(p.id);
- ctx.add.points[p.id] = p;
- }
- });
- cItem.lines.forEach((l) => {
- if (!prevItemIds.includes(l.id)) {
- prevItemIds.push(l.id);
- ctx.add.lines[l.id] = l;
- }
- });
- if (initData) {
- cItem = {
- ...cItem,
- points: [...initData.points, ...cItem.points],
- lines: [...initData.lines, ...cItem.lines],
- polygon: [...initData.polygon, ...cItem.polygon],
- };
- } else {
- cItem = {
- ...cItem,
- points: [...cItem.points],
- lines: [...cItem.lines],
- polygon: [...cItem.polygon],
- };
- }
- cItem = normalLineData(cItem, ctx);
- drawSnapInfos.forEach(customSnapInfos.add);
- if (drawItems[0] && store.getItemById(drawItems[0].id)) {
- store.setItem(type, { id: cItem.id, value: cItem });
- } else {
- store.addItem(type, cItem);
- }
- };
- if (ia.singleDone.value) {
- storeAddItem(drawItems[0]);
- return;
- }
- const update = () => {
- const msg = setMessage(cur);
- drawItems[0] = obj.interactiveFixData({
- data: drawItems[0]!,
- info: msg,
- viewTransform: viewTransform.value,
- history,
- store,
- });
- isTempDraw = true;
- };
- stopWatch = mergeFuns(
- watch(() => operMode.value.freeDraw, update),
- watch(cur, update, { immediate: true, deep: true }),
- watch(
- messages,
- () => {
- if (!messages.value) return;
- if (messages.value.length === 0) {
- quitDrawShape();
- } else {
- update();
- }
- },
- { deep: true }
- ),
- // 监听是否消费完毕
- watch(ia.singleDone, () => {
- prev = { ...cur, view: true };
- const isChange = pushMessages(cur);
- if (isChange) {
- storeAddItem(copy(drawItems[0]));
- }
- beforeHandler.clear();
- stopWatch && stopWatch();
- stopWatch = null;
- isTempDraw = false;
- })
- );
- };
- // 每次拽结束都加组件
- watch(
- () => ia.messages,
- (datas: any) => {
- datas.forEach(addItem);
- ia.consume(datas);
- },
- { immediate: true }
- );
- return drawItems;
- };
- export type NLineDataCtx = {
- del: {
- points: Record<string, LineData["points"][0]>;
- lines: Record<string, LineData["lines"][0]>;
- };
- add: {
- points: Record<string, LineData["points"][0]>;
- lines: Record<string, LineData["lines"][0]>;
- };
- update: {
- points: Record<string, LineData["points"][0]>;
- lines: Record<string, LineData["lines"][0]>;
- };
- };
- export const getInitCtx = (): NLineDataCtx => ({
- del: {
- points: {},
- lines: {},
- },
- add: {
- points: {},
- lines: {},
- },
- update: {
- points: {},
- lines: {},
- },
- });
- export const repPointRef = (data: LineData, delId: string, repId: string) => {
- 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 (line.b === delId) {
- data.lines[i] = { ...line, b: repId };
- }
- }
- return data
- };
- export const deduplicateLines = (data: LineData) => {
- 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 时也去重
- const key1 = `${line.a},${line.b}`;
- const key2 = `${line.b},${line.a}`;
- // 检查是否已存在相同键
- const existingKey = seen.has(key1) ? key1 : seen.has(key2) ? key2 : null;
- if (existingKey) {
- // 如果存在重复键,覆盖旧值(保留尾部元素)
- seen.delete(existingKey);
- seen.set(key1, line); // 统一存储为 key1 格式
- isChange = true
- } else {
- // 新记录,直接存储
- seen.set(key1, line);
- }
- }
- if (isChange) {
- data.lines = Array.from(seen.values())
- }
- return data
- };
- export const normalLineData = (data: LineData, ctx: NLineDataCtx) => {
- const changePoints = [
- ...Object.values(ctx.add.points),
- ...Object.values(ctx.update.points),
- ];
- for (const p2 of changePoints) {
- const ndx = data.points.findIndex((item) => item.id === p2.id);
- if (!~ndx) continue;
- for (let i = 0; i < data.points.length; i++) {
- const p1 = data.points[i];
- if (p1.id !== p2.id && eqPoint(p1, p2)) {
- repPointRef(data, p1.id, p2.id);
- data.points.splice(i, 1);
- i--;
- }
- }
- }
- 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)
- }
- }
- return deduplicateLines(data);
- };
|