cuiqian2004
4 天以前 2af5f043b60c1f7ac38ecccc8f5bf44743134325
pages/map/js/ctx.js
@@ -115,14 +115,26 @@
               stopContextMenu: true, // 禁止长按菜单
               fireRightClick: true,
               fireMiddleClick: true,
               targetFindTolerance: 10, // 增大触摸容差
               isTouchSupported: true,
               enableRetinaScaling: true,
               renderOnAddRemove: false,
               imageSmoothingEnabled: true
               imageSmoothingEnabled: true,
               perPixelTargetFind: true, // 点击图形自身才选中
               targetFindTolerance: 5 // 增大触摸容差
            })
            const originalShouldClearSelection = this.canvas._shouldClearSelection;
            // 重写方法
            this.canvas._shouldClearSelection = function(e) {
               // 只有当点击在对象上,并且是我们期望的条件(如边框)时,才执行默认的清除逻辑
               // 点击空白处直接返回 false,不清除选择
               if (e.target && e.subTargets) {
                  // 这里可以添加更精细的判断,例如结合后面的边框判断
                  return originalShouldClearSelection.call(this, e);
               }
               return false;
            };
            this.canvas.clear()
            this.eleWidth = cantainerEl.clientWidth
            this.eleHeight = cantainerEl.clientHeight
@@ -445,7 +457,24 @@
            _this.onOjectMoving(e.target)
         });
         _this.canvas.on("mouse:down", function(opt) {
            console.log("mouse:down", opt);
            // 如果点击的是空白处(没有目标对象)
            if (!opt.target) {
               // 取消当前选中
               _this.canvas.discardActiveObject();
               // 重新渲染画布
               _this.canvas.renderAll();
            } else {
               if (!opt.target.selectable) {
                  _this.canvas.discardActiveObject();
                  // 重新渲染画布
                  _this.canvas.renderAll();
               }
               console.log("mouse:down", opt.target.eleType);
            }
         });
         cantainerEl.addEventListener('touchstart', function(e) {
            //      console.log('touchstart:', e.touches.length);
@@ -468,12 +497,18 @@
         cantainerEl.addEventListener('touchend', function(e) {
            //   _this.canvas._onMouseUp(e);
            //   console.log('touchend:');
            e.preventDefault(); // 阻止默认行为
            _this.touchPoint = {
               x: 0,
               y: 0
            };
            if (_this.pressObjTimer) {
               clearTimeout(_this.pressObjTimer);
               _this.pressObjTimer = null
            }
            const activeObj = _this.canvas.getActiveObject()
            if (!activeObj) {
               // 处理结束事件
@@ -488,16 +523,13 @@
               //    _this.canvas.discardActiveObject();
               // }
            }
            if (this.pressObjTimer) {
               clearTimeout(this.pressObjTimer);
               this.pressObjTimer = null
            }
         });
         cantainerEl.addEventListener('touchcancel', function(e) {
            //   console.log('touchcancel:');
            if (this.pressObjTimer) {
               clearTimeout(this.pressObjTimer);
               this.pressObjTimer = null
            if (_this.pressObjTimer) {
               clearTimeout(_this.pressObjTimer);
               _this.pressObjTimer = null
            }
         })
@@ -505,7 +537,7 @@
      },
      canvasTouchStart(e) {
         const _this = this
         _this.pointerSelectObject(e)
         //   _this.pointerSelectObject(e)
         if (!_this.canvas.getActiveObject()) {
            // 根据触摸点数量判断交互类型
            if (e.touches.length === 1) {
@@ -522,6 +554,7 @@
               const list = _this.canvas.getActiveObjects()
               if (list.length === 1) {
                  if (!_this.objEditing) {
                     this.pressObjTimer = setTimeout(function() {
                        const zoom = _this.canvas.getZoom();
@@ -569,6 +602,7 @@
                           });
                        }
                     }, 1000); //
                  }
                  let activeObj = list[0]
                  if (activeObj.eleType == "region_pt_add") {
@@ -786,6 +820,33 @@
         const objActive = this.canvas.getActiveObject()
         let pointerList = []
         let pointerList2 = []
         return
         if (objActive instanceof fabric.Group) {
            const ptr = pointer
            // 计算相对于 Group 的本地坐标
            const localX = ptr.x - objActive.left;
            const localY = ptr.y - objActive.top;
            // 判断这个本地坐标是否“真的”点中了某个子对象
            let hit = false;
            objActive._objects.forEach((obj) => {
               if (obj.selectable) {
                  if (this.isPointOnStroke(obj, pointer, 8)) {
                     hit = true;
                  }
               }
            });
            console.log(hit)
            // 如果已经选中,但这一次没点中任何子对象 → 取消选中
            if (!hit) {
               this.canvas.discardActiveObject();
               this.canvas.requestRenderAll();
            }
         }
         for (let i = objects.length - 1; i >= 0; i--) {
            const obj = objects[i];
@@ -812,7 +873,10 @@
            if (obj instanceof fabric.Path || obj instanceof fabric.Line) {
               if (this.isPointOnStroke(obj, pointer, 1)) {
                  if (objActive != obj) {
                     if (this.pressObjTimer) {
                        clearTimeout(this.pressObjTimer);
                        this.pressObjTimer = null
                     }
                     this.canvas.discardActiveObject()
                     this.canvas.setActiveObject(obj);
                     this.canvas.requestRenderAll();
@@ -822,7 +886,10 @@
               pointerList2.unshift(obj)
            } else {
               if (objActive != obj) {
                  if (this.pressObjTimer) {
                     clearTimeout(this.pressObjTimer);
                     this.pressObjTimer = null
                  }
                  this.canvas.discardActiveObject()
                  this.canvas.setActiveObject(obj);
                  this.canvas.requestRenderAll();
@@ -835,7 +902,10 @@
            if (pointerList2.length == 1) {
               const obj = pointerList2[0];
               if (objActive != obj) {
                  if (this.pressObjTimer) {
                     clearTimeout(this.pressObjTimer);
                     this.pressObjTimer = null
                  }
                  this.canvas.discardActiveObject()
                  this.canvas.setActiveObject(obj);
                  this.canvas.requestRenderAll();
@@ -848,7 +918,10 @@
               const obj = pointerList[i];
               if (this.isPointOnStroke(obj, pointer, 2)) {
                  if (objActive != obj) {
                     if (this.pressObjTimer) {
                        clearTimeout(this.pressObjTimer);
                        this.pressObjTimer = null
                     }
                     this.canvas.discardActiveObject()
                     this.canvas.setActiveObject(obj);
                     this.canvas.requestRenderAll();
@@ -862,7 +935,10 @@
            if (pointerList2.length == 1) {
               const obj = pointerList2[0];
               if (objActive != obj) {
                  if (this.pressObjTimer) {
                     clearTimeout(this.pressObjTimer);
                     this.pressObjTimer = null
                  }
                  this.canvas.discardActiveObject()
                  this.canvas.setActiveObject(obj);
                  this.canvas.requestRenderAll();
@@ -875,7 +951,10 @@
               const obj = pointerList[i];
               if (this.isPointOnStroke(obj, pointer, 3)) {
                  if (objActive != obj) {
                     if (this.pressObjTimer) {
                        clearTimeout(this.pressObjTimer);
                        this.pressObjTimer = null
                     }
                     this.canvas.discardActiveObject()
                     this.canvas.setActiveObject(obj);
                     this.canvas.requestRenderAll();
@@ -889,7 +968,10 @@
            if (pointerList2.length == 1) {
               const obj = pointerList2[0];
               if (objActive != obj) {
                  if (this.pressObjTimer) {
                     clearTimeout(this.pressObjTimer);
                     this.pressObjTimer = null
                  }
                  this.canvas.discardActiveObject()
                  this.canvas.setActiveObject(obj);
                  this.canvas.requestRenderAll();
@@ -902,7 +984,10 @@
               const obj = pointerList[i];
               if (this.isPointOnStroke(obj, pointer, 4)) {
                  if (objActive != obj) {
                     if (this.pressObjTimer) {
                        clearTimeout(this.pressObjTimer);
                        this.pressObjTimer = null
                     }
                     this.canvas.discardActiveObject()
                     this.canvas.setActiveObject(obj);
                     this.canvas.requestRenderAll();
@@ -916,7 +1001,10 @@
            const obj = pointerList2[pointerList2.length - 1];
            if (objActive != obj) {
               if (this.pressObjTimer) {
                  clearTimeout(this.pressObjTimer);
                  this.pressObjTimer = null
               }
               this.canvas.discardActiveObject()
               this.canvas.setActiveObject(obj);
               this.canvas.requestRenderAll();
@@ -1202,7 +1290,7 @@
               _this.$ownerInstance.callMethod('receiveRenderData', {
                  method: "set_backgroud_progress",
                  type: "error",
                  msg:err
                  msg: err
               });
               console.error("图片加载失败", err)
               reject(new Error('图片加载失败'));
@@ -1236,6 +1324,7 @@
         }
      },
      clearObjects() {
         if (!this.canvas) return;
         this.canvas.discardActiveObject()
         const objects = this.canvas.getObjects()
@@ -1251,6 +1340,8 @@
         this.objEditing = null;
         this.editObject = null;
         this.drawingObj = null;
         this.objAgvLaser = null
         this.objAgvLaserLine = null
         if (this.pressObjTimer) {
            clearTimeout(this.pressObjTimer);
            this.pressObjTimer = null;
@@ -1370,6 +1461,8 @@
                     // _this.canvas.renderAll()
                     _this.workSpace = wsGroup
                     const scale = _this.eleWidth / (4 * 84)
                     _this.setZoomAuto(scale); //
                  }
                  //_this.checkMemoryUsage()
                  resolve()
@@ -1391,7 +1484,7 @@
                     strokeLineCap: 'butt',
                     fill: "rgba(255,255,255,0)",
                  })
                  let wsGroup = new fabric.Group([ rect], {
                  let wsGroup = new fabric.Group([rect], {
                     id: "workspace",
                     eleType: "workspace",
                     selectable: false,
@@ -1400,11 +1493,13 @@
                     top: 0,
                     width: _this.mapInfo.img_x,
                     height: _this.mapInfo.img_y,
                  });
                  _this.clearObjects()
                  _this.canvas.add(wsGroup)
                  _this.workSpace = wsGroup
                  const scale = _this.eleWidth / (4 * 84)
                  _this.setZoomAuto(scale); //
                  resolve()
               })
@@ -1539,11 +1634,13 @@
                  clearTimeout(this.pressObjTimer);
                  this.pressObjTimer = null
               }
            }
            if (Math.abs(deltaX) > 20 || Math.abs(deltaY) > 20) {
               this.$ownerInstance.callMethod('receiveRenderData', {
                  method: "cancel_positioning_agv",
               });
            }
            // 移动视口
            const vpt = this.canvas.viewportTransform;
            this.canvas.relativePan(new fabric.Point(deltaX, deltaY));
@@ -1773,9 +1870,7 @@
         }
         //console.log(scale, scaleAuto)
         this.setZoomAuto(scale, center)
         this.$ownerInstance.callMethod('receiveRenderData', {
            method: "cancel_positioning_agv",
         });
         //   console.log('多点移动 - 距离:', distance, '角度:', angle);
         // 多点移动逻辑
      },
@@ -1823,12 +1918,20 @@
         const scale2 = 1 / scale
         let list = this.canvas.getObjects()
         const filter = ["agv", "current_teaching", "edit_teaching", "edit_teaching_pt", "cmd", "station",
         const filter = ["agv", "public_teaching", "current_teaching", "edit_teaching", "edit_teaching_pt", "cmd",
            "station",
            "station_mark", "station_tip"
         ]
         list = list.filter((a) => filter.includes(a.eleType))
         list.forEach((obj) => {
            if (obj.eleType == "edit_teaching") {
            if (obj.eleType == "public_teaching") {
               // const subObjs = obj.getObjects()
               // subObjs.forEach((obj2) => {
               //    obj2.set({
               //       strokeWidth: obj2.strokeOriginWidth * scale2
               //    })
               // })
            } else if (obj.eleType == "edit_teaching") {
               obj.set({
                  strokeWidth: 2 * scale2
               })
@@ -1838,7 +1941,7 @@
                  scaleY: scale2
               })
            }
            if (obj.eleType == "station" || obj.eleType == "edit_teaching") {
             if (obj.eleType == "station" || obj.eleType == "edit_teaching") {
               const tipObj = obj.tipObj
               if (tipObj) {
                  tipObj.set({
@@ -1848,8 +1951,7 @@
                  })
                  tipObj.setCoords()
               }
            }
            if (obj.eleType == "cmd") {
            } else if (obj.eleType == "cmd") {
               const obj2 = obj.mainObj
               if (obj2?.eleType == "station") {
                  if (obj.id == `cancel`) {
@@ -1886,7 +1988,7 @@
            const left = _this.getXOnImg(info.x) // * scale
            const top = _this.getYOnImg(info.y) //* scale
            const angle = info.angle * 180 / Math.PI
            const angle = -(info.angle * 180 / Math.PI)
            fabric.loadSVGFromURL(svg).then(
               ({
                  objects,
@@ -2018,7 +2120,8 @@
            this.canvas.remove(this.curTeachingObj)
            this.curTeachingObj = null
         }
         if (teachingData.lenght === 0)
            return
         posArr.forEach((item) => {
            const curIndex = pos_list.findIndex((item2) => item2.x === item.x && item2.y === item.y)
            if (curIndex < 0) {
@@ -2026,9 +2129,6 @@
            }
         })
         let path2 = ""
         const theta = 20;
         let headlen = 10;
         var main_road = teachingData.main_road || 0
         const len = pos_list.length
         let fromX = 0,
@@ -2042,45 +2142,18 @@
               y: this.getYOnImg(pt.y)
            }
            if (index > 0) {
               if ((index % 50 == 0 || index == len - 1 || index % 50 == 1)) {
                  if (index % 50 == 0 || index == len - 1) {
                     toX = pt2.x
                     toY = pt2.y
                     path2 += ` L${pt2.x} ${pt2.y}`
                     // 计算各角度和对应的P2,P3坐标
                     let angle = (Math.atan2(fromY - toY, fromX - toX) * 180) / Math.PI,
                        angle1 = ((angle + theta) * Math.PI) / 180,
                        angle2 = ((angle - theta) * Math.PI) / 180,
                        topX = headlen * Math.cos(angle1),
                        topY = headlen * Math.sin(angle1),
                        botX = headlen * Math.cos(angle2),
                        botY = headlen * Math.sin(angle2);
                     let arrowX = fromX - topX,
                        arrowY = fromY - topY;
                     arrowX = toX + topX;
                     arrowY = toY + topY;
                     path2 += " L " + arrowX + " " + arrowY;
                     arrowX = toX + botX;
                     arrowY = toY + botY;
                     path2 += " M " + arrowX + " " + arrowY;
                     path2 += " L " + toX + " " + toY;
                  }
               } else {
                  path2 += ` L${pt2.x} ${pt2.y}`
               }
               path2 += ` L${pt2.x} ${pt2.y}`
            } else {
               path2 = `M${pt2.x} ${pt2.y}`
            }
            fromX = pt2.x
            fromY = pt2.y
         }
         let strokeWidth = 1
         let stroke = "#95DE64"
         let strokeWidth = 5
         let stroke = "#69C0FF"
         if (main_road == 1) {
            stroke = "#69C0FF"
         }
         const objPath = new fabric.Path(
            path2, {
               id: "current_teaching",
@@ -2112,33 +2185,55 @@
               pos_list.push(item)
            }
         })
         let teachingGroup = []
         let path2 = ""
         const theta = 20;
         let pathArrow = ""
         const theta = 30;
         let headlen = 10;
         var main_road = 1
         var main_road = teachingData.main_road
         const len = pos_list.length
         let fromX = 0,
            fromY = 0,
            toX = 0,
            toY = 0;
         let right = 0
         let bottom = 0
         let left = this.mapInfo.img_x
         let top = this.mapInfo.img_y
         var scale = this.canvas.getZoom()
         if (scale < 1) {
            scale = 1
         }
         const scale2 = 1 / scale
         for (let index = 0; index < len; index++) {
            const pt = pos_list[index]
            let pt2 = {
               x: this.getXOnImg(pt.x),
               y: this.getYOnImg(pt.y)
            }
            if (fromX == pt2.x && fromY == pt2.y) {
               pos_list.splice(index, 1)
               index--;
               continue;
            }
            if (index > 0) {
               if ((type == "public_teaching") && (index % 50 == 0 || index == len - 1 || index % 50 == 1)) {
                  if (index % 50 == 1 || index == len - 1) {
                     if (teachingData.bidirection == 1 && index < len - 1) { //- 1
                        toY = fromY
                        toX = fromX
                        fromX = pt2.x
                        fromY = pt2.y
               if (type == "public_teaching") {
                  const offset = index % 50
                  if (offset > 20 && offset < 41) {
                     if (offset == 21) {
                        pathArrow = `M${pt2.x} ${pt2.y}`
                     } else
                        pathArrow += ` L${pt2.x} ${pt2.y}`
                     if (offset == 40) {
                        const pt = pos_list[index - 2]
                        fromX = this.getXOnImg(pt.x)
                        fromY = this.getYOnImg(pt.y)
                        //   console.log(fromX,fromY, this.getXOnImg(pt.x),this.getYOnImg(pt.y))
                        toX = pt2.x
                        toY = pt2.y
                        let angle = (Math.atan2(fromY - toY, fromX - toX) * 180) / Math.PI,
                           angle1 = ((angle + theta) * Math.PI) / 180,
                           angle2 = ((angle - theta) * Math.PI) / 180,
@@ -2150,78 +2245,70 @@
                           arrowY = fromY - topY;
                        arrowX = toX + topX;
                        arrowY = toY + topY;
                        let path3 = " L " + arrowX + " " + arrowY;
                        pathArrow += " M " + arrowX + " " + arrowY;
                        pathArrow += " L " + toX + " " + toY;
                        arrowX = toX + botX;
                        arrowY = toY + botY;
                        path3 += " M " + arrowX + " " + arrowY;
                        path3 += " L " + toX + " " + toY;
                        pathArrow += " L " + arrowX + " " + arrowY;
                        path2 += path3
                        fromY = toY
                        fromX = toX
                        const objArrow = new fabric.Path(
                           pathArrow, {
                              stroke: "#fff",
                              strokeWidth:2,// * scale2,
                              strokeOriginWidth:2,
                              fill: "#ffffff00",
                              selectable: false,
                           })
                        teachingGroup.push(objArrow)
                     }
                  }
                  if (index % 50 == 0 || index == len - 1) {
                     toX = pt2.x
                     toY = pt2.y
                     // if (fromX == toX && fromY == toY) {
                     //    if (index - 2 >= 0) {
                     //       const pt3 = pos_list[index - 2]
                     //       fromX = this.getXOnImg(pt3.x),
                     //          fromY = this.getYOnImg(pt3.y)
                     //    } else {
                     //       continue;
                     //    }
                     // }
                     path2 += ` L${pt2.x} ${pt2.y}`
                     // 计算各角度和对应的P2,P3坐标
                     let angle = (Math.atan2(fromY - toY, fromX - toX) * 180) / Math.PI,
                        angle1 = ((angle + theta) * Math.PI) / 180,
                        angle2 = ((angle - theta) * Math.PI) / 180,
                        topX = headlen * Math.cos(angle1),
                        topY = headlen * Math.sin(angle1),
                        botX = headlen * Math.cos(angle2),
                        botY = headlen * Math.sin(angle2);
                     let arrowX = fromX - topX,
                        arrowY = fromY - topY;
                     arrowX = toX + topX;
                     arrowY = toY + topY;
                     path2 += " L " + arrowX + " " + arrowY;
                     arrowX = toX + botX;
                     arrowY = toY + botY;
                     path2 += " M " + arrowX + " " + arrowY;
                     path2 += " L " + toX + " " + toY;
                  }
                  //   console.log(`箭头 L${pt2.x} ${pt2.y}`)
               } else {
                  //   console.log(`点 L${pt2.x} ${pt2.y} ${index} == ${len-1}`)
                  path2 += ` L${pt2.x} ${pt2.y}`
               }
               path2 += ` L${pt2.x} ${pt2.y}`
            } else {
               main_road = pt.main_road
               if (main_road == 1) {
                  headlen = 15
               }
               if (main_road === undefined)
                  main_road = pt.main_road
               path2 = `M${pt2.x} ${pt2.y}`
            }
            // if (left > pt2.x) {
            //    left = pt2.x
            // }
            // if (top > pt2.y) {
            //    top = pt2.y
            // }
            // if (right < pt2.x) {
            //    right = pt2.x
            // }
            // if (bottom < pt2.y) {
            //    bottom = pt2.y
            // }
            fromX = pt2.x
            fromY = pt2.y
         }
         // console.log("addTeachingPath",path2)
         // path2 += " Z"
         let strokeWidth = 1
         let stroke = "#95DE64"
         let strokeWidth = 8
         let stroke = "#69C0FF"
         if (type == "station_teaching") {
            stroke = "#69C0FF"
            stroke = "#69C0FF80"
         } else {
            if (main_road == 1) {
               stroke = "#69C0FF"
               //stroke = "#69C0FF"
               if (teachingData.bidirection == 1) {
                  stroke = "#ffaa0080"
               }
               strokeWidth = 12
            } else {
               strokeWidth = 8
               if (teachingData.bidirection == 1) {
                  stroke = "#FF00FF80"
               }
            }
         }
         teachingData.main_road = main_road
         let list = this.canvas.getObjects() || []
         list = list.filter((a) => a.eleType == "station_teaching" || a.eleType == "public_teaching")
         let lenTeaching = list.length
@@ -2230,15 +2317,29 @@
            lenTeaching = list.length
         }
         let ptList = []
         const objPath = new fabric.Path(
         const obj = new fabric.Path(
            path2, {
               id: id,
               eleType: type,
               stroke: stroke,
               strokeWidth,
               strokeWidth:strokeWidth,// * scale2,
               strokeOriginWidth:strokeWidth,
               // strokeDashArray: [5, 3],
               // strokeLineCap: 'butt',
               // originX: "center",
               // originY: "center",
               selectable: true,
               fill: "#ffffff00",
            })
         // this.canvas.add(obj)
         teachingGroup.unshift(obj)
         const objPath = new fabric.Group(
            teachingGroup, {
               id: id,
               eleType: type,
               // stroke: stroke,
               // strokeWidth,
               fill: "#ffffff00",
               hasControls: false,
               lockRotation: true,
@@ -2246,7 +2347,12 @@
               lockScalingY: true,
               lockMovementX: true,
               lockMovementY: true,
               // perPixelTargetFind: true,
               // left: left - strokeWidth / 2,
               // top: top - strokeWidth / 2,
               // width: right - left + strokeWidth,
               // height: bottom - top + strokeWidth,
               mainRoad: main_road,
               data: teachingData
            })
@@ -2256,6 +2362,7 @@
         return objPath
      },
      isObjectFullyContained(outerObj, innerObj) {
         const outer = outerObj.getBoundingRect(true, true);
         const inner = innerObj.getBoundingRect(true, true);
@@ -2310,7 +2417,7 @@
         let id = ""
         if (teachingMode.mode == "Public") {
            eleType = "public_teaching"
            id = `public_teaching_${teachingMode.name}`
            id = `public_teaching_${teachingMode.name}_${teachingMode.edge_name}`
         } else if (teachingMode.mode == "Stations") {
            eleType = "station_teaching"
            id = `station_teaching_${teachingMode.src_dst}`
@@ -2461,7 +2568,7 @@
            //   const scale = this.getAutoScale()
            const left = _this.getXOnImg(info.x) // * scale
            const top = _this.getYOnImg(info.y) //* scale
            const angle = info.angle * 180 / Math.PI
            const angle = -(info.angle * 180 / Math.PI) + 90
            if (obj) {
               obj.set({
                  left,
@@ -3028,53 +3135,105 @@
         this.canvas.remove(obj)
      },
      updateAgvLaser(param) {
         const angle = param.angle - Math.PI / 2 // * 180 / Math.PI
         const angle = -param.angle //- Math.PI / 2 // * 180 / Math.PI
         const pt = {
            x: this.getXOnImg(param.x),
            y: this.getYOnImg(param.y)
         }
         if (this.objAgvLaser) {
            this.objAgvLaser.set({
               stroke: "#00ff00",
               rx: 2,
               ry: 2
            })
         }
         let ellipse = new fabric.Ellipse({
            id: "agv_laser",
            eleType: "agv_laser",
            left: pt.x,
            top: pt.y,
            rx: 2,
            ry: 2,
            stroke: "#00aa00",
            rx: 3,
            ry: 3,
            stroke: "#ff0000",
            strokeWidth: 1,
            fill: "#00aa00",
            fill: "#00ff00",
            originX: "center",
            originY: "center",
            selectable: false,
            hasControls: true,
            lockRotation: true,
            lockScalingX: true,
            lockScalingY: true,
            lockMovementX: true,
            lockMovementY: true,
            hasControls: false,
         });
         this.canvas.add(ellipse)
         const offX = 20 * Math.cos(angle)
         const offY = 20 * Math.sin(angle)
         if (this.objAgvLaser) {
            this.canvas.remove(this.objAgvLaser)
         this.objAgvLaser = ellipse
         if (this.objAgvLaserLine) {
            this.canvas.remove(this.objAgvLaserLine)
         }
         const line = new fabric.Line([pt.x, pt.y, pt.x + offX,
            pt.y + offY
         ], {
            id: "agv_laser_angle",
            eleType: "agv_laser_angle",
            stroke: "#00aa00",
            strokeWidth: 2,
            lockRotation: true,
            lockScalingX: true,
            lockScalingY: true,
            lockMovementX: true,
            lockMovementY: true,
            stroke: "#00ff00",
            strokeWidth: 1,
            selectable: false,
            hasControls: false,
         });
         this.objAgvLaser = line
         this.objAgvLaserLine = line
         this.canvas.add(line)
      },
      addTrajectoryPoint(list) {
         const objs = []
         let right = 0
         let bottom = 0
         let left = this.mapInfo.img_x
         let top = this.mapInfo.img_y
         for (let i in list) {
            const pt = list[i]
            const pt2 = {
               x: this.getXOnImg(pt[0]),
               y: this.getYOnImg(pt[1])
            }
            let ellipse = new fabric.Ellipse({
               left: pt2.x,
               top: pt2.y,
               rx: 2,
               ry: 2,
               stroke: "#00ff00",
               strokeWidth: 1,
               fill: "#00ff00",
               originX: "center",
               originY: "center",
            });
            objs.push(ellipse)
            if (left > pt2.x - 2) {
               left = pt2.x - 2
            }
            if (top > pt2.y - 2) {
               top = pt2.y - 2
            }
            if (right < pt2.x + 2) {
               right = pt2.x + 2
            }
            if (bottom < pt2.y + 2) {
               bottom = pt2.y + 2
            }
         }
         const groupObj = new fabric.Group(objs, {
            id: `trajectory_point_group`,
            eleType: "trajectory_point_group",
            left,
            top,
            width: right - left,
            height: bottom - top,
            selectable: false,
            hasControls: true,
         })
         this.canvas.add(groupObj)
      },
      updateLaserPoint(param) {
@@ -3116,7 +3275,7 @@
               top: pt2.y,
               width: 1,
               height: 1,
               fill: "#F5222D",
               fill: "#ff00ff",
               originX: "left",
               originY: "top",
               // selectable: false,
@@ -3325,7 +3484,7 @@
                     } else {
                        //   _this.canvas.remove(list[curIndex])
                        const curStationObj = list[curIndex]
                        const angle = station.angle * 180 / Math.PI
                        const angle = -station.angle * 180 / Math.PI
                        //const scale = this.getAutoScale()
                        const left = this.getXOnImg(station.x) //* scale
                        const top = this.getYOnImg(station.y) //* scale
@@ -3360,6 +3519,7 @@
               } else if (item.method == "remove_station") {
                  const stationList = item.param || []
                  this.canvas.discardActiveObject()
                  let list = _this.canvas.getObjects() || []
                  list = list.filter((a) => a.eleType == "station")
@@ -3536,6 +3696,7 @@
                  _this.showTeachingPath(_this.showTeachPathFlag ? true : false)
               } else if (item.method == "clear_teaching_path") {
                  this.canvas.discardActiveObject()
                  let list = _this.canvas.getObjects() || []
                  list = list.filter((a) => a.eleType == "public_teaching" || a.eleType ==
                     "station_teaching")
@@ -3548,7 +3709,7 @@
                  const show = item.param?.show || false
                  for (let i2 in teachingPathList) {
                     const teachingPath = teachingPathList[i2]
                     const id = `public_teaching_${teachingPath.name}`
                     const id = `public_teaching_${teachingPath.name}_${teachingPath.edge_name}`
                     const obj = await this.addTeachingPath(teachingPath, id, "public_teaching",
                        show)
                     obj.set({
@@ -3575,10 +3736,11 @@
                  _this.showTeachingPath(item.param.show)
               } else if (item.method == "remove_teaching_path") {
                  this.canvas.discardActiveObject()
                  let list = _this.canvas.getObjects() || []
                  if (item.param.mode == "Public") {
                     list = list.filter((a) => a.eleType == "public_teaching")
                     const id = `public_teaching_${item.param.name}`
                     const id = `public_teaching_${item.param.name}_${item.param.edge_name}`
                     const curIndex = list.findIndex((a) => a.id == id)
                     if (curIndex > -1) {
@@ -3600,6 +3762,35 @@
                  const teachingMode = item.param
                  _this.showEditTeachingPath(teachingMode)
               } else if (item.method == "update_teaching") {
                  let list = _this.canvas.getObjects() || []
                  list = list.filter((a) => a.eleType == "public_teaching")
                  const id = `public_teaching_${item.param.name}_${item.param.edge_name}`
                  const curIndex = list.findIndex((a) => a.id == id)
                  if (curIndex > -1) {
                     const obj = list[curIndex]
                     let strokeWidth = 1.5
                     let stroke = "#69C0FF"
                     if (item.param.main_road == 1) {
                        //stroke = "#69C0FF"
                        if (item.param.bidirection == 1) {
                           stroke = "#ffaa00"
                        }
                        strokeWidth = 3
                     } else {
                        if (item.param.bidirection == 1) {
                           stroke = "#FF00FF"
                        }
                     }
                     obj.set({
                        strokeWidth,
                        stroke
                     })
                  }
               } else if (item.method == "set_selectable") {
                  if (item.param)
                     _this.setAllObjectSelectable(true)
@@ -3648,6 +3839,7 @@
                  }
               } else if (item.method == "remove_wall") {
                  const wallList = item.param || []
                  this.canvas.discardActiveObject()
                  let list = _this.canvas.getObjects() || []
                  list = list.filter((a) => a.eleType == "virtual_wall")
                  for (let i2 in wallList) {
@@ -3659,6 +3851,7 @@
                  }
               } else if (item.method == "remove_region") {
                  const regionList = item.param || []
                  this.canvas.discardActiveObject()
                  let list = _this.canvas.getObjects() || []
                  list = list.filter((a) => a.eleType == "region")
                  for (let i2 in regionList) {
@@ -3672,7 +3865,10 @@
                  this.updateAgvLaser(item.param || {})
               } else if (item.method == "point_cloud") {
                  this.updateLaserPoint(item.param || {})
               } else if (item.method == "point_trajectory") {
                  this.addTrajectoryPoint(item.param || [])
               }
            }
            if (_this.canvas)
               _this.canvas.renderAll()
@@ -3686,7 +3882,7 @@
         if (type == "string") {
            let tip = ex
            console.log(ex)
            plus.nativeUI.alert(tip, undefined, "错误");
            plus.nativeUI.alert(tip, undefined);
            return
         }
         let exStr = JSON.stringify(ex)
@@ -3695,7 +3891,7 @@
         let tip = typeof ex.msg == "string" ? ex.msg : exStr
         console.log(tip)
         plus.nativeUI.alert(tip, undefined, "错误");
         plus.nativeUI.alert(tip, undefined);
      },
      showToast(ex) {
         const type = typeof ex