瀏覽代碼

Merge branch 'master' of http://192.168.0.115:3000/bill/traffic-laser

xzw 2 年之前
父節點
當前提交
168078d45b
共有 81 個文件被更改,包括 1189 次插入269 次删除
  1. 1 1
      server/test/SS-t-P1d6CwREny2/attach/sceneStore
  2. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684479043688299.jpg
  3. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/168447904681957.jpg
  4. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684479049416775.jpg
  5. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/168472768891240.jpg
  6. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684728206467446.jpg
  7. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684735841295821.jpg
  8. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/16847359104042.jpg
  9. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684735920147128.jpg
  10. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684735932996462.jpg
  11. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/168473603939957.jpg
  12. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684736953359567.jpg
  13. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684736982113424.jpg
  14. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737013765711.jpg
  15. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737048191442.jpg
  16. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/168473708385387.jpg
  17. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737199119728.jpg
  18. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737206687866.jpg
  19. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737271702517.jpg
  20. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737360285887.jpg
  21. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737393938206.jpg
  22. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737453052523.jpg
  23. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737457117775.jpg
  24. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737506828614.jpg
  25. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737510832263.jpg
  26. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737534494643.jpg
  27. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737663096982.jpg
  28. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737706400796.jpg
  29. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737718696260.jpg
  30. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737741845187.jpg
  31. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737782029382.jpg
  32. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684737802452542.jpg
  33. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/168473930031273.jpg
  34. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/168473937425148.jpg
  35. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684740940126552.jpg
  36. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/1684740958334633.jpg
  37. 二進制
      server/test/SS-t-P1d6CwREny2/attach/upload/168474170400058.jpg
  38. 33 1
      server/test/SS-t-P1d6CwREny2/webcloud/sources.json
  39. 2 1
      src/components/group-button/index.vue
  40. 43 6
      src/graphic/CanvasStyle/default.js
  41. 36 2
      src/graphic/CanvasStyle/focus.js
  42. 37 2
      src/graphic/CanvasStyle/select.js
  43. 3 0
      src/graphic/Constant.js
  44. 2 11
      src/graphic/Controls/AddLine.js
  45. 173 46
      src/graphic/Controls/AddPoint.js
  46. 0 6
      src/graphic/Controls/MoveLine.js
  47. 86 0
      src/graphic/Controls/MovePoint.js
  48. 65 16
      src/graphic/Controls/UIControl.js
  49. 14 3
      src/graphic/Geometry/Line.js
  50. 21 2
      src/graphic/Geometry/Point.js
  51. 13 20
      src/graphic/History/History.js
  52. 19 3
      src/graphic/History/HistoryUtil.js
  53. 30 4
      src/graphic/Layer.js
  54. 199 32
      src/graphic/Renderer/Draw.js
  55. 35 41
      src/graphic/Service/DataService.js
  56. 43 0
      src/graphic/Service/LineService.js
  57. 1 2
      src/graphic/Service/PointService.js
  58. 13 0
      src/graphic/Service/RoadService.js
  59. 45 0
      src/graphic/Service/UIService.js
  60. 5 0
      src/graphic/Settings.js
  61. 14 10
      src/graphic/Util/MathUtil.js
  62. 3 0
      src/graphic/enum/LayerEvents.js
  63. 7 8
      src/graphic/enum/UIEvents.js
  64. 2 2
      src/graphic/enum/VectorCategory.js
  65. 4 1
      src/hook/useGraphic.ts
  66. 4 2
      src/router/constant.ts
  67. 6 0
      src/router/info.ts
  68. 1 1
      src/sdk/types/sdk.ts
  69. 1 0
      src/store/accidentPhotos.ts
  70. 1 0
      src/store/photos.ts
  71. 1 0
      src/store/roadPhotos.ts
  72. 12 0
      src/store/sync.ts
  73. 30 4
      src/views/accidents/index.vue
  74. 103 0
      src/views/accidents/print.vue
  75. 1 0
      src/views/graphic/data.ts
  76. 1 1
      src/views/graphic/geos/index.ts
  77. 39 11
      src/views/graphic/menus.ts
  78. 15 9
      src/views/photos/index.vue
  79. 17 3
      src/views/roads/index.vue
  80. 0 16
      src/views/roads/tabulation.vue
  81. 8 2
      src/views/scene/photo.vue

文件差異過大導致無法顯示
+ 1 - 1
server/test/SS-t-P1d6CwREny2/attach/sceneStore


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684479043688299.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/168447904681957.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684479049416775.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/168472768891240.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684728206467446.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684735841295821.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/16847359104042.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684735920147128.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684735932996462.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/168473603939957.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684736953359567.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684736982113424.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737013765711.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737048191442.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/168473708385387.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737199119728.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737206687866.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737271702517.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737360285887.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737393938206.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737453052523.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737457117775.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737506828614.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737510832263.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737534494643.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737663096982.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737706400796.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737718696260.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737741845187.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737782029382.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684737802452542.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/168473930031273.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/168473937425148.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684740940126552.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/1684740958334633.jpg


二進制
server/test/SS-t-P1d6CwREny2/attach/upload/168474170400058.jpg


+ 33 - 1
server/test/SS-t-P1d6CwREny2/webcloud/sources.json

@@ -1 +1,33 @@
-{"bounds":{"min":[-28.193546295166017,-25.514963150024415,-1.328073501586914],"max":[35.724212646484378,38.40279579162598,62.58968544006348]},"projection":"","sources":[{"name":"info.json","points":1177633,"bounds":{"min":[-28.193546295166017,-25.514963150024415,-1.328073501586914],"max":[35.724212646484378,38.40279579162598,62.58968544006348]}}]}
+{
+  "bounds": {
+    "min": [
+      -28.193546295166017,
+      -25.514963150024415,
+      -1.328073501586914
+    ],
+    "max": [
+      35.724212646484378,
+      38.40279579162598,
+      62.58968544006348
+    ]
+  },
+  "projection": "",
+  "sources": [
+    {
+      "name": "info.json",
+      "points": 1177633,
+      "bounds": {
+        "min": [
+          -28.193546295166017,
+          -25.514963150024415,
+          -1.328073501586914
+        ],
+        "max": [
+          35.724212646484378,
+          38.40279579162598,
+          62.58968544006348
+        ]
+      }
+    }
+  ]
+}

+ 2 - 1
src/components/group-button/index.vue

@@ -5,7 +5,7 @@
       :key="menu.key"
       class="menu"
       :style="menuStyle"
-      :class="{ active: activeKey === menu.key, dire }"
+      :class="{ active: activeKey === menu.key, dire, disabled: menu.disabled }"
       @click="menu.onClick && menu.onClick(menu)"
     >
       <template v-if="$slots.default">
@@ -26,6 +26,7 @@ type Menu =  {
   key: any,
   text?: string,
   icon?: string,
+  disabled?: boolean
   onClick?: (menu: Menu) => void
 }
 

+ 43 - 6
src/graphic/CanvasStyle/default.js

@@ -16,7 +16,7 @@ const Lane = {
   dash: [8, 8],
 };
 
-const Arrow = {
+const ArrowLine = {
   lineWidth: 2,
   strokeStyle: "red",
 };
@@ -112,14 +112,33 @@ const GuideLine = {
   dash: [3, 2, 2],
 };
 const MeasureLine = {
-  strokeStyle: "#CED806",
-  lineWidth: 2,
+  strokeStyle: "#2F8FFF",
+  lineWidth: 4,
+  text: {
+    fillColor: "#fff",
+    padding: 6,
+    backColor: "#2F8FFF"
+  },
+
 };
 const BaseLine = {
-  strokeStyle: "#3290FF",
-  lineWidth: 2,
+  strokeStyle: "#000",
+  lineWidth: 1,
 };
 
+const BasePoint = {
+  strokeStyle: "rgba(0,0,0,0)",
+  fillStyle: "#000",
+  radius: 6,
+  lineWidth: 1,
+  out: {
+    strokeStyle: "#000",
+    fillStyle: "rgba(255,255,255,0)",
+    radius: 8,
+    lineWidth: 1,
+  }
+}
+
 const Element = {
   AddingPoint: {
     radius: 4,
@@ -151,6 +170,22 @@ const Element = {
   },
 };
 
+
+const TestPoint = {
+  strokeStyle: "rgba(0,0,0,0)",
+  fillStyle: "#fff",
+  radius: 6,
+  lineWidth: 1,
+  out: {
+    strokeStyle: "#3290FF",
+    fillStyle: "rgba(255,255,255,0)",
+    radius: 8,
+    lineWidth: 3,
+  }
+}
+
+
+
 export default {
   NormalLine,
   Road,
@@ -171,7 +206,9 @@ export default {
   MeasureLine,
   Measure,
   Element,
+  TestPoint,
   RoadPoint,
-  Arrow,
+  ArrowLine,
+  BasePoint,
   bgColor: "#fff",
 };

+ 36 - 2
src/graphic/CanvasStyle/focus.js

@@ -58,21 +58,55 @@ const CurveRoadEdge = {
   lineWidth: 2,
   strokeStyle: "#3290FF",
 };
-const Arrow = {
+const ArrowLine = {
   lineWidth: 2,
   strokeStyle: "red",
 };
+
+const BasePoint = {
+  strokeStyle: "rgba(0,0,0,0)",
+  fillStyle: "#3290FF",
+  radius: 6,
+  lineWidth: 1,
+  out: {
+    strokeStyle: "#3290FF",
+    fillStyle: "rgba(255,255,255,0)",
+    radius: 8,
+    lineWidth: 1,
+  }
+}
+
+
+const TestPoint = {
+  strokeStyle: "rgba(0,0,0,0)",
+  fillStyle: "#fff",
+  radius: 6,
+  lineWidth: 1,
+  out: {
+    strokeStyle: "#3290FF",
+    fillStyle: "rgba(255,255,255,0)",
+    radius: 8,
+    lineWidth: 3,
+  }
+}
+const BaseLine = {
+  strokeStyle: "#3290FF",
+  lineWidth: 1,
+};
 export default {
   Road,
   Text,
+  BasePoint,
   Point,
-  Arrow,
+  ArrowLine,
   RoadPoint,
   CurveRoadPoint,
   CrossPoint,
   CurveRoad,
+  TestPoint,
   RoadEdge,
   Magnifier,
+  BaseLine,
   NormalLine,
   CurveRoadEdge,
 };

+ 37 - 2
src/graphic/CanvasStyle/select.js

@@ -6,12 +6,16 @@ const Road = {
   strokeStyle: "#3290FF",
 };
 
+const BaseLine = {
+  strokeStyle: "#3290FF",
+  lineWidth: 1,
+};
 const NormalLine = {
   ...def.NormalLine,
   lineWidth: 2,
   strokeStyle: "#3290FF",
 };
-const Arrow = {
+const ArrowLine = {
   lineWidth: 2,
   strokeStyle: "red",
 };
@@ -58,16 +62,47 @@ const CrossPoint = {
   fillStyle: "#3290FF",
 };
 
+const BasePoint = {
+  strokeStyle: "rgba(0,0,0,0)",
+  fillStyle: "#3290FF",
+  radius: 6,
+  lineWidth: 1,
+  out: {
+    strokeStyle: "#3290FF",
+    fillStyle: "rgba(255,255,255,0)",
+    radius: 8,
+    lineWidth: 1,
+  }
+}
+
+const TestPoint = {
+  strokeStyle: "rgba(0,0,0,0)",
+  fillStyle: "#fff",
+  radius: 6,
+  lineWidth: 1,
+  out: {
+    strokeStyle: "#3290FF",
+    fillStyle: "rgba(255,255,255,0)",
+    radius: 8,
+    lineWidth: 3,
+  }
+}
+
+
+
 export default {
   Road,
   Text,
   Point,
+  TestPoint,
   RoadPoint,
   CurveRoadPoint,
   CrossPoint,
   CurveRoad,
+  BasePoint,
+  BaseLine,
   RoadEdge,
   NormalLine,
   CurveRoadEdge,
-  Arrow,
+  ArrowLine,
 };

+ 3 - 0
src/graphic/Constant.js

@@ -36,5 +36,8 @@ const Constant = {
   twoWay: "twoWay", //one表示单向,two表示双向
   defaultSingleLaneWidth: 30, //单个车道的宽度
   defaultMidDivideWidth: 5, //隔离带的宽度
+  angleLocationMode: "AngleLocationMode", //直角定位
+  allLocationMode: "AllLocationMode", //综合定位
+  normalLocationMode: "NormalLocationMode", //自由测量
 };
 export default Constant;

+ 2 - 11
src/graphic/Controls/AddLine.js

@@ -4,13 +4,13 @@ import { listenLayer } from "../ListenLayer";
 import VectorCategory from "../enum/VectorCategory";
 import Point from "../Geometry/Point.js";
 import { mathUtil } from "../Util/MathUtil";
+import Settings from "../Settings";
 
 export default class AddLine {
   constructor() {
     this.newLine = null;
     this.startInfo = {};
     this.baseLineId = null;
-    this.category = VectorCategory.Line.NormalLine;
   }
 
   setPointInfo(pointInfo) {
@@ -38,11 +38,7 @@ export default class AddLine {
       this.newLine == null &&
       !mathUtil.equalPoint(this.startInfo.position, position)
     ) {
-      this.newLine = lineService.create(
-        this.startInfo.position,
-        position,
-        this.category
-      );
+      this.newLine = lineService.create(this.startInfo.position, position);
     }
   }
 
@@ -65,10 +61,6 @@ export default class AddLine {
     }
   }
 
-  setCategory(value) {
-    this.category = value;
-  }
-
   setBaseLineId(baseLineId) {
     this.baseLineId = baseLineId;
   }
@@ -87,7 +79,6 @@ export default class AddLine {
     this.newLine = null;
     this.startInfo = {};
     this.baseLineId = null;
-    this.category = VectorCategory.Line.NormalLine;
   }
 }
 

+ 173 - 46
src/graphic/Controls/AddPoint.js

@@ -5,25 +5,69 @@ import VectorCategory from "../enum/VectorCategory";
 import Point from "../Geometry/Point.js";
 import { mathUtil } from "../Util/MathUtil";
 import addLine from "./AddLine";
+import Settings from "../Settings";
+import { stateService } from "../Service/StateService";
+import VectorType from "../enum/VectorType";
+import Constant from "../Constant";
 
 export default class AddPoint {
-  constructor() {}
+  constructor() {
+    this.basePointIds = []; //所有基准点
+    this.testPointIds = []; //所有待测点
+  }
+
+  buildPoint(position) {
+    const newPoint = pointService.create(position);
+    if (newPoint.getCategory() == VectorCategory.Point.BasePoint) {
+      this.basePointIds.push(newPoint.vectorId);
+    } else {
+      if (Settings.locationMode == Constant.angleLocationMode) {
+        this.setLocationByAngle(newPoint.vectorId);
+      } else if (Settings.locationMode == Constant.allLocationMode) {
+        this.setLocationByAll(newPoint.vectorId);
+      } else if (Settings.locationMode == Constant.normalLocationMode) {
+        this.setLocationByNormal(newPoint.vectorId);
+      }
+      if (newPoint.getCategory() == VectorCategory.Point.TestPoint) {
+        this.testPointIds.push(newPoint.vectorId);
+      }
+    }
+    listenLayer.clear();
+    return newPoint;
+  }
+
+  isFocusBasePoint() {
+    let focusItem = stateService.getFocusItem();
+    if (focusItem && focusItem.type == VectorType.Point) {
+      let point = dataService.getPoint(focusItem.vectorId);
+      if (point.getCategory() == VectorCategory.Point.BasePoint) {
+        return point;
+      }
+    }
+    return null;
+  }
 
   //直角定位法
-  setLocationByAngle(testPointId, basePointId) {
-    if (testPointId == null || basePointId == null) {
-      return null;
+  setLocationByAngle(testPointId) {
+    let basePoint = this.isFocusBasePoint();
+    if (!basePoint) {
+      return;
     }
     let testPoint = dataService.getPoint(testPointId);
-    let basePoint = dataService.getPoint(basePointId);
+    if (testPoint.getCategory() != VectorCategory.Point.TestPoint) {
+      return;
+    }
+    testPoint.setLinkedBasePointId(basePoint.vectorId);
     let lineGeometry = dataService.getLine(addLine.baseLineId);
     let startPoint = dataService.getPoint(lineGeometry.startId);
     let endPoint = dataService.getPoint(lineGeometry.endId);
     let line = mathUtil.createLine1(startPoint, endPoint);
-    let vLine1 = mathUtil.getVerticalLine(line, testPoint);
-    let join = mathUtil.getJoinLinePoint(basePoint, vLine1);
-    join = pointService.create(join, VectorCategory.Point.TestBasePoint);
-
+    let vLine = mathUtil.getVerticalLine(line, testPoint);
+    let join = mathUtil.getJoinLinePoint(basePoint, vLine);
+    join = pointService.create(join);
+    join.setCategory(VectorCategory.Point.TestBasePoint);
+    join.setLinkedBasePointId(basePoint.vectorId);
+    join.setLinkedTestPointId(testPointId);
     lineService.createByPointId(
       testPointId,
       join.vectorId,
@@ -31,53 +75,136 @@ export default class AddPoint {
     );
 
     lineService.createByPointId(
-      basePointId,
+      basePoint.vectorId,
       join.vectorId,
       VectorCategory.Line.PositionLine
     );
   }
 
+  //待测基准点,待测点与基准线相交的点
+  getTestBasePoint(basePointId, testPointId) {
+    let points = dataService.getPoints();
+    for (let key in points) {
+      const point = dataService.getPoint(key);
+      if (point.getCategory() == VectorCategory.Point.TestBasePoint) {
+        if (
+          point.getLinkedBasePointId() == basePointId &&
+          point.getLinkedTestPointId() == testPointId
+        ) {
+          return point;
+        }
+      }
+    }
+    return null;
+  }
+
+  //更新待测点(直角定位法)
+  updateTestPointByAngle(testPointId, newPosition) {
+    let testPoint = dataService.getPoint(testPointId);
+    mathUtil.clonePoint(testPoint, newPosition);
+    let basePoint = dataService.getPoint(testPoint.getLinkedBasePointId());
+    let lineGeometry = dataService.getLine(addLine.baseLineId);
+    let startPoint = dataService.getPoint(lineGeometry.startId);
+    let endPoint = dataService.getPoint(lineGeometry.endId);
+    let line = mathUtil.createLine1(startPoint, endPoint);
+    let vLine = mathUtil.getVerticalLine(line, testPoint);
+    let join = mathUtil.getJoinLinePoint(basePoint, vLine);
+    let testBasePoint = this.getTestBasePoint(basePoint.vectorId, testPointId);
+    mathUtil.clonePoint(testBasePoint, join);
+  }
+
   //综合定位法
-  setLocationByFull(testPointId1, testPointId2, basePointId) {
-    if (testPointId1 == null || basePointId == null) {
-      return null;
+  setLocationByAll(testPointId) {
+    let basePoint = this.isFocusBasePoint();
+    if (!basePoint) {
+      return;
+    }
+
+    let testPoint = dataService.getPoint(testPointId);
+    testPoint.setLinkedBasePointId(basePoint.vectorId);
+    let lineGeometry = dataService.getLine(addLine.baseLineId);
+    let startPoint = dataService.getPoint(lineGeometry.startId);
+    let endPoint = dataService.getPoint(lineGeometry.endId);
+    let line = mathUtil.createLine1(startPoint, endPoint);
+    let join = mathUtil.getJoinLinePoint(testPoint, line);
+    join = pointService.create(join); //经过待测点且与基准线垂直的线段,与基准线的交点
+    join.setCategory(VectorCategory.Point.TestBasePoint);
+    join.setLinkedBasePointId(basePoint.vectorId);
+    join.setLinkedTestPointId(testPointId);
+    //待测点与基准线的垂直线
+    lineService.createByPointId(
+      testPointId,
+      join.vectorId,
+      VectorCategory.Line.PositionLine
+    );
+    //暂时没有其他待测点
+    if (this.testPointIds.length == 0) {
+      //待测点与基准线点的连线
+      lineService.createByPointId(
+        basePoint.vectorId,
+        testPointId,
+        VectorCategory.Line.PositionLine
+      );
     } else {
-      let testPoint1 = dataService.getPoint(testPointId1);
-      let lineGeometry = dataService.getLine(addLine.baseLineId);
-      let startPoint = dataService.getPoint(lineGeometry.startId);
-      let endPoint = dataService.getPoint(lineGeometry.endId);
-      let line = mathUtil.createLine1(startPoint, endPoint);
-      if (testPointId2 == null) {
-        let join = mathUtil.getJoinLinePoint(testPoint1, line);
-        join = pointService.create(join, VectorCategory.Point.TestBasePoint);
-        lineService.createByPointId(
-          testPointId1,
-          join.vectorId,
-          VectorCategory.Line.PositionLine
-        );
+      //取上一个待测点
+      lineService.createByPointId(
+        this.testPointIds[this.testPointIds.length - 1],
+        testPointId,
+        VectorCategory.Line.PositionLine
+      );
+      testPoint.setLinkedTestPointId(
+        this.testPointIds[this.testPointIds.length - 1]
+      );
+    }
+  }
 
-        lineService.createByPointId(
-          basePointId,
-          testPointId1,
-          VectorCategory.Line.PositionLine
-        );
-      } else {
-        let testPoint2 = dataService.getPoint(testPointId2);
-        let join = mathUtil.getJoinLinePoint(testPoint2, line);
-        join = pointService.create(join, VectorCategory.Point.TestBasePoint);
-        lineService.createByPointId(
-          testPointId2,
-          join.vectorId,
-          VectorCategory.Line.PositionLine
-        );
+  //更新待测点(综合定位法)
+  updateTestPointByAll(testPointId, newPosition) {
+    let testPoint = dataService.getPoint(testPointId);
+    mathUtil.clonePoint(testPoint, newPosition);
+    let basePoint = dataService.getPoint(testPoint.getLinkedBasePointId());
+    let lineGeometry = dataService.getLine(addLine.baseLineId);
+    let startPoint = dataService.getPoint(lineGeometry.startId);
+    let endPoint = dataService.getPoint(lineGeometry.endId);
+    let line = mathUtil.createLine1(startPoint, endPoint);
+    let join = getJoinLinePoint(testPoint, line);
+    let testBasePoint = this.getTestBasePoint(basePoint.vectorId, testPointId);
+    mathUtil.clonePoint(testBasePoint, join);
+  }
 
-        lineService.createByPointId(
-          testPointId2,
-          testPointId1,
-          VectorCategory.Line.PositionLine
-        );
-      }
+  setLocationByNormal(testPointId) {
+    let testPoint = dataService.getPoint(testPointId);
+    if (testPoint.getCategory() != VectorCategory.Point.TestPoint) {
+      return;
     }
+    let lineGeometry = dataService.getLine(addLine.baseLineId);
+    let startPoint = dataService.getPoint(lineGeometry.startId);
+    let endPoint = dataService.getPoint(lineGeometry.endId);
+    let line = mathUtil.createLine1(startPoint, endPoint);
+    let vLine = mathUtil.getVerticalLine(line, testPoint);
+    let join = mathUtil.getIntersectionPoint(vLine, line);
+    join = pointService.create(join);
+    join.setCategory(VectorCategory.Point.TestBasePoint);
+    join.setLinkedTestPointId(testPointId);
+    lineService.createByPointId(
+      testPointId,
+      join.vectorId,
+      VectorCategory.Line.PositionLine
+    );
+  }
+
+  //更新待测点(自由定位法)
+  updateTestPointByNormal(testPointId, newPosition) {
+    let testPoint = dataService.getPoint(testPointId);
+    mathUtil.clonePoint(testPoint, newPosition);
+    let basePoint = dataService.getPoint(testPoint.getLinkedBasePointId());
+    let lineGeometry = dataService.getLine(addLine.baseLineId);
+    let startPoint = dataService.getPoint(lineGeometry.startId);
+    let endPoint = dataService.getPoint(lineGeometry.endId);
+    let line = mathUtil.createLine1(startPoint, endPoint);
+    let join = getJoinLinePoint(testPoint, line);
+    let testBasePoint = this.getTestBasePoint(basePoint.vectorId, testPointId);
+    mathUtil.clonePoint(testBasePoint, join);
   }
 }
 

+ 0 - 6
src/graphic/Controls/MoveLine.js

@@ -3,12 +3,6 @@ import { dataService } from "../Service/DataService";
 export default class MoveLine {
   constructor() {}
 
-  movePoint(position, pointId) {
-    let point = dataService.getPoint(pointId);
-    point.x = position.x;
-    point.y = position.y;
-  }
-
   moveLine(lineId, dx, dy) {
     dx = dx;
     dy = -dy;

+ 86 - 0
src/graphic/Controls/MovePoint.js

@@ -0,0 +1,86 @@
+import { dataService } from "../Service/DataService";
+
+export default class MovePoint {
+  constructor() {}
+
+  movePoint(position, pointId) {
+    let point = dataService.getPoint(pointId);
+    point.x = position.x;
+    point.y = position.y;
+  }
+
+  //直角定位法
+  //movePointForLocationByAngle(testPointId, basePointId) {
+  movePointByAngleLocation(pointId) {
+    if (testPointId == null || basePointId == null) {
+      return null;
+    }
+    let testPoint = dataService.getPoint(testPointId);
+    let basePoint = dataService.getPoint(basePointId);
+    let lineGeometry = dataService.getLine(addLine.baseLineId);
+    let startPoint = dataService.getPoint(lineGeometry.startId);
+    let endPoint = dataService.getPoint(lineGeometry.endId);
+    let line = mathUtil.createLine1(startPoint, endPoint);
+    let vLine1 = mathUtil.getVerticalLine(line, testPoint);
+    let join = mathUtil.getJoinLinePoint(basePoint, vLine1);
+    join = pointService.create(join);
+
+    lineService.createByPointId(
+      testPointId,
+      join.vectorId,
+      VectorCategory.Line.PositionLine
+    );
+
+    lineService.createByPointId(
+      basePointId,
+      join.vectorId,
+      VectorCategory.Line.PositionLine
+    );
+  }
+
+  //综合定位法
+  movePointForLocationByFull(testPointId1, testPointId2, basePointId) {
+    if (testPointId1 == null || basePointId == null) {
+      return null;
+    } else {
+      let testPoint1 = dataService.getPoint(testPointId1);
+      let lineGeometry = dataService.getLine(addLine.baseLineId);
+      let startPoint = dataService.getPoint(lineGeometry.startId);
+      let endPoint = dataService.getPoint(lineGeometry.endId);
+      let line = mathUtil.createLine1(startPoint, endPoint);
+      if (testPointId2 == null) {
+        let join = mathUtil.getJoinLinePoint(testPoint1, line);
+        join = pointService.create(join);
+        lineService.createByPointId(
+          testPointId1,
+          join.vectorId,
+          VectorCategory.Line.PositionLine
+        );
+
+        lineService.createByPointId(
+          basePointId,
+          testPointId1,
+          VectorCategory.Line.PositionLine
+        );
+      } else {
+        let testPoint2 = dataService.getPoint(testPointId2);
+        let join = mathUtil.getJoinLinePoint(testPoint2, line);
+        join = pointService.create(join);
+        lineService.createByPointId(
+          testPointId2,
+          join.vectorId,
+          VectorCategory.Line.PositionLine
+        );
+
+        lineService.createByPointId(
+          testPointId2,
+          testPointId1,
+          VectorCategory.Line.PositionLine
+        );
+      }
+    }
+  }
+}
+
+const movePoint = new MovePoint();
+export { movePoint };

+ 65 - 16
src/graphic/Controls/UIControl.js

@@ -8,17 +8,22 @@ import { uiService } from "../Service/UIService.js";
 import { dataService } from "../Service/DataService.js";
 import { historyService } from "../Service/HistoryService.js";
 import { elementService } from "../Service/ElementService";
+import { lineService } from "../Service/LineService.js";
+import { circleService } from "../Service/CircleService.js";
+import { textService } from "../Service/TextService.js";
+import { magnifierService } from "../Service/MagnifierService.js";
 import { mathUtil } from "../Util/MathUtil";
-import { textService } from "../Service/TextService.js/";
 import Constant from "../Constant";
 // import { roomsUtil } from "../Room/RoomsUtil.js";
 import { addRoad } from "../Controls/AddRoad";
 import { addLine } from "./AddLine.js";
 import VectorCategory from "../enum/VectorCategory.js";
 // import { floorplanData } from "../VectorData.js";
+import Message from "@/components/base/components/message/message.vue";
 
 export default class UIControl {
   constructor(layer, newsletter, graphicStateUI) {
+    this._prompts = [];
     this.layer = layer;
     this.newsletter = newsletter;
     this.graphicStateUI = graphicStateUI;
@@ -53,6 +58,7 @@ export default class UIControl {
   }
 
   clearUI() {
+    this.currentUI = null;
     this.selectUI = null;
   }
 
@@ -74,14 +80,10 @@ export default class UIControl {
           stateService.setEventName(LayerEvents.AddRoad);
         } else if (selectUI == UIEvents.CurveRoad) {
           stateService.setEventName(LayerEvents.AddCurveRoad);
-        } else if (selectUI == UIEvents.Arrow) {
-          stateService.setEventName(LayerEvents.AddLine);
-          addLine.setCategory(VectorCategory.Line.ArrowLine);
-        } else if (selectUI == UIEvents.MeasureLine) {
-          stateService.setEventName(LayerEvents.AddLine);
-          addLine.setCategory(VectorCategory.Line.MeasureLine);
-        } else if (selectUI == UIEvents.Line) {
+        } else if (uiService.isBelongLine(selectUI)) {
           stateService.setEventName(LayerEvents.AddLine);
+        } else if (uiService.isBelongPoint(selectUI)) {
+          stateService.setEventName(LayerEvents.AddPoint);
         } else if (selectUI == UIEvents.Circle) {
           stateService.setEventName(LayerEvents.AddCircle);
         } else if (selectUI == UIEvents.Text) {
@@ -103,11 +105,11 @@ export default class UIControl {
     if (item && item.vectorId) {
       switch (action) {
         case GeoActions.CopyAction:
-          await dataService.copyVector(item.vectorId, item.type);
+          await this.copyVector(item.vectorId, item.type);
           needAutoRedraw = true;
           break;
         case GeoActions.DeleteAction:
-          dataService.deleteVector(item.vectorId, item.type);
+          this.deleteVector(item.vectorId, item.type);
           needAutoRedraw = true;
           break;
       }
@@ -119,6 +121,41 @@ export default class UIControl {
     }
   }
 
+  //删除按钮
+  deleteVector(vectorId, geoType) {
+    switch (geoType) {
+      case VectorType.Line:
+        dataService.deleteLine(vectorId);
+        break;
+      case VectorType.Circle:
+        dataService.deleteCircle(vectorId);
+        break;
+      case VectorType.Text:
+        dataService.deleteText(vectorId);
+        break;
+      case VectorType.Magnifier:
+        dataService.deleteMagnifier(vectorId);
+        break;
+    }
+  }
+
+  //复制按钮
+  async copyVector(vectorId, geoType) {
+    switch (geoType) {
+      case VectorType.Line:
+        lineService.copy(vectorId);
+        break;
+      case VectorType.Circle:
+        circleService.copy(vectorId);
+        break;
+      case VectorType.Text:
+        textService.copy(vectorId);
+        break;
+      case VectorType.Magnifier:
+        await magnifierService.copy(vectorId);
+        break;
+    }
+  }
 
   //截图
   async screenShot() {
@@ -127,31 +164,31 @@ export default class UIControl {
     dataService.setGridDisplay(false);
     this.layer.renderer.autoRedraw();
     // this.downloadCadImg(canvas, "test.jpg");
-    const blob = await this.getCadBlob(canvas)
+    const blob = await this.getCadBlob(canvas);
     //显示grid
     dataService.setGridDisplay(true);
     this.layer.renderer.autoRedraw();
 
-    return blob
+    return blob;
   }
 
   getCadBlob(canvas) {
     var type = "jpg";
-    return new Promise(resolve => canvas.toBlob(resolve, `${type}/image`))
+    return new Promise((resolve) => canvas.toBlob(resolve, `${type}/image`));
   }
 
   downloadCadImg(canvas, filename) {
     // 图片导出为 jpg 格式
     var type = "jpg";
     var imgData = canvas.toDataURL(type, 3);
-    canvas.toBlob(`${type}/image`)
+    canvas.toBlob(`${type}/image`);
 
     // 加工image data,替换mime type
     imgData = imgData.replace(this._fixType(type), "image/octet-stream");
     // 下载后的图片名
     //var filename = 'cad_' + new Date().getTime() + '.' + type
     // download
-    debugger
+    debugger;
     this.saveFile(imgData, filename);
   }
 
@@ -222,10 +259,12 @@ export default class UIControl {
 
   // value 为true则开 false则关
   menu_backgroundImg(value) {
-    console.log(value)
+    console.log(value);
     //
     const backgroundImg = dataService.getBackgroundImg();
     backgroundImg.setDisplay(value);
+
+    this.graphicStateUI.showBackImage = value;
     this.layer.renderer.autoRedraw();
   }
 
@@ -237,4 +276,14 @@ export default class UIControl {
   }
 
   /******************************************************************************************************************************************************************/
+
+  prompt(msg) {
+    this._prompts.push(Message.success({ msg }));
+  }
+
+  hidePrompt() {
+    for (let prompt of this._prompts) {
+      prompt();
+    }
+  }
 }

+ 14 - 3
src/graphic/Geometry/Line.js

@@ -4,14 +4,15 @@ import SelectState from "../enum/SelectState.js";
 import Geometry from "./Geometry";
 import Constant from "../Constant.js";
 import Style from "@/graphic/CanvasStyle/index.js";
+import Settings from "../Settings";
 
 export default class Line extends Geometry {
   constructor(startId, endId, vectorId) {
     super();
     this.startId = startId;
     this.endId = endId;
-    this.category = VectorCategory.Line.NormalLine;
-    this.arrowColor = Style.Arrow.strokeStyle; //箭头类型会用到
+    this.category = Settings.lineCategory;
+    this.arrowColor = Style.ArrowLine.strokeStyle; //箭头类型会用到
     this.geoType = VectorType.Line;
     this.setId(vectorId);
   }
@@ -19,7 +20,7 @@ export default class Line extends Geometry {
   //NormalLine,GuideLine,MeasureLine,BaseLine
   setCategory(value) {
     if (!value) {
-      this.category = VectorCategory.Line.NormalLine;
+      this.category = Settings.lineCategory;
     } else {
     }
     this.category = value;
@@ -32,4 +33,14 @@ export default class Line extends Geometry {
   setArrowColor(value) {
     this.arrowColor = value;
   }
+
+  getDir(pointId) {
+    if (this.startId == pointId) {
+      return "start";
+    } else if (this.endId == pointId) {
+      return "end";
+    } else {
+      return null;
+    }
+  }
 }

+ 21 - 2
src/graphic/Geometry/Point.js

@@ -1,6 +1,7 @@
 import VectorType from "../enum/VectorType.js";
 import VectorCategory from "../enum/VectorCategory.js";
 import Geometry from "./Geometry";
+import Settings from "../Settings";
 
 export default class Point extends Geometry {
   constructor(position, vectorId) {
@@ -8,7 +9,9 @@ export default class Point extends Geometry {
     this.x = null;
     this.y = null;
     this.parent = {};
-    this.category = VectorCategory.Point.NormalPoint;
+    this.linkedBasePointId = null; //关联基准点
+    this.linkedTestPointId = null; //关联待测点
+    this.category = Settings.pointCategory;
     this.geoType = VectorType.Point;
     this.setId(vectorId);
 
@@ -24,10 +27,26 @@ export default class Point extends Geometry {
     return this.category;
   }
 
+  getLinkedBasePointId() {
+    return this.linkedBasePointId;
+  }
+
+  setLinkedBasePointId(pointId) {
+    this.linkedBasePointId = pointId;
+  }
+
+  setLinkedTestPointId(pointId) {
+    this.linkedTestPointId = pointId;
+  }
+
+  getLinkedTestPointId() {
+    return this.linkedTestPointId;
+  }
+
   //基准点:BasePoint
   setCategory(value) {
     if (!value) {
-      this.category = VectorCategory.Point.NormalPoint;
+      this.category = Settings.pointCategory;
     } else {
       this.category = value;
     }

+ 13 - 20
src/graphic/History/History.js

@@ -119,11 +119,8 @@ export default class History {
       if (item.handle == HistoryEvents.AddPoint) {
         dataService.deletePoint(item.point.id);
       } else if (item.handle == HistoryEvents.DeletePoint) {
-        let point = pointService.create(
-          item.point,
-          item.category,
-          item.point.id
-        );
+        let point = pointService.create(item.point, item.point.id);
+        point.setCategory(item.category);
         point.parent = JSON.parse(JSON.stringify(item.point.parent));
       } else if (item.handle == HistoryEvents.ModifyPoint) {
         const prePoint = item.prePoint;
@@ -234,7 +231,7 @@ export default class History {
     for (let i = 0; i < itemForRoadPoints.length; ++i) {
       const item = itemForRoadPoints[i];
       if (item.handle == HistoryEvents.AddRoadPoint) {
-        dataService.deleteRoadPoint(item.roadPoint.id);
+        dataService.deleteRoadPoint1(item.roadPoint.id);
       } else if (item.handle == HistoryEvents.DeleteRoadPoint) {
         let newRoadPoint = roadPointService.create(
           item.roadPoint.position,
@@ -279,7 +276,7 @@ export default class History {
       if (item.handle == HistoryEvents.AddRoad) {
         dataService.deleteRoad(item.road.id);
       } else if (item.handle == HistoryEvents.DeleteRoad) {
-        let newRoad = roadService.create(
+        let newRoad = roadService.createOnlyRoad(
           item.road.startId,
           item.road.endId,
           item.road.id
@@ -297,10 +294,9 @@ export default class History {
     for (let i = 0; i < itemForCrossPoints.length; ++i) {
       const item = itemForCrossPoints[i];
       if (item.handle == HistoryEvents.AddCrossPoint) {
-        dataService.deleteCrossPoint(item.crossPoint.id);
+        dataService.deleteCrossPoint1(item.crossPoint.id);
       } else if (item.handle == HistoryEvents.DeleteCrossPoint) {
-        crossPointService.create;
-        let newCrossPoint = roadService.create(
+        let newCrossPoint = crossPointService.create(
           item.crossPoint.position,
           item.crossPoint.id
         );
@@ -310,7 +306,7 @@ export default class History {
         );
       } else if (item.handle == HistoryEvents.ModifyCrossPoint) {
         const preCrossPoint = item.preCrossPoint;
-        let currentCrossPoint = dataService.getCrossPoint2(
+        let currentCrossPoint = dataService.getCrossPoint3(
           item.curCrossPoint.id
         );
         historyUtil.assignCrossPointFromCrossPoint(
@@ -325,11 +321,8 @@ export default class History {
     for (let i = 0; i < itemForPoints.length; ++i) {
       const item = itemForPoints[i];
       if (item.handle == HistoryEvents.AddPoint) {
-        let newPoint = pointService.create(
-          item.point,
-          item.category,
-          item.point.id
-        );
+        let newPoint = pointService.create(item.point, item.point.id);
+        newPoint.setCategory(item.category);
         historyUtil.assignPointFromPoint(newPoint, item.point);
       } else if (item.handle == HistoryEvents.DeletePoint) {
         dataService.deletePoint(item.point.id);
@@ -448,7 +441,7 @@ export default class History {
         );
         historyUtil.assignRoadPointFromRoadPoint(vRoadPoint, item.roadPoint);
       } else if (item.handle == HistoryEvents.DeleteRoadPoint) {
-        dataService.deleteRoadPoint(item.roadPoint.id);
+        dataService.deleteRoadPoint1(item.roadPoint.id);
       } else if (item.handle == HistoryEvents.ModifyRoadPoint) {
         const currentRoadPoint = item.curRoadPoint;
         let preRoadPoint = dataService.getRoadPoint(item.curRoadPoint.id);
@@ -485,7 +478,7 @@ export default class History {
     for (let i = 0; i < itemForRoads.length; ++i) {
       const item = itemForRoads[i];
       if (item.handle == HistoryEvents.AddRoad) {
-        let vRoad = roadService.create(
+        let vRoad = roadService.createOnlyRoad(
           item.road.startId,
           item.road.endId,
           item.road.id
@@ -514,10 +507,10 @@ export default class History {
           item.crossPoint
         );
       } else if (item.handle == HistoryEvents.DeleteCrossPoint) {
-        dataService.deleteCrossPoint(item.crossPoint.id);
+        dataService.deleteCrossPoint1(item.crossPoint.id);
       } else if (item.handle == HistoryEvents.ModifyCrossPoint) {
         const currentCrossPoint = item.curCrossPoint;
-        let preCrossPoint = dataService.getCrossPoint2(item.curCrossPoint.id);
+        let preCrossPoint = dataService.getCrossPoint3(item.curCrossPoint.id);
         historyUtil.assignCrossPointFromCrossPoint(
           preCrossPoint,
           currentCrossPoint

+ 19 - 3
src/graphic/History/HistoryUtil.js

@@ -19,7 +19,9 @@ export default class HistoryUtil {
       point1.x == point2.x &&
       point1.y == point2.y &&
       JSON.stringify(point1.parent) == JSON.stringify(point2.parent) &&
-      point1.category == point2.category
+      point1.category == point2.category &&
+      point1.linkedBasePointId == point2.linkedBasePointId &&
+      point1.linkedTestPointId == point2.linkedTestPointId
     ) {
       return false;
     } else {
@@ -164,6 +166,8 @@ export default class HistoryUtil {
     pointInfo.vectorId = point1.vectorId;
     pointInfo.position = { x: point2.x, y: point2.y };
     pointInfo.parent = JSON.parse(JSON.stringify(point2.parent));
+    pointInfo.linkedBasePointId = point2.linkedBasePointId;
+    pointInfo.linkedTestPointId = point2.linkedTestPointId;
     pointInfo.category = point2.category;
     this.setPointInfo(pointInfo);
   }
@@ -261,13 +265,19 @@ export default class HistoryUtil {
   assignCrossPointFromCrossPoint(crossPoint1, crossPoint2) {
     const crossPointInfo = {};
     crossPointInfo.vectorId = crossPoint1.vectorId;
-    crossPointInfo.position = { x: crossPoint2.x, y: crossPoint2.y };
+    crossPointInfo.position = {
+      x: crossPoint2.position.x,
+      y: crossPoint2.position.y,
+    };
     crossPointInfo.edgeInfo1 = JSON.parse(
       JSON.stringify(crossPoint2.edgeInfo1)
     );
     crossPointInfo.edgeInfo2 = JSON.parse(
       JSON.stringify(crossPoint2.edgeInfo2)
     );
+    crossPoint1.edgeInfo1 = JSON.parse(JSON.stringify(crossPoint2.edgeInfo1));
+    crossPoint1.edgeInfo2 = JSON.parse(JSON.stringify(crossPoint2.edgeInfo2));
+    dataService.addCrossPoint(crossPoint1);
     crossPointInfo.extremePoint = {
       x: crossPoint2.extremePoint.x,
       y: crossPoint2.extremePoint.y,
@@ -282,6 +292,8 @@ export default class HistoryUtil {
     mathUtil.clonePoint(data, point);
     data.parent = JSON.parse(JSON.stringify(point.parent));
     data.category = point.category;
+    data.linkedBasePointId = point.linkedBasePointId;
+    data.linkedTestPointId = point.linkedTestPointId;
     data.type = point.geoType;
     return data;
   }
@@ -398,6 +410,7 @@ export default class HistoryUtil {
 
     data.edgeInfo1 = JSON.parse(JSON.stringify(crossPoint.edgeInfo1));
     data.edgeInfo2 = JSON.parse(JSON.stringify(crossPoint.edgeInfo2));
+    data.curves = JSON.parse(JSON.stringify(crossPoint.curves));
     return data;
   }
 
@@ -406,6 +419,8 @@ export default class HistoryUtil {
     mathUtil.clonePoint(point, pointInfo.position);
     point.parent = JSON.parse(JSON.stringify(pointInfo.parent));
     point.category = pointInfo.category;
+    point.linkedBasePointId = pointInfo.linkedBasePointId;
+    point.linkedTestPointId = pointInfo.linkedTestPointId;
     return point;
   }
 
@@ -489,9 +504,10 @@ export default class HistoryUtil {
   }
 
   setCrossPointInfo(crossPointInfo) {
-    let crossPoint = dataService.getCrossPoint2(crossPointInfo.vectorId);
+    let crossPoint = dataService.getCrossPoint3(crossPointInfo.vectorId);
     crossPoint.vectorId = crossPointInfo.vectorId;
     mathUtil.clonePoint(crossPoint, crossPointInfo.position);
+    crossPoint.extremePoint = {};
     mathUtil.clonePoint(crossPoint.extremePoint, crossPointInfo.extremePoint);
     crossPoint.edgeInfo1 = JSON.parse(JSON.stringify(crossPointInfo.edgeInfo1));
     crossPoint.edgeInfo2 = JSON.parse(JSON.stringify(crossPointInfo.edgeInfo2));

+ 30 - 4
src/graphic/Layer.js

@@ -11,12 +11,14 @@ import { moveSVG } from "./Controls/MoveSVG";
 import { moveMagnifier } from "./Controls/MoveMagnifier";
 import { addRoad } from "./Controls/AddRoad";
 import { addLine } from "./Controls/AddLine";
+import { addPoint } from "./Controls/AddPoint";
 import { addCircle } from "./Controls/AddCircle";
 import { addText } from "./Controls/AddText";
 import { addMagnifier } from "./Controls/AddMagnifier";
 import { addSVG } from "./Controls/AddSVG";
 import { moveRoad } from "./Controls/MoveRoad";
 import { moveLine } from "./Controls/MoveLine";
+import { movePoint } from "./Controls/MovePoint";
 import { moveCircle } from "./Controls/MoveCircle";
 import { coordinate } from "./Coordinate";
 import Render from "./Renderer/Render";
@@ -34,8 +36,11 @@ import { edgeService } from "./Service/EdgeService";
 import { roadPointService } from "./Service/RoadPointService";
 import { curveRoadService } from "./Service/CurveRoadService";
 import VectorCategory from "./enum/VectorCategory";
+import Settings from "./Settings";
 
 const minDragDis = 10;
+const minZoom = 20;
+const maxZoom = 800;
 
 export default class Layer {
   constructor(canvas, newsletter, graphicState) {
@@ -112,8 +117,6 @@ export default class Layer {
     // 右键
     if (e.button == 2) {
       this.stopAddVector();
-      this.uiControl.currentUI = null;
-      this.renderer.autoRedraw();
       return;
     }
     this.dragging = false;
@@ -134,6 +137,15 @@ export default class Layer {
         stateService.setEventName(LayerEvents.AddingLine);
         addLine.setNewLinePoint(position);
         break;
+      case LayerEvents.AddPoint:
+        stateService.setEventName(LayerEvents.MovePoint);
+        const newPoint = addPoint.buildPoint(position);
+        stateService.setSelectItem(
+          newPoint.vectorId,
+          VectorType.Point,
+          SelectState.Select
+        );
+        break;
       case LayerEvents.AddCircle:
         stateService.setEventName(LayerEvents.AddingCircle);
         addCircle.setCenter(position);
@@ -566,7 +578,7 @@ export default class Layer {
               y: listenLayer.modifyPoint.y,
             };
           }
-          moveLine.movePoint(position, draggingItem.vectorId);
+          movePoint.movePoint(position, draggingItem.vectorId);
           needAutoRedraw = true;
         }
         break;
@@ -832,7 +844,7 @@ export default class Layer {
   }
 
   zoomVector(zoom) {
-    if (zoom < 14) {
+    if (zoom < minZoom || zoom > maxZoom) {
       return;
     }
 
@@ -1094,5 +1106,19 @@ export default class Layer {
     addLine.clear(); //之前会保留category
     this.uiControl.clearUI();
     elementService.hideAll();
+    this.renderer.autoRedraw();
+  }
+
+  //更新定位信息
+  updateForLocation() {
+    if (
+      addLine.newLine &&
+      addLine.newLine.getCategory() == VectorCategory.Line.BaseLine
+    ) {
+      Settings.baseLineId = addLine.newLine.vectorId;
+      this.uiControl.graphicStateUI.canAngleLocationMode = true;
+      this.uiControl.graphicStateUI.canAllLocationMode = true;
+      this.uiControl.graphicState.existsBaseLine = true;
+    }
   }
 }

+ 199 - 32
src/graphic/Renderer/Draw.js

@@ -7,6 +7,7 @@ import { mathUtil } from "../Util/MathUtil.js";
 import ElementEvents from "../enum/ElementEvents.js";
 import { elementService } from "../Service/ElementService.js";
 import UIEvents from "@/graphic/enum/UIEvents.js";
+import VectorCategory from "@/graphic/enum/VectorCategory.js";
 
 const imgCache = {};
 const help = {
@@ -30,9 +31,12 @@ const help = {
           // item.type === VectorType[geoType] &&
           geoId === item.vectorId
         ) {
-          if (Style[attr] && Style[attr][geoType]) {
-            currentAttr = attr;
-            return Style[attr][geoType];
+          if (Style[attr]) {
+            const style = Style[attr][geoType] || Style[attr][item.category]
+            if (style) {
+              currentAttr = attr;
+              return style;
+            }
           }
         }
         return prev;
@@ -40,8 +44,7 @@ const help = {
       currentAttr,
     ];
   },
-  setVectorStyle(ctx, vector, geoType = vector.geoType) {
-    const [styles, attr] = help.getVectorStyle(vector, geoType);
+  setStyle(ctx, styles) {
     for (const style in styles) {
       if (typeof styles[style] === "function") {
         styles[style](ctx, vector);
@@ -49,6 +52,20 @@ const help = {
         ctx[style] = styles[style];
       }
     }
+  },
+  setVectorStyle(ctx, vector, geoType = vector.geoType) {
+    let styles, attr
+    if (Array.isArray(geoType)) {
+      for (const type of geoType) {
+        [styles, attr] = help.getVectorStyle(vector, type);
+        if (styles) {
+          break;
+        }
+      }
+    } else {
+      [styles, attr] = help.getVectorStyle(vector, geoType);
+    }
+    help.setStyle(ctx, styles)
     return [styles, attr];
   },
   transformCoves(lines) {
@@ -99,6 +116,86 @@ const help = {
       };
     }));
   },
+  getTextCenter(ctx, txt) {
+    const text = ctx.measureText(txt);
+    const height = text.actualBoundingBoxAscent + text.actualBoundingBoxDescent
+    return {
+      width: text.width,
+      height,
+      x: text.width / 2,
+      y: -height / 2
+    }
+  },
+  // 绘制圆角矩形
+  roundRect(ctx, x, y, width, height, radius) {
+    ctx.beginPath();
+    ctx.moveTo(x + radius, y);
+    ctx.lineTo(x + width - radius, y);
+    ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
+    ctx.lineTo(x + width, y + height - radius);
+    ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
+    ctx.lineTo(x + radius, y + height);
+    ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
+    ctx.lineTo(x, y + radius);
+    ctx.quadraticCurveTo(x, y, x + radius, y);
+    ctx.closePath();
+  },
+  getRealDistance(p1, p2) {
+    return Math.round(mathUtil.getDistance(p1, p2) * 100) / 100
+  },
+  getPerpendicularPoint(p1, p2, p3, d) {
+    if (p1.x === p2.x) {
+      return {x: p3.x + d, y: p3.y}
+    } else if (p1.y === p2.y) {
+      return {x: p3.x, y: p3.y + d}
+    }
+
+    // 计算通过 p1 和 p2 的直线的斜率和截距
+    const slope = (p2.y - p1.y) / (p2.x - p1.x);
+    const intercept = p1.y - slope * p1.x;
+
+    // 计算垂直线的斜率和截距
+    const perpendicularSlope = -1 / slope;
+    const perpendicularIntercept = p3.y - perpendicularSlope * p3.x;
+
+    // 计算垂足点 p0
+    const x = (perpendicularIntercept - intercept) / (slope - perpendicularSlope);
+    const y = slope * x + intercept;
+    const p0 = { x, y };
+
+// 计算点 p4
+    const distance = d; // 指定距离
+    const dx = distance / Math.sqrt(1 + perpendicularSlope ** 2);
+    const dy = perpendicularSlope * dx;
+    return { x: p0.x + dx, y: p0.y + dy };
+  },
+  drawLineText(ctx, start, end, text, style) {
+    if (start.x > end.x) {
+      [start, end] = [end, start]
+    }
+
+    const angle = Math.atan2(end.y - start.y, end.x - start.x) * 180 / Math.PI;
+    const center = mathUtil.lineCenter(start, end)
+
+    ctx.save();
+    ctx.translate(center.x, center.y);
+    ctx.rotate(angle * Math.PI / 180);
+    const textCenter = help.getTextCenter(ctx, text)
+    const padding = style.padding;
+    help.roundRect(
+      ctx,
+      -textCenter.x - padding,
+      textCenter.y - padding,
+      textCenter.width + 2 * padding,
+      textCenter.height + 2 * padding,
+      (textCenter.height / 2) + padding
+    )
+    ctx.fillStyle = style.backColor
+    ctx.fill()
+    ctx.fillStyle = style.fillColor
+    ctx.fillText(text, -textCenter.x, -textCenter.y);
+    ctx.restore();
+  }
 };
 
 export default class Draw {
@@ -127,6 +224,9 @@ export default class Draw {
   }
 
   drawBackGroundImg(vector) {
+    if (!vector.display) {
+      return;
+    }
     const img = vector.imageData;
     const width = help.getReal(img.width);
     const height = help.getReal(img.height);
@@ -404,13 +504,10 @@ export default class Draw {
 
     ctx.save();
 
-    const [style] = help.setVectorStyle(this.context, vector, "Arrow");
+    const [style] = help.setVectorStyle(this.context, vector);
     if (vector.arrowColor) {
       ctx.strokeStyle = vector.arrowColor;
     }
-    ctx.beginPath();
-    ctx.moveTo(start.x, start.y);
-    ctx.lineTo(end.x, end.y);
 
     const dires =
       vector.category === UIEvents.MeasureLine
@@ -427,11 +524,6 @@ export default class Draw {
     }
     ctx.stroke();
     ctx.restore();
-
-    if ([Style.Focus.Arrow, Style.Select.Arrow].includes(style)) {
-      this.drawPoint(startReal);
-      this.drawPoint(endReal);
-    }
   }
 
   drawMagnifier(vector) {
@@ -520,17 +612,29 @@ export default class Draw {
   drawPoint(vector) {
     const pt = coordinate.getScreenXY({ x: vector.x, y: vector.y });
     const ctx = this.context;
-    const [style] = help.setVectorStyle(ctx, vector, vector.geoType || "Point");
+    const [style] = help.setVectorStyle(
+      ctx,
+      vector,
+      [vector.category, vector.geoType, "Point"]
+
+    );
     if (vector.color) {
       ctx.strokeStyle = vector.color;
     }
-    const radius = help.getReal(vector.radius || style.radius);
-    ctx.save();
-    ctx.beginPath();
-    ctx.arc(pt.x, pt.y, radius, 0, Math.PI * 2, true);
-    ctx.stroke();
-    ctx.fill();
-    ctx.restore();
+    const draw = (style) => {
+      const radius = help.getReal(vector.radius || style.radius);
+      ctx.save();
+      ctx.beginPath();
+      ctx.arc(pt.x, pt.y, radius, 0, Math.PI * 2, true);
+      help.setStyle(ctx, style)
+      ctx.stroke();
+      ctx.fill();
+      ctx.restore();
+    }
+    draw(style)
+    if (style.out) {
+      draw(style.out)
+    }
 
     if (import.meta.env.DEV) {
       if (vector.vectorId) {
@@ -545,9 +649,10 @@ export default class Draw {
     setStyle && help.setVectorStyle(ctx, null, "Text");
 
     const pt = coordinate.getScreenXY(position);
-    const text = ctx.measureText(txt);
-    pt.x -= text.width / 2;
-    pt.y += (text.actualBoundingBoxAscent + text.actualBoundingBoxDescent) / 2;
+    const textCenter = help.getTextCenter(ctx, txt);
+    pt.x -= textCenter.x;
+    pt.y -= textCenter.y;
+
     if (angle) {
       ctx.translate(pt.x, pt.y);
       ctx.rotate(angle);
@@ -575,10 +680,53 @@ export default class Draw {
     this.context.font = oldFont;
   }
 
+  drawLineText(vector, style) {
+    const startReal = dataService.getPoint(vector.startId);
+    const endReal = dataService.getPoint(vector.endId);
+    help.drawLineText(
+      this.context,
+      coordinate.getScreenXY(startReal),
+      coordinate.getScreenXY(endReal),
+      help.getRealDistance(startReal, endReal) + "m",
+      style
+    )
+  }
+
+  drawBaseLineLabel(vector) {
+    const startReal = dataService.getPoint(vector.startId);
+    const start = coordinate.getScreenXY(startReal);
+    const endReal = dataService.getPoint(vector.endId);
+    const end = coordinate.getScreenXY(endReal);
+    const point = mathUtil.translate(
+      end, start, end, mathUtil.getDistance(start, end) / 3
+    )
+    const p4 = help.getPerpendicularPoint(start, end, point, 30)
+    const ctx = this.context
+    ctx.beginPath();
+    const [style] = help.setVectorStyle(
+      this.context,
+      vector,
+      vector.category || vector.geoType
+    );
+    ctx.moveTo(point.x, point.y);
+    ctx.lineTo(p4.x, p4.y);
+    ctx.stroke();
+
+    const p5 = help.getPerpendicularPoint(start, end, point, 35)
+    help.drawLineText(
+      this.context,
+      help.getPerpendicularPoint(point, p5, p5, 10),
+      help.getPerpendicularPoint(point, p5, p5, -10),
+      "基准线",
+      {
+        padding: 6,
+        backColor: "rgba(0,0,0,0)",
+        fillColor: style.strokeStyle
+      }
+    )
+  }
+
   drawLine(vector) {
-    if ([UIEvents.Arrow, UIEvents.MeasureLine].includes(vector.category)) {
-      return this.drawArrow(vector);
-    }
     const startReal = dataService.getPoint(vector.startId);
     const start = coordinate.getScreenXY(startReal);
     const endReal = dataService.getPoint(vector.endId);
@@ -593,16 +741,35 @@ export default class Draw {
     if (style.dash) {
       this.context.setLineDash(style.dash);
     }
+
     this.context.beginPath();
     this.context.moveTo(start.x, start.y);
     this.context.lineTo(end.x, end.y);
     this.context.stroke();
     this.context.restore();
 
-    // if (attr) {
-    //   this.drawPoint(startReal)
-    //   this.drawPoint(endReal)
-    // }
+    const drawPoints = () => {
+      // if (attr) {
+        this.drawPoint(dataService.getPoint(vector.startId))
+        this.drawPoint(dataService.getPoint(vector.endId))
+      // }
+    }
+
+    switch (vector.category) {
+      case VectorCategory.Line.ArrowLine:
+        this.drawArrow(vector);
+        drawPoints()
+        break
+      case VectorCategory.Line.BaseLine:
+        this.drawBaseLineLabel(vector)
+        drawPoints()
+        break;
+      case VectorCategory.Line.MeasureLine:
+        this.drawLineText(vector, style.text)
+        drawPoints()
+        break;
+    }
+
   }
 
   drawElementLine(element) {

+ 35 - 41
src/graphic/Service/DataService.js

@@ -2,11 +2,6 @@ import Line from "../Geometry/Line.js";
 import CurveLine from "../Geometry/CurveLine.js";
 import VectorType from "../enum/VectorType.js";
 import { coordinate } from "../Coordinate.js";
-import { lineService } from "./LineService.js";
-import { uiService } from "./UIService.js";
-import { circleService } from "./CircleService.js";
-import { textService } from "./TextService.js";
-import { magnifierService } from "./MagnifierService.js";
 
 export class DataService {
   constructor() {
@@ -137,6 +132,20 @@ export class DataService {
   }
 
   deleteLine(lineId) {
+    let line = this.getLine(lineId);
+    let start = this.getPoint(line.startId);
+    let startParent = start.getParent();
+    let end = this.getPoint(line.endId);
+    let endParent = end.getParent();
+
+    delete startParent[lineId];
+    if (Object.keys(startParent).length == 0) {
+      this.deletePoint(line.startId);
+    }
+    delete endParent[lineId];
+    if (Object.keys(endParent).length == 0) {
+      this.deletePoint(line.endId);
+    }
     delete this.vectorData.lines[lineId];
   }
 
@@ -340,6 +349,10 @@ export class DataService {
     }
   }
 
+  deleteRoadPoint1(roadPointId) {
+    delete this.vectorData.roadPoints[roadPointId];
+  }
+
   addRoadPoint(roadPoint) {
     this.vectorData.roadPoints[roadPoint.vectorId] = roadPoint;
   }
@@ -381,6 +394,14 @@ export class DataService {
     return this.vectorData.crossPoints[key];
   }
 
+  getCrossPoint3(vectorId) {
+    for (let key in this.vectorData.crossPoints) {
+      if (this.vectorData.crossPoints[key].vectorId == vectorId) {
+        return this.vectorData.crossPoints[key];
+      }
+    }
+  }
+
   getCrossPointForEdgeId(edgeId, dir) {
     for (let key in this.vectorData.crossPoints) {
       const crossPoint = this.vectorData.crossPoints[key];
@@ -414,6 +435,15 @@ export class DataService {
     }
   }
 
+  deleteCrossPoint1(vectorId) {
+    for (let key in this.vectorData.crossPoints) {
+      if (this.vectorData.crossPoints[key].vectorId == vectorId) {
+        delete this.vectorData.crossPoints[key];
+        return;
+      }
+    }
+  }
+
   deleteCrossPointForEdge(edgeId, dir) {
     let dCrossPointIds = [];
     for (let key in this.vectorData.crossPoints) {
@@ -497,42 +527,6 @@ export class DataService {
     return this.vectorData.svgs;
   }
 
-  //删除按钮
-  deleteVector(vectorId, geoType) {
-    switch (geoType) {
-      case VectorType.Line:
-        this.deleteLine(vectorId);
-        break;
-      case VectorType.Circle:
-        this.deleteCircle(vectorId);
-        break;
-      case VectorType.Text:
-        this.deleteText(vectorId);
-        break;
-      case VectorType.Magnifier:
-        this.deleteMagnifier(vectorId);
-        break;
-    }
-  }
-
-  //复制按钮
-  async copyVector(vectorId, geoType) {
-    switch (geoType) {
-      case VectorType.Line:
-        lineService.copy(vectorId);
-        break;
-      case VectorType.Circle:
-        circleService.copy(vectorId);
-        break;
-      case VectorType.Text:
-        textService.copy(vectorId);
-        break;
-      case VectorType.Magnifier:
-        await magnifierService.copy(vectorId);
-        break;
-    }
-  }
-
   clear() {
     //直路
     this.vectorData.roadPoints = {};

+ 43 - 0
src/graphic/Service/LineService.js

@@ -4,6 +4,7 @@ import { dataService } from "./DataService.js";
 import VectorCategory from "../enum/VectorCategory.js";
 import { mathUtil } from "../Util/MathUtil.js";
 import { uiService } from "./UIService.js";
+import { addLine } from "../Controls/AddLine.js";
 
 export default class LineService {
   constructor() {}
@@ -39,6 +40,27 @@ export default class LineService {
     return line;
   }
 
+  deleteLine(lineId) {
+    let line = dataService.getLine(lineId);
+    //如果是基准线
+    if (line.getCategory() == VectorCategory.Line.BaseLine) {
+      let points = dataService.getPoints();
+      for (let key in points) {
+        let point = dataService.getPoint(key);
+        let category = point.getCategory();
+        if (
+          category == VectorCategory.Point.BasePoint ||
+          category == VectorCategory.Point.TestBasePoint ||
+          category == VectorCategory.Point.TestPoint
+        ) {
+          dataService.deletePoint(key);
+        }
+      }
+      addLine.baseLineId = null;
+    }
+    dataService.deleteLine();
+  }
+
   copy(vectorId) {
     let line = dataService.getLine(vectorId);
     let startPoint = dataService.getPoint(line.startId);
@@ -49,6 +71,27 @@ export default class LineService {
     newLine.setArrowColor(line.arrowColor);
     return newLine;
   }
+
+  //将pointId1合并到pointId2
+  // mergePoint(pointId1, pointId2) {
+  //   let point1 = dataService.getPoint(pointId1);
+  //   let parent1 = point1.getParent();
+  //   let point2 = dataService.getPoint(pointId2);
+  //   let parent2 = point2.getParent();
+  //   for (let key in parent1) {
+  //     let line = dataService.getLine(key);
+  //     let dir = line.getDir(pointId1);
+  //     if (dir == "start") {
+  //       line.startId = pointId2;
+  //     } else if (dir == "end") {
+  //       line.endId = pointId2;
+  //     }
+  //     if(parent2.hasOwnProperty(''))
+  //   }
+  //   let point2 = dataService.getPoint(pointId2);
+  //   let parent2 = point2.getParent();
+  //   if(parent2.haso)
+  // }
 }
 
 const lineService = new LineService();

+ 1 - 2
src/graphic/Service/PointService.js

@@ -6,9 +6,8 @@ import { mathUtil } from "../Util/MathUtil.js";
 export default class PointService {
   constructor() {}
 
-  create(position, categoryType, vectorId) {
+  create(position, vectorId) {
     let point = new Point(position, vectorId);
-    point.setCategory(categoryType);
     dataService.addPoint(point);
     return point;
   }

+ 13 - 0
src/graphic/Service/RoadService.js

@@ -58,6 +58,19 @@ export default class RoadService {
     return road;
   }
 
+  //撤销恢复用的,edge这些会专门调用
+  createOnlyRoad(startId, endId, vectorId) {
+    let road = new Road(startId, endId, vectorId);
+    dataService.addRoad(road);
+
+    let startPoint = dataService.getRoadPoint(startId);
+    startPoint.setPointParent(road.vectorId, "start");
+
+    let endPoint = dataService.getRoadPoint(endId);
+    endPoint.setPointParent(road.vectorId, "end");
+    return road;
+  }
+
   getMidLine(road) {
     let startPoint = dataService.getRoadPoint(road.startId);
     let endPoint = dataService.getRoadPoint(road.endId);

+ 45 - 0
src/graphic/Service/UIService.js

@@ -4,6 +4,7 @@ import { mathUtil } from "../Util/MathUtil.js";
 import Settings from "../Settings";
 import { coordinate } from "../Coordinate.js";
 import UIEvents from "../enum/UIEvents.js";
+import VectorCategory from "../enum/VectorCategory.js";
 import Constant from "../Constant.js";
 
 export default class UIService {
@@ -62,6 +63,37 @@ export default class UIService {
     }
   }
 
+  isBelongLine(ui) {
+    if (ui == UIEvents.Arrow) {
+      this.setLineCategory(VectorCategory.Line.ArrowLine);
+      return true;
+    } else if (ui == UIEvents.MeasureLine) {
+      this.setLineCategory(VectorCategory.Line.MeasureLine);
+      return true;
+    } else if (ui == UIEvents.Line) {
+      this.setLineCategory(VectorCategory.Line.NormalLine);
+      return true;
+    } else if (ui == UIEvents.BaseLine) {
+      this.setLineCategory(VectorCategory.Line.BaseLine);
+      return true;
+    }
+    return false;
+  }
+
+  isBelongPoint(ui) {
+    // if (ui == UIEvents.Arrow) {
+    //   this.setLineCategory(VectorCategory.Line.ArrowLine);
+    //   return true;
+    // } else if (ui == UIEvents.MeasureLine) {
+    //   this.setLineCategory(VectorCategory.Line.MeasureLine);
+    //   return true;
+    // } else if (ui == UIEvents.Line) {
+    //   this.setLineCategory(VectorCategory.Line.NormalLine);
+    //   return true;
+    // }
+    // return false;
+  }
+
   setWayType(value) {
     Settings.wayType = value;
   }
@@ -98,6 +130,19 @@ export default class UIService {
     Settings.rightRoadWidth = value;
   }
 
+  setLineCategory(value) {
+    Settings.lineCategory = value;
+  }
+
+  setPointCategory(value) {
+    Settings.pointCategory = value;
+  }
+
+  //设置定位法
+  setLocationMode(value) {
+    Settings.locationMode = value;
+  }
+
   //如果position在屏幕左上角,返回的就朝向右下角,如果是右下角,则返回的是左上角。其他情况以此类推
   getNewPositionForPop(position) {
     const offset = 50;

+ 5 - 0
src/graphic/Settings.js

@@ -1,4 +1,5 @@
 import Constant from "./Constant";
+import VectorCategory from "./enum/VectorCategory";
 const Settings = {
   roadLeftDrivewayCount: 1,
   roadRightDrivewayCount: 1,
@@ -16,5 +17,9 @@ const Settings = {
   roadMidDivideWidth: Constant.defaultMidDivideWidth,
   curveRoadMidDivideWidth: Constant.defaultMidDivideWidth,
   wayType: Constant.twoWay, //one表示单向,two表示双向
+  lineCategory: VectorCategory.Line.NormalLine,
+  pointCategory: VectorCategory.Point.NormalPoint,
+  locationMode: Constant.angleLocationMode,
+  baseLineId: null, //基准线id,有且只有一条
 };
 export default Settings;

+ 14 - 10
src/graphic/Util/MathUtil.js

@@ -350,17 +350,13 @@ export default class MathUtil {
   }
 
   getArrow(start, end, ange = 30, L = 20) {
-    let a = Math.atan2((end.y - start.y), (end.x - start.x));
-    let xC = end.x - L * Math.cos(a + ange * Math.PI/180); // θ=30
-    let yC = end.y - L * Math.sin(a + ange * Math.PI/180);
-    let xD = end.x - L * Math.cos(a - ange * Math.PI/180);
-    let yD = end.y - L * Math.sin(a - ange * Math.PI/180);
+    let a = Math.atan2(end.y - start.y, end.x - start.x);
+    let xC = end.x - L * Math.cos(a + (ange * Math.PI) / 180); // θ=30
+    let yC = end.y - L * Math.sin(a + (ange * Math.PI) / 180);
+    let xD = end.x - L * Math.cos(a - (ange * Math.PI) / 180);
+    let yD = end.y - L * Math.sin(a - (ange * Math.PI) / 180);
 
-    return [
-      {x: xC, y: yC},
-      end,
-      {x: xD, y: yD}
-    ]
+    return [{ x: xC, y: yC }, end, { x: xD, y: yD }];
   }
 
   //经过point且与line垂直的线
@@ -1161,6 +1157,14 @@ export default class MathUtil {
       y: v1.y + v2.y,
     };
   }
+  // 中心点
+  lineCenter(v1, v2) {
+    const point = this.pointPlus(v1, v2)
+    return {
+      x: point.x / 2,
+      y: point.y / 2
+    }
+  }
   // 点放大
   pointScale(v, a) {
     return {

+ 3 - 0
src/graphic/enum/LayerEvents.js

@@ -14,6 +14,9 @@ const LayerEvents = {
   AddLine: "addLine",
   AddingLine: "addingLine",
   MoveLine: "moveLine",
+
+  AddPoint: "addPoint",
+  AddingPoint: "addingPoint",
   MovePoint: "movePoint",
 
   AddCircle: "addCircle",

+ 7 - 8
src/graphic/enum/UIEvents.js

@@ -7,19 +7,18 @@ const UIEvents = {
   Img: "backgroundImage",
   // 箭头
   Arrow: "ArrowLine",
+  //基准线
+  BaseLine: "BaseLine",
+  //基准点
+  BasePoint: "BasePoint",
 
   // 测量
-  // 测量线
-  MeasureLine: "MeasureLine",
-  // 测量点
-  MeasurePoint: "MeasurePoint",
   // 自由测量
-  MeasureFree: "MeasureFree",
+  NormalLocationMode: "NormalLocationMode",
   // 直角定位法
-  MeasureCartesianMethod: "MeasureCartesianMethod",
+  AngleLocationMode: "AngleLocationMode",
   // 综合定位法
-  MeasureComprehensiveMethod: "MeasureComprehensiveMethod",
-
+  AllLocationMode: "AllLocationMode",
 
   // 文字
   Text: "text", //这个是标注,暂时这样

+ 2 - 2
src/graphic/enum/VectorCategory.js

@@ -5,13 +5,13 @@ const VectorCategory = {
     BaseLine: "BaseLine",
     MeasureLine: "MeasureLine",
     GuideLine: "GuideLine",
-    PositionLine: "PositionLine", //定位线。与定位法相关的
+    PositionLine: "PositionLine", //定位线。基准点与待测点相连的线,或者与待测基准点相连的线
   },
   Point: {
     BasePoint: "BasePoint", //基准点
     TestPoint: "TestPoint", //待测点
     NormalPoint: "NormalPoint", //正常点
-    TestBasePoint: "TestBasePoint", //待测基准点
+    TestBasePoint: "TestBasePoint", //待测基准点,待测点与基准线相交的点
     FixPoint: "FixPoint", //固定点
   },
 };

+ 4 - 1
src/hook/useGraphic.ts

@@ -31,7 +31,10 @@ export const useChange = (fn: () => void) => {
 export const graphicState = ref({
   canRevoke: false,
   canRecovery: false,
-  showBackImage: false
+  showBackImage: true,
+  canAngleLocationMode: false,
+  canAllLocationMode: false,
+  existsBaseLine: false,
 })
 
 export const setCanvas = async (canvas: HTMLCanvasElement, data: Ref<AccidentPhoto | RoadPhoto>) => {

+ 4 - 2
src/router/constant.ts

@@ -12,7 +12,8 @@ export const readyRouteName = {
   photos: "photos",
   accidents: "accidents",
   roads: "roads",
-  tabulation: "tabulation"
+  tabulation: "tabulation",
+  gena4: "gena4"
 } as const;
 
 export const writeRouteName = {
@@ -47,7 +48,8 @@ export const readyRouteMeta: RouteMetaRaw = {
   [readyRouteName.photos]: {title: "相册"},
   [readyRouteName.accidents]: {title: "事故照片"},
   [readyRouteName.roads]: {title: "道路照片"},
-  [readyRouteName.tabulation]: {title: "制表"}
+  [readyRouteName.tabulation]: {title: "制表"},
+  [readyRouteName.gena4]: {title: "生成A4"}
 };
 
 export const writeRouteMeta: RouteMetaRaw<typeof modeFlags.LOGIN> = {

+ 6 - 0
src/router/info.ts

@@ -50,6 +50,12 @@ export const writeRoutesRaw: RoutesRaw<typeof modeFlags.LOGIN> = [
     component: () => import("@/views/accidents/index.vue"),
   },
   {
+    path: "/gena4/:id1/:id2",
+    name: readyRouteName.gena4,
+    meta: readyRouteMeta.gena4,
+    component: () => import("@/views/accidents/print.vue"),
+  },
+  {
     path: "/roads",
     name: readyRouteName.roads,
     meta: readyRouteMeta.roads,

+ 1 - 1
src/sdk/types/sdk.ts

@@ -39,7 +39,7 @@ export type Base = Emitter<SceneEventMap> & {
     slide?: boolean
   ) => { pos: Pos; trueSide: boolean };
 
-  screenshot: (width: number, height: number) => Promise<{ dataUrl: string }>;
+  screenshot: (width: number, height: number) => {finishPromise: Promise<{ dataUrl: string }>, meterPerPixel: number};
   el: HTMLElement;
 };
 

+ 1 - 0
src/store/accidentPhotos.ts

@@ -10,6 +10,7 @@ export type AccidentPhoto = {
   data: any,
   type?: string
   sceneData: {
+    meterPerPixel: number
     measures: PhotoRaw['measures'],
     fixPoints: PhotoRaw['fixPoints'],
     basePoints: PhotoRaw['basePoints']

+ 1 - 0
src/store/photos.ts

@@ -4,6 +4,7 @@ import {Pos} from "@/sdk";
 export type PhotoRaw = {
   id: string
   url: string
+  meterPerPixel: number
   time: number,
   measures: Array<Pos[]>,
   fixPoints: Array<Pos>,

+ 1 - 0
src/store/roadPhotos.ts

@@ -23,6 +23,7 @@ export type RoadPhoto = {
   table?: RoadPhotoTable,
   data: any
   sceneData: {
+    meterPerPixel: number
     measures: PhotoRaw['measures'],
     fixPoints: PhotoRaw['fixPoints'],
     basePoints: PhotoRaw['basePoints']

+ 12 - 0
src/store/sync.ts

@@ -46,6 +46,18 @@ export const uploadImage = async (blob: Blob) => {
   return res.data.data
 }
 
+export const downloadImage = async (data: Blob | string, name: string = getId()) => {
+  const blob: Blob = typeof data === "string"
+    ? (await axios.get(data, { responseType: "blob" })).data
+    : data
+
+  if (import.meta.env.DEV) {
+    window.open(URL.createObjectURL(blob))
+  } else {
+    const file = new File([blob], name)
+  }
+}
+
 const syncSceneStore = () => {
   return watch(
     () => ({

+ 30 - 4
src/views/accidents/index.vue

@@ -38,9 +38,7 @@
       </div>
     </div>
 
-    <ButtonPane class="del fun-ctrl" @click="delSelects" v-if="selects.length">
-      <ui-icon type="close" class="icon" />
-    </ButtonPane>
+    <ActionMenus class="select-menus" :menus="selectMenus" dire="row" v-if="selects.length" />
   </MainPanel>
 
   <FillSlide :data="sortPhotos" v-model:active="active" @quit="active = null" v-if="active">
@@ -57,7 +55,7 @@ import ActionMenus from "@/components/group-button/index.vue";
 import {types, accidentPhotos, AccidentPhoto} from '@/store/accidentPhotos'
 import UiIcon from "@/components/base/components/icon/index.vue";
 import {router, writeRouteName} from '@/router'
-import {computed, ref, watchEffect} from "vue";
+import {computed, reactive, ref, watchEffect} from "vue";
 import {Mode} from '@/views/graphic/menus'
 import UiButton from "@/components/base/components/button/index.vue";
 import Photos from "@/components/photos/index.vue";
@@ -94,6 +92,27 @@ const menus = [
   }
 ]
 
+const selectMenus = reactive([
+  {
+    key: "gen",
+    text: "生成A4",
+    disabled: computed(() => selects.value.length > 2),
+    onClick: () => {
+      const params = {
+        id1: selects.value[0].id,
+        id2: selects.value.length === 1 ? '-1' : selects.value[1].id
+      }
+      router.push({ name: writeRouteName.gena4, params})
+    }
+  },
+  {
+    key: "del",
+    text: "删除",
+    onClick: () => delSelects()
+  },
+])
+
+
 watchEffect(() => {
   if (!selectMode.value) {
     selects.value = []
@@ -172,4 +191,11 @@ const gotoDraw = () => {
   padding: 0 24px 2px;
   font-size: 16px;
 }
+
+
+.select-menus {
+  left: 50%;
+  transform: translateX(-50%);
+  bottom: var(--boundMargin);
+}
 </style>

+ 103 - 0
src/views/accidents/print.vue

@@ -0,0 +1,103 @@
+<template>
+  <MainPanel>
+    <template v-slot:header>
+      <Header  title="生成A4">
+        <ui-button
+            type="primary"
+            @click="saveHandler"
+            width="96px"
+        >
+          保存
+        </ui-button>
+      </Header>
+    </template>
+
+    <div class="print-layout">
+      <div class="content" ref="layoutRef">
+        <div v-for="accidentPhoto in genAccidentPhotos" :key="accidentPhoto.id" class="item">
+          <h4>{{accidentPhoto.title}}</h4>
+          <img :src="getStaticFile(accidentPhoto.url)">
+        </div>
+      </div>
+    </div>
+  </MainPanel>
+</template>
+<script setup lang="ts">
+import UiButton from "@/components/base/components/button/index.vue";
+import Header from "@/components/photos/header.vue";
+import MainPanel from "@/components/main-panel/index.vue";
+import {computed, ref} from "vue";
+import html2canvas from "html2canvas";
+import {router, writeRouteName} from "@/router";
+import {AccidentPhoto, accidentPhotos} from "@/store/accidentPhotos";
+import {getStaticFile} from "@/dbo/main";
+import {downloadImage} from "@/store/sync";
+import Message from "@/components/base/components/message/message.vue";
+
+const back = () => {
+  router.replace({name: writeRouteName.accidents})
+}
+
+const genAccidentPhotos = computed<AccidentPhoto[]>(() => {
+  let route, params
+  if (((route = router.currentRoute.value).name === writeRouteName.gena4)
+    && (params = route.params)
+    && params.id1 
+    && params.id2) {
+    return accidentPhotos.value?.filter(data => [params.id1, params.id2].includes(data.id))
+  }
+})
+
+const layoutRef = ref<HTMLDivElement>()
+const saveHandler = async () => {
+  const canvas = await html2canvas(layoutRef.value)
+  const blob = await new Promise<Blob>(
+    resolve => canvas.toBlob(resolve, "image/jpeg", 0.95)
+  )
+  await downloadImage(blob)
+  const hide = Message.success({ msg: "照片已生成" })
+  setTimeout(() => {
+    hide()
+    back()
+  }, 3000)
+}
+</script>
+
+<style lang="scss" scoped>
+.print-layout {
+  top: calc(var(--header-top) + var(--editor-head-height));
+  position: absolute;
+  background: #16181A;
+  padding: 65px;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  overflow-y: auto;
+
+}
+.content {
+  width: 1066px;
+  max-width: 100%;
+  height: 1514px;
+  padding: 0 64px;
+  margin: 0 auto;
+  background: #fff;
+}
+
+.item {
+  h4 {
+    font-size: 32px;
+    color: #000000;
+    padding-top: 64px;
+    box-sizing: content-box;
+    height: 38px;
+    line-height: 38px;
+  }
+
+  img {
+    width: 100%;
+    height: 600px;
+    object-fit: cover;
+  }
+}
+</style>

+ 1 - 0
src/views/graphic/data.ts

@@ -20,6 +20,7 @@ export const useData = () => {
           title: "",
           type: types[0],
           sceneData: {
+            meterPerPixel: photo.meterPerPixel,
             measures: photo.measures,
             basePoints: photo.basePoints,
             baseLines: photo.baseLines,

+ 1 - 1
src/views/graphic/geos/index.ts

@@ -8,7 +8,7 @@ import VectorCategory from "@/graphic/enum/VectorCategory";
 
 export default {
   [VectorType.ArrowLine]: Arrow,
-  [VectorCategory.Line.MeasureLine]: Arrow,
+  // [VectorCategory.Line.MeasureLine]: Arrow,
   [VectorCategory.Line.NormalLine]: Arrow,
   [VectorType.Text]: Text,
   [VectorType.Circle]: Circle,

+ 39 - 11
src/views/graphic/menus.ts

@@ -1,9 +1,10 @@
-import {uiType, UIType, VectorType} from "@/hook/useGraphic";
+import {uiType, UIType, VectorType, graphicState} from "@/hook/useGraphic";
 import {
   findMenuByAttr,
   generateByMenus as generateByMenusRaw,
   generateMixMenus as generateMixMenusRaw
 } from '@/utils/menus'
+import Message from "@/components/base/components/message/message.vue";
 
 export enum Mode {
   Road,
@@ -26,6 +27,7 @@ export const UITypeExtend = {
 export type MenuRaw = {
   key: string,
   text: string,
+  onClick?: (data: MenuRaw) => void,
   icon?: string,
   children?: MenusRaw
   extend?: MenusRaw
@@ -73,11 +75,31 @@ export const templateMenusRaw = [
 ]
 
 export const measureMenusRaw = [
-  { key: UIType.MeasureLine, text: "基准线" },
-  { key: UIType.MeasurePoint, text: "基准点" },
-  { key: UIType.MeasureFree, text: "自由测量" },
-  { key: UIType.MeasureCartesianMethod, text: "直角定位法" },
-  { key: UIType.MeasureComprehensiveMethod, text: "综合定位法" },
+  { key: UIType.BaseLine, text: "基准线" },
+  { key: UIType.BasePoint, text: "基准点" },
+  { key: UIType.NormalLocationMode, text: "自由测量" },
+  {
+    key: UIType.AngleLocationMode,
+    text: "直角定位法",
+    onClick(data) {
+      if (graphicState.value.canAllLocationMode) {
+        uiType.change(data.key)
+      } else {
+        Message.success({ msg: "请添加基准线及基准点后再执行此操作", time: 3000 })
+      }
+    }
+  },
+  {
+    key: UIType.AllLocationMode,
+    text: "综合定位法",
+    onClick(data) {
+      if (graphicState.value.canAllLocationMode) {
+        uiType.change(data.key)
+      } else {
+        Message.success({ msg: "请添加基准线及基准点后再执行此操作", time: 3000 })
+      }
+    }
+  },
 ]
 
 export const mainMenusRaw: MenusRaw = [
@@ -155,8 +177,8 @@ export const focusMenuRaw : { [key in string]: MenusRaw } = {
 
       ]
     },
-    { key: UIType.AddControlPoint, text: "加控制点" },
-    { key: UIType.MinusControlPoint, text: "减控制点" },
+    { key: UIType.AddCrossPoint, text: "加控制点" },
+    { key: UIType.MinusCrossPoint, text: "减控制点" },
     { key: UIType.Copy, text: "复制" },
     { key: UIType.Delete, text: "删除" }
   ],
@@ -166,8 +188,8 @@ export const focusMenuRaw : { [key in string]: MenusRaw } = {
     { key: UIType.AddNarrowRoad, text: "加窄路" },
     { key: UIType.AddLane, text: "加车道" },
     { key: UIType.DelLane, text: "减车道" },
-    { key: UIType.AddControlPoint, text: "加控制点" },
-    { key: UIType.MinusControlPoint, text: "减控制点" },
+    { key: UIType.AddCrossPoint, text: "加控制点" },
+    { key: UIType.MinusCrossPoint, text: "减控制点" },
     { key: UIType.Copy, text: "复制" },
     { key: UIType.Delete, text: "删除" }
   ],
@@ -196,6 +218,12 @@ export const generateMixMenus = <T extends {}, K extends keyof MenuRaw>(
   childKey,
   generateFn,
   mainMenus,
-    menu => uiType.change(menu.key as any),
+    menu => {
+      if (menu.onClick) {
+        menu.onClick(menu)
+      } else {
+        uiType.change(menu.key as any)
+      }
+    },
   () => uiType.current
 );

+ 15 - 9
src/views/photos/index.vue

@@ -17,9 +17,8 @@
     <ButtonPane class="back fun-ctrl" @click="router.push(writeRouteName.scene)" v-if="!selectMode">
       <ui-icon type="close" class="icon" />
     </ButtonPane>
-    <ButtonPane class="del fun-ctrl" @click="delSelects" v-if="selects.length">
-      <ui-icon type="close" class="icon" />
-    </ButtonPane>
+
+    <ActionMenus class="select-menus" :menus="selectMenus" dire="row" v-if="selects.length" />
   </MainPanel>
 
   <FillSlide :data="sortPhotos" v-model:active="active" @quit="active = null" v-if="active">
@@ -65,6 +64,14 @@ const menus = [
   }
 ]
 
+const selectMenus = [
+  {
+    key: "del",
+    text: "删除",
+    onClick: () => delSelects()
+  },
+]
+
 watchEffect(() => {
   if (!selectMode.value) {
     selects.value = []
@@ -104,15 +111,14 @@ const gotoDraw = (mode: Mode) => {
     transform: translateX(-50%);
   }
 }
-
-.back {
-  right: var(--boundMargin);
+.select-menus {
+  left: 50%;
+  transform: translateX(-50%);
   bottom: var(--boundMargin);
 }
 
-.del {
-  left: 50%;
-  transform: translateX(-50%);
+.back {
+  right: var(--boundMargin);
   bottom: var(--boundMargin);
 }
 

+ 17 - 3
src/views/roads/index.vue

@@ -33,9 +33,8 @@
       </template>
     </Photos>
 
-    <ButtonPane class="del fun-ctrl" @click="delSelects" v-if="selects.length">
-      <ui-icon type="close" class="icon" />
-    </ButtonPane>
+
+    <ActionMenus class="select-menus" :menus="selectMenus" dire="row" v-if="selects.length" />
   </MainPanel>
 
   <FillSlide
@@ -83,6 +82,14 @@ const menus = [
   }
 ]
 
+const selectMenus = [
+  {
+    key: "del",
+    text: "删除",
+    onClick: () => delSelects()
+  },
+]
+
 watchEffect(() => {
   if (!selectMode.value) {
     selects.value = []
@@ -139,4 +146,11 @@ const gotoDraw = () => {
   bottom: var(--boundMargin);
 }
 
+
+.select-menus {
+  left: 50%;
+  transform: translateX(-50%);
+  bottom: var(--boundMargin);
+}
+
 </style>

+ 0 - 16
src/views/roads/tabulation.vue

@@ -178,22 +178,6 @@ const { cssMatrix: photoCSSMatrix, matrix: photoMatrix } = useHand(
 
 
 const downMode = ref(false)
-const layoutRef = ref<HTMLDivElement>()
-const getLayoutImage = async () => {
-  downMode.value = true
-  await nextTick()
-  const canvas = await html2canvas(layoutRef.value)
-  downMode.value = false
-  const blob = await new Promise<Blob>(resolve => canvas.toBlob(resolve, "image/jpeg", 0.95))
-  return await uploadImage(blob)
-}
-const saveHandler = async () => {
-  roadPhoto.value.table = {
-    ...history.value.value,
-    url: await getLayoutImage()
-  }
-  router.replace({name: writeRouteName.roads})
-}
 
 </script>
 

+ 8 - 2
src/views/scene/photo.vue

@@ -10,7 +10,7 @@
         :src="showCoverUrl"
         class="cover"
         :style="{opacity: showCoverUrl ? '1' : 0}"
-        @click="router.replace(writeRouteName.photos)"
+        @click="router.push(writeRouteName.photos)"
     >
   </div>
 </template>
@@ -51,7 +51,12 @@ const photo = genUseLoading(async () => {
   const dom = sdk.scene.el
   dom.style.pointerEvents = "none"
 
-  const {dataUrl: base64} = await sdk.scene.screenshot(dom.offsetWidth, dom.offsetHeight)
+  const data = sdk.scene.screenshot(
+    dom.offsetWidth,
+    dom.offsetHeight
+  )
+  const {dataUrl: base64} = await data.finishPromise
+  console.log(data)
   const blob = base64ToBlob(base64)
   tempPhoto.value = URL.createObjectURL(blob)
 
@@ -67,6 +72,7 @@ const photo = genUseLoading(async () => {
         id: getId(),
         url: url,
         time: new Date().getTime(),
+        meterPerPixel: data.meterPerPixel,
         measures: list.value
           .map(data => getCurrentScreens(data.points))
           .filter(poss => poss.length),