Explorar o código

制作吸附功能

bill hai 1 ano
pai
achega
cff34429cc

+ 62 - 12
src/board/packages/whole-line/editable/edit-whole-line.ts

@@ -5,10 +5,14 @@ import {
   WholeLineLineAttrib,
   WholeLinePoint,
   WholeLinePointAttrib,
+  WholeLinePolygonAttrib,
 } from "../view/";
 import {
   getWholeLineLine,
+  getWholeLinePolygonPoints,
+  getWholeLinePolygonRaw,
   mergeWholeLinePointsByPoint,
+  penWholeLinePoygonsEditWithHelperShapesMouse,
   spliceWholeLineLineByPoint,
 } from "../service";
 import { EntityDragHandlers, openEntityDrag } from "../../../shared/shape-mose";
@@ -35,10 +39,12 @@ export class EditWholeLine<
     inc.lineEntityInc.adds.forEach((line) => {
       openEntityDrag(line, this.lineDragHandler);
       line.enableMouseAct({ ...line.actShape, active: undefined });
+      line.shape.name("adsord-line");
     });
     inc.pointEntityInc.adds.forEach((point) => {
       openEntityDrag(point, this.pointDragHandler);
       point.enableMouseAct({ ...point.actShape, active: undefined });
+      point.shape.addName("adsord-point");
     });
     return inc;
   }
@@ -60,6 +66,55 @@ export class EditWholeLine<
     return this.children.filter((line) => names.includes(line.name));
   }
 
+  /**
+   * 开始绘制多边形
+   */
+  private endCreatePolygon: () => void;
+  createPolygon() {
+    let prePolygonId: string;
+    this.endCreatePolygon && this.endCreatePolygon();
+    this.endCreatePolygon = penWholeLinePoygonsEditWithHelperShapesMouse(
+      {
+        quotePoint: true,
+        autoClose: false,
+        tree: this.container,
+        adsorbRadius: 10,
+        config: this.attrib,
+        changePolygon: (pid) => {
+          if (prePolygonId && prePolygonId !== pid) {
+            const polygonAttrib = getWholeLinePolygonRaw(
+              this.attrib,
+              prePolygonId
+            );
+            polygonAttrib && this.polygonAfterHandler(polygonAttrib);
+          }
+          prePolygonId = pid;
+        },
+      },
+      this.shape
+    );
+  }
+
+  pointAfterHandler(pointAttrib: WholeLinePointAttrib) {
+    const merge = mergeWholeLinePointsByPoint(this.attrib, pointAttrib.id);
+    spliceWholeLineLineByPoint(
+      this.attrib,
+      merge?.info.main.id || pointAttrib.id
+    );
+  }
+
+  lineAfterHandler(lineAttrib: WholeLineLineAttrib) {
+    const points = getWholeLineLine(this.attrib, lineAttrib.id);
+    this.pointAfterHandler(points[0]);
+    this.pointAfterHandler(points[1]);
+  }
+
+  polygonAfterHandler(polygonAttrib: WholeLinePolygonAttrib) {
+    getWholeLinePolygonPoints(this.attrib, polygonAttrib.id).forEach(
+      this.pointAfterHandler.bind(this)
+    );
+  }
+
   init() {
     this.lineDragHandler = {
       readyHandler: (lineAttrib) => {
@@ -87,11 +142,7 @@ export class EditWholeLine<
           ev
         );
       },
-      endHandler: (lineAttrib, initPositions, ev) => {
-        const points = getWholeLineLine(this.attrib, lineAttrib.id);
-        this.pointDragHandler.endHandler(points[0], initPositions[0], ev);
-        this.pointDragHandler.endHandler(points[1], initPositions[1], ev);
-      },
+      endHandler: this.lineAfterHandler.bind(this),
     };
 
     this.pointDragHandler = {
@@ -103,14 +154,13 @@ export class EditWholeLine<
         pointAttrib.x = move[0];
         pointAttrib.y = move[1];
       },
-      endHandler: (pointAttrib) => {
-        const merge = mergeWholeLinePointsByPoint(this.attrib, pointAttrib.id);
-        spliceWholeLineLineByPoint(
-          this.attrib,
-          merge?.info.main.id || pointAttrib.id
-        );
-      },
+      endHandler: this.pointAfterHandler.bind(this),
     };
     super.init();
   }
+
+  mounted(): void {
+    super.mounted();
+    this.createPolygon();
+  }
 }

+ 8 - 2
src/board/packages/whole-line/editable/pen-whole-line.ts

@@ -5,6 +5,7 @@ import {
   WLPY,
   WholeLine,
   WholeLineAttrib,
+  WholeLineInc,
   WholeLineLine,
   WholeLineLineAttrib,
   WholeLinePoint,
@@ -129,8 +130,7 @@ export class PenEditWholeLine<
   helperInfo?: Ref<WholeLineHelperInfoAL>;
   autoClose = true;
 
-  diffRedraw() {
-    const inc = super.diffRedraw();
+  dragAttach(inc: WholeLineInc<W>) {
     inc.pointEntityInc.adds.forEach((point) => {
       openEntityDrag(point, {
         readyHandler: (attrib) => {
@@ -153,6 +153,11 @@ export class PenEditWholeLine<
     inc.polygonEntityInc.adds.forEach((py) => {
       py.enableMouseAct(py.actShape);
     });
+  }
+
+  diffRedraw() {
+    const inc = super.diffRedraw();
+    this.dragAttach(inc as any);
     return inc;
   }
 
@@ -189,6 +194,7 @@ export class PenEditWholeLine<
       penWholeLinePoygonsEditWithHelperMouse({
         ...props,
         tree: this.container,
+        autoClose: this.autoClose,
         config: this.attrib,
         changePolygon: (pid: string) => {
           console.error("changePolygon", pid);

+ 3 - 3
src/board/packages/whole-line/service/whole-line-base.ts

@@ -6,13 +6,13 @@ import {
 } from "../view";
 
 export const generateWholeLineLineId = (config: WholeLineAttrib) =>
-  (Math.max(...config.lines.map(({ id }) => Number(id))) + 1).toString();
+  (Math.max(1, ...config.lines.map(({ id }) => Number(id))) + 1).toString();
 
 export const generateWholeLinePointId = (config: WholeLineAttrib) =>
-  (Math.max(...config.points.map(({ id }) => Number(id))) + 1).toString();
+  (Math.max(1, ...config.points.map(({ id }) => Number(id))) + 1).toString();
 
 export const generateWholeLinePoygonId = (config: WholeLineAttrib) =>
-  (Math.max(...config.polygons.map(({ id }) => Number(id))) + 1).toString();
+  (Math.max(1, ...config.polygons.map(({ id }) => Number(id))) + 1).toString();
 
 export const getWholeLinePoint = (config: WholeLineAttrib, id: string) =>
   config.points.find((point) => point.id === id);

+ 45 - 29
src/board/packages/whole-line/service/whole-line-edit.ts

@@ -22,6 +22,25 @@ import {
   wholeLineAddLineByPointIds,
   wholeLineDelPointByPointIds,
 } from "./whole-line-base";
+import { getAdsorbPosition } from "../../../shared/adsorb";
+
+export type PenWholeLinePoygonsEditProps<
+  P extends Omit<WholeLinePointAttrib, "id">
+> = {
+  tree: Container;
+  config: WholeLineAttrib<P & Attrib>;
+  polygonId?: string;
+  adsorbRadius?: number;
+  pointAttribFactory?: (pos: number[]) => Omit<P, "id">;
+  quotePoint?: boolean;
+  canOper?: (
+    tree: WholeLineLine | WholeLinePoint,
+    operShape: ShapeType
+  ) => boolean;
+  canDelPoint?: (point: P, evt: KonvaEventObject<any>) => boolean;
+  changePolygon?: (polygonId: string) => void;
+  autoClose?: boolean;
+};
 
 /**
  * 钢笔模式编辑多边形
@@ -36,23 +55,11 @@ export const penWholeLinePoygonsEdit = <
   pointAttribFactory,
   quotePoint,
   canOper,
+  adsorbRadius,
   canDelPoint,
   changePolygon,
-  dev,
-}: {
-  tree: Container;
-  config: WholeLineAttrib<P & Attrib>;
-  polygonId?: string;
-  pointAttribFactory?: (pos: number[]) => Omit<P, "id">;
-  quotePoint?: boolean;
-  canOper?: (
-    tree: WholeLineLine | WholeLinePoint,
-    operShape: ShapeType
-  ) => boolean;
-  canDelPoint?: (point: P, evt: KonvaEventObject<any>) => boolean;
-  changePolygon?: (polygonId: string) => void;
-  dev?: boolean;
-}) => {
+  autoClose,
+}: PenWholeLinePoygonsEditProps<P>) => {
   pointAttribFactory =
     pointAttribFactory ||
     ((pos: number[]) =>
@@ -100,7 +107,9 @@ export const penWholeLinePoygonsEdit = <
     }
   };
 
-  const continuous = (evt: KonvaEventObject<any>): WholeLineChange => {
+  const continuous = (
+    evt: KonvaEventObject<any>
+  ): { change: WholeLineChange; isClose?: boolean } => {
     let change: WholeLineChange = {
       lineChange: {
         del: [],
@@ -135,7 +144,7 @@ export const penWholeLinePoygonsEdit = <
             child.attrib.id,
             pointAttribFactory(tree.getRealFromStage(pixel))
           );
-          return mergeChange(change, cChange);
+          return { change: mergeChange(change, cChange), isClose: false };
         }
       }
     }
@@ -143,20 +152,25 @@ export const penWholeLinePoygonsEdit = <
     let pointAttrib: P & Attrib;
     if (child instanceof WholeLinePoint) {
       if (canOper && !canOper(child, evt.target)) {
-        return;
+        return { change, isClose: false };
       }
       pointAttrib = quotePoint ? child.attrib : { ...child.attrib };
+
+      if (polyginAttrib.id === prevId) {
+        return { change, isClose: false };
+      }
     } else {
-      pointAttrib = pointAttribFactory(tree.getRealFromStage(pixel)) as P &
-        Attrib;
+      const position = adsorbRadius
+        ? getAdsorbPosition({ tree, pixel, radius: adsorbRadius })
+        : tree.getRealFromStage(pixel);
+      pointAttrib = pointAttribFactory(position) as P & Attrib;
     }
 
-    const exixts = getWholeLinePolygonPoints(config, polyginAttrib.id).some(
-      ({ id }) => id === pointAttrib.id
-    );
-
+    const polygonPoints = getWholeLinePolygonPoints(config, polyginAttrib.id);
+    const curNdx = polygonPoints.findIndex(({ id }) => id === pointAttrib.id);
+    const isClose = curNdx === 0 && (!autoClose || polygonPoints.length >= 3);
     // 存在的情况下删除
-    if (exixts) {
+    if (curNdx >= 0 && !isClose) {
       const cChange = wholeLineDelPointByPointIds(
         config,
         pointAttrib.id,
@@ -177,7 +191,7 @@ export const penWholeLinePoygonsEdit = <
           id: prevId,
         });
       }
-      return change;
+      return { change, isClose };
     }
 
     if (!quotePoint) {
@@ -192,7 +206,7 @@ export const penWholeLinePoygonsEdit = <
           pointAttrib
         ).change;
         // 持续添加模式
-        return mergeChange(change, mChange);
+        return { change: mergeChange(change, mChange), isClose };
       } else {
         // 直接当成新建操作
         start(null);
@@ -220,12 +234,13 @@ export const penWholeLinePoygonsEdit = <
         },
       });
       polyginAttrib.lineIds.push(line.id);
-      return change;
+      prevId = null;
+      return { change, isClose };
     } else {
       const addPoint = wholeLineAddPoint(config, pointAttrib)!;
       prevId = addPoint.id;
       change.pointChange.add.push(addPoint);
-      return change;
+      return { change, isClose };
     }
   };
 
@@ -255,6 +270,7 @@ export const penWholeLinePoygonsEdit = <
       polyginAttrib = status.config.polygons.find(
         ({ id }) => id === status.polyginAttribId
       );
+      polygonId = polyginAttrib.id;
       prevId = status.prevId;
       config = status.config;
     },

+ 98 - 32
src/board/packages/whole-line/service/whole-line-helper.ts

@@ -4,16 +4,19 @@ import {
   getWholeLineLinesRaw,
   getWholeLinePoints,
 } from "./whole-line-base";
-import { ref } from "vue";
+import { computed, ref, watchEffect } from "vue";
+import { WLP, WholeLineAttrib, WholeLinePointAttrib } from "../view";
+import { Attrib } from "../../../type";
 import {
-  WholeLineAttrib,
-  WholeLineLine,
-  WholeLinePoint,
-  WholeLinePointAttrib,
-} from "../view";
-import { Attrib, ShapeType } from "../../../type";
-import { penWholeLinePoygonsEdit } from "./whole-line-edit";
-import { Container } from "../../container";
+  PenWholeLinePoygonsEditProps,
+  penWholeLinePoygonsEdit,
+} from "./whole-line-edit";
+import addMouseIco from "../../../shared/cursor/pic_pen_a.ico";
+import delMouseIco from "../../../shared/cursor/pic_pen_r.ico";
+import { getRealAbsoluteSize } from "../../../shared";
+import { lineShapeFactory, pointShapeFactory } from "../style";
+import { Layer } from "konva/lib/Layer";
+import { Group } from "konva/lib/Group";
 
 export type WholeLineHelperInfo = {
   change: WholeLineChange;
@@ -30,24 +33,13 @@ export type WholeLineHelperInfoAL = ReturnType<
  */
 export const penWholeLinePoygonsEditWithHelperMouse = <
   P extends Omit<WholeLinePointAttrib, "id">
->(props: {
-  tree: Container;
-  config: WholeLineAttrib<P & Attrib>;
-  polygonId?: string;
-  pointAttribFactory?: (pos: number[]) => Omit<P, "id">;
-  quotePoint?: boolean;
-  canOper?: (
-    tree: WholeLineLine | WholeLinePoint,
-    operShape: ShapeType
-  ) => boolean;
-  canDelPoint?: (point: P, evt: KonvaEventObject<any>) => boolean;
-  changePolygon?: (polygonId: string) => void;
-  quitHandler?: () => void;
-}) => {
+>(
+  props: PenWholeLinePoygonsEditProps<P> & { quitHandler?: () => void }
+) => {
   const copyAttrib = () =>
     JSON.parse(JSON.stringify(props.config)) as WholeLineAttrib<P & Attrib>;
 
-  const edit = penWholeLinePoygonsEdit({ ...props, dev: true });
+  const edit = penWholeLinePoygonsEdit({ ...props });
   const helper = penWholeLinePoygonsEdit({
     ...props,
     changePolygon: undefined,
@@ -73,7 +65,7 @@ export const penWholeLinePoygonsEditWithHelperMouse = <
   const moveHandler = (evt: KonvaEventObject<any>) => {
     if (downing) return;
     syncHelper();
-    const change = helper.continuous(evt);
+    const { change } = helper.continuous(evt);
     const { config } = helper.getStatus();
     helperInfo.value = {
       change,
@@ -84,21 +76,26 @@ export const penWholeLinePoygonsEditWithHelperMouse = <
     downing = false;
     moveHandler(evt);
   };
-  let prevTime = 0;
   const clickHandler = (evt: KonvaEventObject<any>) => {
-    if (Date.now() - prevTime < 200) {
+    const { isClose } = edit.continuous(evt);
+    syncHelper();
+    if (isClose) {
+      leaveEditMode();
+    }
+  };
+
+  const quitHandler = (ev: KeyboardEvent) => {
+    if (ev.key === "Escape") {
       leaveEditMode();
-    } else {
-      prevTime = Date.now();
-      edit.continuous(evt);
-      syncHelper();
     }
   };
+
   let leaveEditMode = () => {
-    stage.off("mousedown.editPolygonsMode", clickHandler);
+    stage.off("click.editPolygonsMode", clickHandler);
     stage.off("mousedown.editPolygonsMode", downHandler);
     stage.off("mousemove.editPolygonsMode", moveHandler);
     stage.off("mouseup.editPolygonsMode", upHandler);
+    document.removeEventListener("keydown", quitHandler);
 
     helperInfo.value = undefined;
     edit.end();
@@ -110,6 +107,7 @@ export const penWholeLinePoygonsEditWithHelperMouse = <
   stage.on("mousemove.editPolygonsMode", moveHandler);
   stage.on("mouseup.editPolygonsMode", upHandler);
   stage.on("mousedown.editPolygonsMode", clickHandler);
+  document.addEventListener("keydown", quitHandler);
 
   return {
     helperInfo,
@@ -218,3 +216,71 @@ export const wholeLineAnalysisHelperInfo = (
     cloneLines,
   };
 };
+
+export const penWholeLinePoygonsEditWithHelperShapesMouse = <
+  W extends WholeLineAttrib
+>(
+  props: PenWholeLinePoygonsEditProps<WLP<W>> & { quitHandler?: () => void },
+  tel: Group | Layer
+) => {
+  const { helperInfo: helperInfoRaw, destory: mouseDestory } =
+    penWholeLinePoygonsEditWithHelperMouse({
+      ...props,
+      quitHandler: () => {
+        stopWatchHelper();
+        props.quitHandler && props.quitHandler();
+      },
+    });
+
+  const helperInfo = computed(
+    () =>
+      helperInfoRaw.value &&
+      wholeLineAnalysisHelperInfo(helperInfoRaw.value, props.autoClose)
+  );
+
+  const tempPoint = pointShapeFactory();
+  tempPoint.shape.listening(false);
+
+  const tempLine = lineShapeFactory();
+  tempLine.active();
+  tempLine.shape.dash([5, 5]);
+  tempLine.shape.listening(false);
+
+  const stopWatchHelper = watchEffect((onCleanup) => {
+    if (!helperInfo.value) return;
+
+    const { addLines, cloneLines, addPoints } = helperInfo.value;
+
+    const lineShapes = [...addLines, ...Object.values(cloneLines)].map(
+      (line) => {
+        tempLine.setData(line);
+        return tempLine.shape.clone() as Group;
+      }
+    );
+
+    const [scale] = getRealAbsoluteSize(tel as any, [1, 1]);
+    const pointShapes = addPoints.map((point) => {
+      tempPoint.setData(point);
+      tempPoint.shape.scale({ x: scale, y: scale });
+      return tempPoint.shape.clone() as Group;
+    });
+
+    tel.add(...lineShapes, ...pointShapes);
+
+    let clearCursor;
+    if (helperInfo.value.delPoints.length) {
+      clearCursor = props.tree.setCursor(delMouseIco);
+    } else if (addPoints.length) {
+      clearCursor = props.tree.setCursor(addMouseIco);
+    }
+
+    onCleanup(() => {
+      [...lineShapes, ...pointShapes].forEach((shape) => {
+        shape.remove();
+      });
+      clearCursor && clearCursor();
+    });
+  });
+
+  return mouseDestory;
+};

+ 15 - 8
src/board/packages/whole-line/style.ts

@@ -3,20 +3,25 @@ import { Line } from "konva/lib/shapes/Line";
 import { getRealAbsoluteSize } from "../../shared";
 import { Group } from "konva/lib/Group";
 import { CustomizeShape } from "../../type";
-import { KonvaNodeEvent } from "konva/lib/types";
 import { KonvaEventObject } from "konva/lib/Node";
 
 export const point = {
   fill: "#ffffff",
   radius: 3,
   stroke: "#409EFF",
+  strokeWidth: 2,
   zIndex: 3,
+  hitStrokeWidth: 5,
   activeFill: "#ffffff",
   activeStroke: "#E6A23C",
 };
 
 export const pointShapeFactory = () => {
-  const p = new Circle({ radius: point.radius });
+  const p = new Circle({
+    radius: point.radius,
+    strokeWidth: point.strokeWidth,
+    hitStrokeWidth: point.hitStrokeWidth,
+  });
   const pub = () => {
     const [size] = getRealAbsoluteSize(p, [1, 1]);
     p.scale({ x: size, y: size });
@@ -123,12 +128,14 @@ export const polygonShapeFactory = (props: PolygonShapeProps = {}) => {
     shape: group,
     setData(data: number[]) {
       path.points(data);
-      if (data.length > 4) {
-        const ndxs = [data.length - 2, data.length - 1, 0, 1];
-        closeLine.setData(ndxs.map((ndx) => data[ndx]));
-        closeLine.shape.visible(true);
-      } else {
-        closeLine.shape.visible(false);
+      if (closeLine) {
+        if (data.length > 4) {
+          const ndxs = [data.length - 2, data.length - 1, 0, 1];
+          closeLine.setData(ndxs.map((ndx) => data[ndx]));
+          closeLine.shape.visible(true);
+        } else {
+          closeLine.shape.visible(false);
+        }
       }
     },
     common,

+ 1 - 1
src/board/packages/whole-line/view/whole-line-line.ts

@@ -78,6 +78,6 @@ export class WholeLineLine<
 
   mounted(): void {
     super.mounted();
-    this.actShape.common();
+    this.actShape.common(null);
   }
 }

+ 1 - 1
src/board/packages/whole-line/view/whole-line-point.ts

@@ -49,6 +49,6 @@ export class WholeLinePoint<
 
   mounted(): void {
     super.mounted();
-    this.actShape.common();
+    this.actShape.common(null);
   }
 }

+ 1 - 1
src/board/packages/whole-line/view/whole-line-polygon.ts

@@ -71,6 +71,6 @@ export class WholeLinePolygon<
 
   mounted(): void {
     super.mounted();
-    this.actShape.common();
+    this.actShape.common(null);
   }
 }

+ 11 - 6
src/board/packages/whole-line/view/whole-line.ts

@@ -7,6 +7,7 @@ import { WholeLinePolygon, WholeLinePolygonAttrib } from "./whole-line-polygon";
 import { Group } from "konva/lib/Group";
 import { Entity, EntityProps } from "../../entity";
 import {
+  IncEntitys,
   IncEntitysFactory,
   incEntitysFactoryGenerate,
 } from "../../../shared/entity-utils";
@@ -48,6 +49,12 @@ export type WLPY<W extends WholeLineAttrib> = W extends WholeLineAttrib<
   ? PY
   : never;
 
+export type WholeLineInc<W extends WholeLineAttrib = WholeLineAttrib> = {
+  pointEntityInc: IncEntitys<WLP<W>, WholeLinePoint<WLP<W>>>;
+  lineEntityInc: IncEntitys<WLL<W>, WholeLineLine<WLL<W>>>;
+  polygonEntityInc: IncEntitys<WLPY<W>, WholeLinePolygon<WLPY<W>>>;
+};
+
 export class WholeLine<
   W extends WholeLineAttrib = WholeLineAttrib,
   PS extends ShapeType = ShapeType,
@@ -90,7 +97,10 @@ export class WholeLine<
 
     this.incPolygonFactory = incEntitysFactoryGenerate(
       WholeLinePolygon<WLPY<W>, PYS>,
-      this
+      this,
+      (py) => {
+        py.setConfig(this.attrib);
+      }
     );
 
     if (DEV) {
@@ -105,11 +115,6 @@ export class WholeLine<
         WholeLinePointHelper,
         this
       );
-
-      this.incPointsHelperFactory = incEntitysFactoryGenerate(
-        WholeLinePointHelper,
-        this
-      );
     }
   }
 

+ 97 - 0
src/board/shared/adsorb.ts

@@ -0,0 +1,97 @@
+import { Line } from "konva/lib/shapes/Line";
+import { Container } from "../packages";
+import { getLine2Angle, getLineDist, getLineProjection } from "./math";
+import { MathUtils } from "three";
+
+type AdsorbBaseProps = { tree: Container; position: number[] };
+
+export type AdsorbPointProps = AdsorbBaseProps & {
+  refPointName?: string;
+  radius?: number;
+};
+/**
+ * 参考点吸附
+ */
+export const getAdsorbPointPosition = ({
+  tree,
+  position,
+  refPointName = "adsord-point",
+  radius = 10,
+}: AdsorbPointProps) => {
+  const refPositions = tree.stage.find(`.${refPointName}`).map((ref) => {
+    const refPos = ref.position();
+    return [refPos.x, refPos.y];
+  });
+
+  let adsorbPosition: number[] | null = null;
+  let minDis = Number.MAX_VALUE;
+  for (const refPosition of refPositions) {
+    const dis = getLineDist(refPosition, position);
+    if (dis < radius && dis < minDis) {
+      minDis = dis;
+      adsorbPosition = refPosition;
+    }
+  }
+
+  return adsorbPosition;
+};
+
+type AdsorbLineProps = AdsorbBaseProps & {
+  refLineName?: "adsord-line";
+  angle?: number;
+};
+
+/**
+ * 参考线吸附
+ */
+export const getAdsorbLinePosition = ({
+  tree,
+  position,
+  refLineName = "adsord-line",
+  angle = 3,
+}: AdsorbLineProps) => {
+  const refLines = tree.stage.find<Line>(`.${refLineName}`).flatMap((ref) => {
+    const line = ref.points();
+
+    return [line];
+  });
+
+  angle = MathUtils.degToRad(angle);
+
+  let minAngle = Number.MAX_VALUE;
+  let adsorbLine: number[] | null = null;
+
+  for (const refLine of refLines) {
+    let line = refLine;
+    let cAngle = Math.abs(getLine2Angle(line, [line[0], line[1], ...position]));
+    if (cAngle > angle) {
+      line = [refLine[2], refLine[3], refLine[0], refLine[1]];
+      cAngle = Math.abs(getLine2Angle(line, [line[0], line[1], ...position]));
+    }
+
+    if (cAngle < angle && cAngle < minAngle) {
+      minAngle = cAngle;
+      adsorbLine = line;
+    }
+  }
+
+  return adsorbLine && getLineProjection(adsorbLine, position).point;
+};
+
+export const getAdsorbSelfLinesPosition = () => {};
+
+export type AdsorbProps = Omit<
+  AdsorbPointProps & AdsorbLineProps,
+  "position"
+> & { pixel: number[] };
+
+export const getAdsorbPosition = (props: AdsorbProps) => {
+  const position = props.tree.getPixelFromStage(props.pixel);
+  let refPosition: number[];
+  if ((refPosition = getAdsorbPointPosition({ ...props, position }))) {
+    return refPosition;
+  } else if ((refPosition = getAdsorbLinePosition({ ...props, position }))) {
+    return refPosition;
+  }
+  return position;
+};

+ 7 - 1
src/board/shared/entity-utils.ts

@@ -40,7 +40,13 @@ export const entityFactory = <
 
 export type IncEntitysFactory<T extends Attrib, E extends Entity<T, any>> = (
   attribs: T[] | T
-) => { adds: E[]; dels: E[]; upds: E[] };
+) => IncEntitys<T, E>;
+
+export type IncEntitys<T extends Attrib, E extends Entity<T, any>> = {
+  adds: E[];
+  dels: E[];
+  upds: E[];
+};
 
 // 增量工厂
 export const incEntitysFactoryGenerate = <

+ 1 - 0
src/components/query-board/index.vue

@@ -24,6 +24,7 @@ watch(containerRef, (container, _, onClanup) => {
   if (container) {
     const board = initBoard(container, storeData, false);
     console.log(board.tree);
+    window.board = board;
     onClanup(() => board.destory);
   }
 });

+ 28 - 188
src/components/query-board/storeData.json

@@ -1,227 +1,67 @@
 {
   "rooms": [
     {
-      "id": "2",
+      "id": "1",
       "points": [
         {
-          "id": "1",
-          "x": 881,
-          "y": 492
+          "x": 360,
+          "y": 232,
+          "id": "2"
         },
         {
-          "id": "2",
-          "x": 136,
-          "y": 237
-        },
-        {
-          "id": "8",
-          "x": 371,
-          "y": 699
-        },
-        {
-          "id": "18",
-          "x": 480,
-          "y": 511
-        },
-        {
-          "id": "19",
-          "x": 83,
-          "y": 647
-        },
-        {
-          "id": "3",
-          "x": 178,
-          "y": 803
-        },
-        {
-          "x": 675.8655056700748,
-          "y": 268.3909090194661,
-          "id": "20"
-        },
-        {
-          "x": 163.5307763543757,
-          "y": 608.0099861089675,
-          "id": "21"
-        },
-        {
-          "x": 579.4292704980454,
-          "y": 468.065825925416,
-          "id": "22"
+          "x": 773,
+          "y": 206,
+          "id": "3"
         },
         {
-          "x": 579.1720366885647,
-          "y": 467.6217710319711,
-          "id": "23"
-        },
-        {
-          "x": 445.2292582546364,
-          "y": 325.58255334590945,
-          "id": "24"
-        }
-      ],
-      "polygons": [
-        {
-          "id": "1",
-          "lineIds": [
-            "1",
-            "2",
-            "3",
-            "4",
-            "5",
-            "6",
-            "7",
-            "8",
-            "9",
-            "10",
-            "11",
-            "12",
-            "13"
-          ]
+          "x": 734,
+          "y": 455,
+          "id": "4"
         },
         {
-          "id": "2",
-          "lineIds": [
-            "14",
-            "15",
-            "16",
-            "17",
-            "18",
-            "19"
-          ]
+          "x": 339,
+          "y": 440,
+          "id": "5"
         }
       ],
       "lines": [
         {
-          "id": "1",
-          "pointIds": [
-            "1",
-            "22"
-          ]
-        },
-        {
           "id": "2",
           "pointIds": [
-            "22",
-            "23"
+            "2",
+            "3"
           ]
         },
         {
           "id": "3",
           "pointIds": [
-            "23",
-            "24"
+            "3",
+            "4"
           ]
         },
         {
           "id": "4",
           "pointIds": [
-            "24",
-            "2"
+            "4",
+            "5"
           ]
         },
         {
           "id": "5",
           "pointIds": [
-            "2",
-            "20"
-          ]
-        },
-        {
-          "id": "6",
-          "pointIds": [
-            "20",
-            "22"
-          ]
-        },
-        {
-          "id": "7",
-          "pointIds": [
-            "22",
-            "8"
-          ]
-        },
-        {
-          "id": "8",
-          "pointIds": [
-            "8",
-            "18"
-          ]
-        },
-        {
-          "id": "9",
-          "pointIds": [
-            "18",
-            "23"
-          ]
-        },
-        {
-          "id": "10",
-          "pointIds": [
-            "23",
-            "20"
-          ]
-        },
-        {
-          "id": "11",
-          "pointIds": [
-            "20",
-            "24"
-          ]
-        },
-        {
-          "id": "12",
-          "pointIds": [
-            "24",
-            "21"
-          ]
-        },
-        {
-          "id": "13",
-          "pointIds": [
-            "21",
-            "19"
-          ]
-        },
-        {
-          "id": "14",
-          "pointIds": [
-            "3",
-            "21"
-          ]
-        },
-        {
-          "id": "15",
-          "pointIds": [
-            "21",
+            "5",
             "2"
           ]
-        },
+        }
+      ],
+      "polygons": [
         {
-          "id": "16",
-          "pointIds": [
+          "id": "2",
+          "lineIds": [
             "2",
-            "24"
-          ]
-        },
-        {
-          "id": "17",
-          "pointIds": [
-            "24",
-            "23"
-          ]
-        },
-        {
-          "id": "18",
-          "pointIds": [
-            "23",
-            "22"
-          ]
-        },
-        {
-          "id": "19",
-          "pointIds": [
-            "22",
-            "1"
+            "3",
+            "4",
+            "5"
           ]
         }
       ]