bill 1 год назад
Родитель
Сommit
17147bc19a

+ 18 - 4
src/board/packages/whole-line/editable/edit-whole-line.ts

@@ -13,6 +13,7 @@ import {
   getWholeLinePolygonPoints,
   getWholeLinePolygonRaw,
   mergeWholeLinePointsByPoint,
+  moveWholeLineLine,
   penWholeLinePoygonsEditWithHelperShapesMouse,
   spliceWholeLineLineByPoint,
 } from "../service";
@@ -93,8 +94,10 @@ export class EditWholeLine<
             polygonAttrib && this.polygonAfterHandler(polygonAttrib);
           }
           prePolygonId = pid;
+          console.log("changP", [...this.attrib.polygons]);
         },
         quitHandler: () => {
+          console.log("quit!");
           this.container.bus.emit("dataChangeAfter");
         },
       },
@@ -104,7 +107,7 @@ export class EditWholeLine<
 
   pointAfterHandler(pointAttrib: WholeLinePointAttrib) {
     const merge = mergeWholeLinePointsByPoint(this.attrib, pointAttrib.id);
-    spliceWholeLineLineByPoint(
+    return spliceWholeLineLineByPoint(
       this.attrib,
       merge?.info.main.id || pointAttrib.id
     );
@@ -118,19 +121,27 @@ export class EditWholeLine<
 
   polygonAfterHandler(polygonAttrib: WholeLinePolygonAttrib) {
     getWholeLinePolygonPoints(this.attrib, polygonAttrib.id).forEach(
-      this.pointAfterHandler.bind(this)
+      (attrib) => {
+        if (this.attrib.points.includes(attrib)) {
+          console.log(this.pointAfterHandler(attrib));
+        }
+      }
     );
   }
 
   init() {
     this.lineDragHandler = {
       readyHandler: (lineAttrib) => {
+        this.container.bus.emit("dataChangeBefore");
         this.container.tempDraw();
         const pointAttribs = getWholeLineLine(this.attrib, lineAttrib.id);
         return pointAttribs.map(({ x, y }) => [x, y]);
       },
       moveHandler: (lineAttrib, move, initPositions, ev) => {
+        moveWholeLineLine(this.attrib, lineAttrib, initPositions, move);
+        return;
         const points = getWholeLineLine(this.attrib, lineAttrib.id);
+        console.log(move);
         const currentPositions = initPositions.map((pos) => [
           pos[0] + move[0],
           pos[1] + move[1],
@@ -149,7 +160,10 @@ export class EditWholeLine<
           ev
         );
       },
-      endHandler: this.lineAfterHandler.bind(this),
+      endHandler: (attrib) => {
+        this.lineAfterHandler(attrib);
+        this.container.bus.emit("dataChangeAfter");
+      },
     };
 
     this.pointDragHandler = {
@@ -191,6 +205,6 @@ export class EditWholeLine<
 
   mounted(): void {
     super.mounted();
-    this.createPolygon();
+    // this.createPolygon();
   }
 }

+ 11 - 2
src/board/packages/whole-line/helper/whole-line-line-helper.ts

@@ -3,7 +3,7 @@ import {
   WholeLineLineAttrib,
   WholeLineLineProps,
 } from "../view/whole-line-line";
-import { getLineDireAngle } from "../../../shared/math";
+import { getLineCenter, getLineDireAngle } from "../../../shared/math";
 import { MathUtils } from "three";
 import { WholeLineAttrib } from "../view/whole-line";
 import { Entity } from "../../entity";
@@ -11,6 +11,7 @@ import { Group } from "konva/lib/Group";
 import { Label } from "konva/lib/shapes/Label";
 import { Arrow } from "konva/lib/shapes/Arrow";
 import { Text } from "konva/lib/shapes/Text";
+import { getRealAbsoluteSize } from "../../../shared";
 
 export class WholeLineLineHelper extends Entity<WholeLineLineAttrib, Group> {
   static namespace = "line-helper";
@@ -60,7 +61,7 @@ export class WholeLineLineHelper extends Entity<WholeLineLineAttrib, Group> {
 
     const arrowSize = 8;
 
-    const shape = new konva.Group();
+    const shape = new Group({ name: "offset" });
     shape.add(label).add(
       new konva.Arrow({
         name: "arrow-1",
@@ -118,17 +119,24 @@ export class WholeLineLineHelper extends Entity<WholeLineLineAttrib, Group> {
       coords[ndx * 2 + 1] = y;
     });
 
+    let scale = getRealAbsoluteSize(
+      this.shape.findOne<Label>(".label"),
+      [1, 1]
+    );
+
     const angle = MathUtils.radToDeg(getLineDireAngle(coords, [0, 1]));
     this.shape
       .findOne<Label>(".label")
       .x((coords[0] + coords[2]) / 2)
       .y((coords[1] + coords[3]) / 2)
+      .scale({ x: scale[0], y: scale[1] })
       .rotation(angle < 0 ? angle + 90 : angle - 90);
 
     this.shape
       .findOne<Arrow>(".arrow-1")
       .x(coords[2])
       .y(coords[3])
+      .scale({ x: scale[0], y: scale[1] })
       .rotation(angle + 90)
       .visible(true);
 
@@ -138,6 +146,7 @@ export class WholeLineLineHelper extends Entity<WholeLineLineAttrib, Group> {
         .x(coords[0])
         .y(coords[1])
         .rotation(angle - 90)
+        .scale({ x: scale[0], y: scale[1] })
         .visible(true);
     } else {
       this.shape.findOne<Arrow>(".arrow-2").visible(false);

+ 4 - 0
src/board/packages/whole-line/helper/whole-line-point-helper.ts

@@ -5,6 +5,7 @@ import {
 } from "../view/whole-line-point";
 import { Entity } from "../../entity";
 import { Label } from "konva/lib/shapes/Label";
+import { getRealAbsoluteSize } from "../../../shared";
 
 export class WholeLinePointHelper extends Entity<WholeLinePointAttrib, Label> {
   static namespace = "point-helper";
@@ -45,5 +46,8 @@ export class WholeLinePointHelper extends Entity<WholeLinePointAttrib, Label> {
 
   diffRedraw(): void {
     this.shape.x(this.attrib.x).y(this.attrib.y);
+
+    const scale = getRealAbsoluteSize(this.shape, [1, 1]);
+    this.shape.scale({ x: scale[0], y: scale[0] });
   }
 }

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

@@ -25,11 +25,27 @@ export const getWholeLinePoints = (config: WholeLineAttrib, ids?: string[]) => {
   }
 };
 
+export const getWholeLineLinesRawByPointId = (
+  config: WholeLineAttrib,
+  pointId: string
+) => {
+  return config.lines.filter(({ pointIds }) => pointIds.includes(pointId));
+};
+export const getWholeLineLinesRawByPointIds = (
+  config: WholeLineAttrib,
+  qpointIds: string[]
+) => {
+  return config.lines.filter(
+    ({ pointIds }) =>
+      pointIds.includes(qpointIds[0]) && pointIds.includes(qpointIds[1])
+  );
+};
+
 export const getWholeLineLinesRawByPoint = (
   config: WholeLineAttrib,
   point: WholeLinePointAttrib
 ) => {
-  return config.lines.filter(({ pointIds }) => pointIds.includes(point.id));
+  return getWholeLineLinesRawByPointId(config, point.id);
 };
 
 export const getWholeLineLineRaw = (config: WholeLineAttrib, lineId: string) =>
@@ -128,7 +144,7 @@ export const getWholeLinePolygonPoints = (
 export type WholeLineChange = {
   lineChange?: {
     del?: WholeLineLineAttrib[];
-    update?: { before: WholeLineLineAttrib[]; after: WholeLineLineAttrib[] }[];
+    update?: { before: WholeLineLineAttrib; after: WholeLineLineAttrib }[];
     add?: WholeLineLineAttrib[];
   };
   polygonChange?: {
@@ -538,3 +554,102 @@ export const wholeLineReplacePoint = (
 
   return change;
 };
+
+/**
+ * 线段点脱离
+ * @param config
+ * @param polygonLine
+ * @param addPoint
+ */
+// wholeLinePolygonLineAddPoint
+export const wholeLineLineIsolatePoint = (
+  config: WholeLineAttrib,
+  line: WholeLinePointAttrib[],
+  isolateId: string,
+  addPointId: string
+) => {
+  const change: WholeLineChange = {
+    lineChange: {
+      update: [],
+      add: [],
+    },
+    polygonChange: {
+      update: [],
+    },
+    pointChange: {
+      add: [],
+      del: [],
+    },
+  };
+  const isolateLines = getWholeLineLinesRawByPointIds(
+    config,
+    line.map(({ id }) => id)
+  );
+
+  const passiveLines = config.lines.filter((line) => {
+    if (isolateLines.includes(line)) return false;
+    const ndx = line.pointIds.indexOf(isolateId);
+    if (~ndx) {
+      const points = [...line.pointIds];
+      points[ndx] = addPointId;
+      change.lineChange.update.push({
+        before: { ...line, pointIds: [...line.pointIds] },
+        after: { ...line, pointIds: [...points] },
+      });
+      line.pointIds = points;
+      return true;
+    }
+  });
+
+  let addedPoints: string[] | null = null;
+  const lineIds = line.map((p) => p.id);
+  if (lineIds.includes(pointId)) {
+    return { addedPoints, change };
+  }
+
+  for (const polygon of config.polygons) {
+    let initPolygonLineIds: string[];
+    for (let ndx = 0; ndx < polygon.lineIds.length; ndx++) {
+      const lineRaw = getWholeLineLineRaw(config, polygon.lineIds[ndx]);
+      // 如果需要添加的点已经是线段的起点或终点则直接忽略
+      if (
+        lineIds.includes(lineRaw.pointIds[0]) &&
+        lineIds.includes(lineRaw.pointIds[1])
+      ) {
+        initPolygonLineIds = initPolygonLineIds || [...polygon.lineIds];
+        addedPoints = addedPoints || [
+          lineRaw.pointIds[0],
+          pointId,
+          lineRaw.pointIds[1],
+        ];
+        const addl1 = wholeLineAddLineByPointIds(config, [
+          lineRaw.pointIds[0],
+          pointId,
+        ]);
+        // [1,2][2,3] [2,4, 3]
+        // [1.2][2.4],[4.3]
+        const addl2 = wholeLineAddLineByPointIds(config, [
+          pointId,
+          lineRaw.pointIds[1],
+        ]);
+
+        change.lineChange.add.push(
+          ...addl1.change.lineChange.add,
+          ...addl2.change.lineChange.add
+        );
+        polygon.lineIds.splice(ndx, 1, addl1.line.id, addl2.line.id);
+
+        const dl = wholeLineDelLineByPointIds(config, lineRaw.pointIds);
+        change.lineChange.del.push(...dl.change.lineChange.del);
+        change.pointChange.del.push(...dl.change.pointChange.del);
+      }
+    }
+    if (initPolygonLineIds) {
+      change.polygonChange.update.push({
+        before: { ...polygon, lineIds: initPolygonLineIds },
+        after: polygon,
+      });
+    }
+  }
+  return { addedPoints, change };
+};

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

@@ -135,7 +135,7 @@ export const penWholeLinePoygonsEdit = <
       );
     });
     const child = target && tree.find(target.id());
-    const pixel = [evt.evt.x, evt.evt.y];
+    const pixel = [evt.evt.offsetX, evt.evt.offsetY];
 
     if (child instanceof WholeLineLine) {
       if (!canOper || canOper(child, evt.target)) {

+ 174 - 2
src/board/packages/whole-line/service/whole-line-tear-merge.ts

@@ -1,19 +1,34 @@
+import { MathUtils, Vector2 } from "three";
 import { DEV } from "../../../env";
 import {
   getLineIntersection,
   getLineDist,
   getLineNearPointDist,
+  getDire2Angle,
+  getLineDire,
+  getVerticalDire,
+  createLineByDire,
+  getLineProjection,
 } from "../../../shared/math";
-import { WholeLineAttrib, WholeLinePointAttrib } from "../view/";
+import {
+  WholeLineAttrib,
+  WholeLineLineAttrib,
+  WholeLinePointAttrib,
+} from "../view/";
 import {
   WHOLE_LINE_POINT_EQ_DIST,
   WHOLE_LINE_POINT_MERGE_DIST,
 } from "./constant";
 import {
+  generateWholeLineLineId,
+  generateWholeLinePointId,
+  getWholeLineLinesRawByPoint,
+  getWholeLineLinesRawByPointId,
   getWholeLinePoint,
   getWholeLinePoints,
   getWholeLinePolygonLines,
   getWholeLinePolygonLinesByPoint,
+  wholeLineAddLineByPointIds,
   wholeLineLineAddPoint,
   wholeLineReplacePoint,
 } from "./whole-line-base";
@@ -353,7 +368,7 @@ export const spliceWholeLineLineByPoint = (
   config: WholeLineAttrib,
   pointId: string
 ) => {
-  spliceWholeLineLineByLines(
+  return spliceWholeLineLineByLines(
     config,
     getWholeLinePolygonLinesByPoint(config, pointId).reduce((t, c) => {
       t.push(...c.lines);
@@ -435,3 +450,160 @@ export const mergeWholeLinePointsByPoint = (
     change,
   };
 };
+
+/**
+ * 获取挪动线段当前线段两点的位置
+ * @param config
+ * @param lineAttrib
+ * @param initPosition 线段初始位置
+ * @param move 移动向量
+ */
+export const moveWholeLineLine = (
+  config: WholeLineAttrib,
+  lineAttrib: WholeLineLineAttrib,
+  initPosition: number[][],
+  move: number[],
+  maxAngle = 160
+) => {
+  console.log(lineAttrib);
+  const linePointIds = lineAttrib.pointIds;
+  const moveDire = new Vector2(move[0], move[1]).normalize().toArray();
+  const pointJointLineDires: (number[] | null)[] = [];
+  const poinJointLineIds: (string | null)[] = [];
+  const isSplitPoints: boolean[] = [];
+
+  // 获取参考线段,夹角最小,雨move向量夹角最小的优先
+  for (let i = 0; i < 2; i++) {
+    const curPointId = linePointIds[i];
+    const joinLines = getWholeLineLinesRawByPointId(config, curPointId).filter(
+      (line) =>
+        !(
+          line.pointIds.includes(linePointIds[0]) &&
+          line.pointIds.includes(linePointIds[1])
+        )
+    );
+
+    const line = getWholeLinePoints(config, [
+      linePointIds[i],
+      linePointIds[i === 0 ? 1 : 0],
+    ]);
+    const lineDire = getLineDire([line[0].x, line[0].y, line[1].x, line[1].y]);
+
+    let invAngle = Number.MAX_VALUE;
+    let invSelectLineId: string;
+    let invSelectLineDire: number[] | null = null;
+
+    let alongAngle = -Number.MAX_VALUE;
+    let alongSelectLineId: string;
+    let alongSelectLineDire: number[] | null;
+
+    for (let { pointIds: joinPointIds, id } of joinLines) {
+      joinPointIds = [
+        linePointIds[i],
+        ...joinPointIds.filter((id) => id !== linePointIds[i]),
+      ];
+      const joinLine = getWholeLinePoints(config, joinPointIds);
+      const joinLineDire = getLineDire([
+        joinLine[0].x,
+        joinLine[0].y,
+        joinLine[1].x,
+        joinLine[1].y,
+      ]);
+      // if (["10", "14"].includes(id)) {
+      //   continue;
+      // }
+
+      const currentAngle = getDire2Angle(lineDire, joinLineDire);
+      // 逆时针
+      if (currentAngle > 0) {
+        if (currentAngle < invAngle) {
+          invAngle = currentAngle;
+          invSelectLineId = id;
+          invSelectLineDire = joinLineDire;
+        }
+      } else {
+        if (currentAngle > alongAngle) {
+          alongAngle = currentAngle;
+          alongSelectLineId = id;
+          alongSelectLineDire = joinLineDire;
+        }
+      }
+    }
+
+    let pointJointLineDire: number[] | null = null;
+    let angle: number | null = null;
+    let selectLineId: string | null = null;
+
+    if (!invSelectLineDire || !alongSelectLineDire) {
+      pointJointLineDire = invSelectLineDire || alongSelectLineDire;
+      selectLineId = invSelectLineId || alongSelectLineId;
+      angle = invAngle | alongAngle;
+    } else if (
+      Math.abs(getDire2Angle(moveDire, invSelectLineDire)) <
+      Math.abs(getDire2Angle(moveDire, alongSelectLineDire))
+    ) {
+      pointJointLineDire = invSelectLineDire;
+      selectLineId = invSelectLineId;
+      angle = invAngle;
+    } else {
+      pointJointLineDire = alongSelectLineDire;
+      selectLineId = alongSelectLineId;
+      angle = alongAngle;
+    }
+
+    let isSplitPoint = joinLines.length > 2;
+    // 需要判定,如果参考线与当前线段夹角超过多少则不适合作为参考线
+    if (
+      pointJointLineDire !== null &&
+      Math.abs(MathUtils.radToDeg(angle)) > maxAngle
+    ) {
+      selectLineId = null;
+      pointJointLineDire = getVerticalDire(pointJointLineDire);
+      isSplitPoint = true;
+    }
+
+    pointJointLineDires.push(pointJointLineDire);
+    poinJointLineIds.push(selectLineId);
+    isSplitPoints.push(isSplitPoint);
+  }
+
+  const positions = [
+    [initPosition[0][0] + move[0], initPosition[0][1] + move[1]],
+    [initPosition[1][0] + move[0], initPosition[1][1] + move[1]],
+  ];
+  const pointAttribs = getWholeLinePoints(config, lineAttrib.pointIds);
+  if (pointJointLineDires[0] || !pointJointLineDires[1]) {
+    for (let i = 0; i < pointJointLineDires.length; i++) {
+      const joinDire =
+        pointJointLineDires[i] === null
+          ? pointJointLineDires[0] || pointJointLineDires[1]
+          : pointJointLineDires[i];
+      const isSplit = isSplitPoints[i];
+      const joinLine = createLineByDire(joinDire, initPosition[i], 10);
+
+      if (isSplit) {
+        const addPointId = generateWholeLinePointId(config);
+        config.points.push({
+          ...pointAttribs[i],
+          id: addPointId,
+        });
+        config.lines.forEach((line) => {
+          if (line === lineAttrib) return;
+          const ndx = line.pointIds.indexOf(pointAttribs[i].id);
+          if (~ndx) {
+            line.pointIds[ndx] = addPointId;
+          }
+        });
+        console.log(wholeLineLineAddPoint(config, pointAttribs, addPointId));
+      }
+
+      positions[i] = getLineProjection(joinLine, positions[i]).point;
+    }
+  }
+
+  // console.log(positions);
+  pointAttribs[0].x = positions[0][0];
+  pointAttribs[0].y = positions[0][1];
+  pointAttribs[1].x = positions[1][0];
+  pointAttribs[1].y = positions[1][1];
+};

+ 0 - 1
src/board/plugins/history-plugin.ts

@@ -58,7 +58,6 @@ export class HistoryPlugin<T = any> {
       data = JSON.parse(JSON.stringify(data));
       this.history.pushSync(data);
       this.syncState();
-      console.log("push", data);
     }
   }
 

+ 39 - 27
src/board/shared/adsorb.ts

@@ -77,7 +77,7 @@ export const getAdsorbLinePosition = ({
   refLineName = "adsord-line",
   exclusionIds = [],
   angle = 3,
-  radiusInner = 50,
+  radiusInner = 500,
 }: AdsorbLineProps) => {
   const refLines = tree.stage
     .find<Line>(`.${refLineName}`)
@@ -161,15 +161,10 @@ export const getAdsorbLinePositionRaw = ({
   }
 };
 
-export type AdsorbSelfLinesProps = {
-  points?: number[][];
-  angle?: number;
-  position: number[];
-};
-export const getAdsorbSelfLinesPosition = ({
+export const getAdsorbCrossPosition = ({
   position,
-  points,
   angle,
+  points,
 }: AdsorbSelfLinesProps) => {
   if (!points?.length) return;
 
@@ -180,25 +175,38 @@ export const getAdsorbSelfLinesPosition = ({
       [point[0], point[1] - 50, point[0], point[1] + 50],
     ];
     return getAdsorbLinePositionRaw({ position, refLines, angle });
-  } else {
-    const last = points.slice(points.length - 2).flatMap((a) => a);
-    const first = points.slice(0, 2).flatMap((a) => a);
-    const vlines = [
-      createLineByDire(getVerticaLineDire(last), [last[2], last[3]], 10),
-      createLineByDire(getVerticaLineDire(first), [first[0], last[1]], 10),
-    ];
-
-    // 垂直最后一段
-    return (
-      getAdsorbLinePositionRaw({
-        position,
-        refLines: vlines,
-        angle,
-      }) || position
-    );
   }
 };
 
+export type AdsorbSelfLinesProps = {
+  points?: number[][];
+  angle?: number;
+  position: number[];
+};
+export const getAdsorbSelfLinesPosition = ({
+  position,
+  points,
+  angle,
+}: AdsorbSelfLinesProps) => {
+  if (!points?.length) return;
+
+  const last = points.slice(points.length - 2).flatMap((a) => a);
+  const first = points.slice(0, 2).flatMap((a) => a);
+  const vlines = [
+    createLineByDire(getVerticaLineDire(last), [last[2], last[3]], 10),
+    createLineByDire(getVerticaLineDire(first), [first[0], last[1]], 10),
+  ];
+
+  // 垂直最后一段
+  return (
+    getAdsorbLinePositionRaw({
+      position,
+      refLines: vlines,
+      angle,
+    }) || position
+  );
+};
+
 export type AdsorbProps = Omit<
   AdsorbPointProps & AdsorbLineProps & AdsorbSelfLinesProps,
   "position"
@@ -207,12 +215,16 @@ export type AdsorbProps = Omit<
 export const getAdsorbPosition = (props: AdsorbProps) => {
   const position = props.position || props.tree.getRealFromStage(props.pixel);
   let refPosition: number[];
-  if ((refPosition = getAdsorbSelfLinesPosition({ ...props, position }))) {
-    return refPosition;
-  } else if ((refPosition = getAdsorbPointPosition({ ...props, position }))) {
+  if ((refPosition = getAdsorbPointPosition({ ...props, position }))) {
     return refPosition;
   } else if ((refPosition = getAdsorbLinePosition({ ...props, position }))) {
     return refPosition;
+  } else if ((refPosition = getAdsorbCrossPosition({ ...props, position }))) {
+    return refPosition;
+  } else if (
+    (refPosition = getAdsorbSelfLinesPosition({ ...props, position }))
+  ) {
+    return refPosition;
   }
   return position;
 };

+ 4 - 0
src/board/shared/math.ts

@@ -145,6 +145,10 @@ export const getLineDireAngle = (line: number[], dire: number[]) => {
   return getDire2Angle(dire, getLineDire(line));
 };
 
+export const getLineCenter = (line: number[]) => {
+  return [(line[0] + line[2]) / 2, (line[1] + line[3]) / 2];
+};
+
 export enum RelationshipEnum {
   Overlap = "Overlap",
   Intersect = "Intersect",

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

@@ -5,6 +5,7 @@
   <ElButton :disabled="!board?.history.state.hasRedo" @click="board?.history.redo()">
     重做
   </ElButton>
+  <ElButton @click="getData">获取数据</ElButton>
   <div
     class="board-layout"
     :style="{ width: width + 'px', height: height + 'px' }"
@@ -41,6 +42,9 @@ watch(containerRef, (container, _, onClanup) => {
     onClanup(() => board.value.destory);
   }
 });
+const getData = () => {
+  console.log(JSON.stringify(board.value?.getData(), null, 4));
+};
 </script>
 
 <style lang="scss" scoped>

+ 61 - 5
src/components/query-board/storeData.json

@@ -22,6 +22,21 @@
           "x": 339,
           "y": 440,
           "id": "5"
+        },
+        {
+          "x": 688,
+          "y": 606,
+          "id": "8"
+        },
+        {
+          "x": 443,
+          "y": 594,
+          "id": "9"
+        },
+        {
+          "x": 556.8756799999999,
+          "y": 448.27376,
+          "id": "10"
         }
       ],
       "lines": [
@@ -40,17 +55,45 @@
           ]
         },
         {
-          "id": "4",
+          "id": "5",
+          "pointIds": [
+            "5",
+            "2"
+          ]
+        },
+        {
+          "id": "8",
+          "pointIds": [
+            "8",
+            "9"
+          ]
+        },
+        {
+          "id": "10",
+          "pointIds": [
+            "9",
+            "10"
+          ]
+        },
+        {
+          "id": "12",
           "pointIds": [
             "4",
+            "10"
+          ]
+        },
+        {
+          "id": "13",
+          "pointIds": [
+            "10",
             "5"
           ]
         },
         {
-          "id": "5",
+          "id": "14",
           "pointIds": [
-            "5",
-            "2"
+            "10",
+            "8"
           ]
         }
       ],
@@ -60,9 +103,22 @@
           "lineIds": [
             "2",
             "3",
-            "4",
+            "12",
+            "13",
             "5"
           ]
+        },
+        {
+          "id": "3",
+          "lineIds": [
+            "10",
+            "14",
+            "8"
+          ]
+        },
+        {
+          "id": "4",
+          "lineIds": []
         }
       ]
     }