Kaynağa Gözat

管道 使用 圆角拐点的折线item

haojianlong 4 yıl önce
ebeveyn
işleme
b7ca3c69f0

+ 2 - 2
persagy-web-big-edit/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@persagy-web/big-edit",
-    "version": "2.2.11",
+    "version": "2.2.12",
     "description": "博锐尚格二维图形编辑器。",
     "main": "lib/index.js",
     "types": "lib/index.d.js",
@@ -40,7 +40,7 @@
         "typescript": "^3.5.3"
     },
     "dependencies": {
-        "@persagy-web/edit": "2.2.10",
+        "@persagy-web/edit": "2.2.11",
         "@types/uuid": "^8.0.0",
         "crypto-js": "^4.0.0",
         "axios": "^0.20.0"

+ 33 - 32
persagy-web-big-edit/src/items/SBasePipe.ts

@@ -27,26 +27,26 @@
 import { SBasePolylineEdit } from "@persagy-web/edit";
 import { SGraphItem, SAnchorItem } from "@persagy-web/graph/lib/";
 import { Relation } from "./../types/Relation";
-import { SMouseEvent, } from "@persagy-web/base/lib";
+import { SMouseEvent } from "@persagy-web/base/lib";
 import { SPoint, SColor } from "@persagy-web/draw";
 import { SLineStyle } from "@persagy-web/graph/";
+import { SBaseCirclePolylineEdit } from "@persagy-web/edit/lib";
 
 /**
  * 编辑基础管道
  *
  * @author 韩耀龙 <han_yao_long@163.com>
  */
-export class SBasePipe extends SBasePolylineEdit {
-    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+export class SBasePipe extends SBaseCirclePolylineEdit {
     //属性
     /** 关系型数据 */
     _relationData: Relation | null = null;
     set relationData(val) {
         this._relationData = val;
-        this.initData()
+        this.initData();
     } // set legendData()
     get relationData(): Relation | null {
-        return this._relationData
+        return this._relationData;
     } // get legendData()
 
     /** 起始锚点  */
@@ -57,49 +57,49 @@ export class SBasePipe extends SBasePolylineEdit {
     _anchor1Id: string = "";
     get anchor1Id(): string {
         return this._anchor1Id;
-    }// get anchor1Id()
+    } // get anchor1Id()
     set anchor1Id(v: string) {
         this._anchor1Id = v;
         if (this.relationData) {
             this.relationData.anchor1Id = this._anchor1Id;
         }
-    }// set anchor1Id()
+    } // set anchor1Id()
 
     /** 关联锚点2ID  */
     _anchor2Id: string = "";
     get anchor2Id(): string {
         return this._anchor2Id;
-    }// get anchor2Id()
+    } // get anchor2Id()
     set anchor2Id(v: string) {
         this._anchor2Id = v;
         if (this.relationData) {
             this.relationData.anchor2Id = this._anchor2Id;
         }
-    }// set anchor2Id()
+    } // set anchor2Id()
 
     /** 关联节点1ID */
     _node1Id: string = "";
     get node1Id(): string {
         return this._node1Id;
-    }// get node1Id()
+    } // get node1Id()
     set node1Id(v: string) {
         this._node1Id = v;
         if (this.relationData) {
             this.relationData.node1Id = this._node1Id;
         }
-    }// set node1Id()
+    } // set node1Id()
 
     /** 关联节点2ID */
     _node2Id: string = "";
     get node2Id(): string {
         return this._node2Id;
-    }// get node2Id()
+    } // get node2Id()
     set node2Id(v: string) {
         this._node2Id = v;
         if (this.relationData) {
             this.relationData.node2Id = this._node2Id;
         }
-    }// set node2Id()
+    } // set node2Id()
 
     /**
      * 构造函数
@@ -111,7 +111,7 @@ export class SBasePipe extends SBasePolylineEdit {
         super(parent);
         this.relationData = data;
         this.zOrder = 9700;
-           // 求掉选中框
+        // 求掉选中框
         this.showSelect = false;
     }
 
@@ -124,49 +124,49 @@ export class SBasePipe extends SBasePolylineEdit {
         // 名称
         this.name = data.name;
         // 线样式
-        this.lineStyle = SLineStyle[data.lineType] ? SLineStyle[data.lineType] : SLineStyle.Solid;
+        this.lineStyle = SLineStyle[data.lineType]
+            ? SLineStyle[data.lineType]
+            : SLineStyle.Solid;
         // 折线点
         if (data.pointList) {
             let setPointList: SPoint[];
             setPointList = data.pointList.map(i => {
-                return new SPoint(i.x, i.y)
+                return new SPoint(i.x, i.y);
             });
             this.pointList = setPointList;
         }
 
         //锚点
         if (data.node1Id) {
-            this._node1Id = data.node1Id
+            this._node1Id = data.node1Id;
         }
 
         if (data.node2Id) {
-            this._node2Id = data.node2Id
+            this._node2Id = data.node2Id;
         }
 
         if (data.anchor1Id) {
-            this._anchor1Id = data.anchor1Id
+            this._anchor1Id = data.anchor1Id;
         }
 
         if (data.anchor2Id) {
-            this._anchor2Id = data.anchor2Id
+            this._anchor2Id = data.anchor2Id;
         }
 
         // 样式
         if (data.style && data.style.default) {
             // 颜色
             if (data.style.default.strokeColor) {
-                this.strokeColor = new SColor(data.style.default.strokeColor)
+                this.strokeColor = new SColor(data.style.default.strokeColor);
             }
 
             // 线宽
             if (data.style.default.lineWidth) {
-                this.lineWidth = data.style.default.lineWidth
+                this.lineWidth = data.style.default.lineWidth;
             }
-        };
-
+        }
     }
 
-
     /**
      * 鼠标按下事件
      *
@@ -186,7 +186,7 @@ export class SBasePipe extends SBasePolylineEdit {
             // 判断删除equip后,不移动
             if (this.startAnchor.parent && this.startAnchor.parent.parent) {
                 let p = this.startAnchor.mapToScene(0, 0);
-                this.pointList[0] = new SPoint(p.x - this.x, p.y - this.y)
+                this.pointList[0] = new SPoint(p.x - this.x, p.y - this.y);
             }
         }
 
@@ -194,9 +194,10 @@ export class SBasePipe extends SBasePolylineEdit {
             // 删除equip后
             if (this.endAnchor.parent && this.endAnchor.parent.parent) {
                 let p = this.endAnchor.mapToScene(0, 0);
-                this.pointList[
-                    this.pointList.length - 1
-                ] = new SPoint(p.x - this.x, p.y - this.y)
+                this.pointList[this.pointList.length - 1] = new SPoint(
+                    p.x - this.x,
+                    p.y - this.y
+                );
             }
         }
     }
@@ -216,13 +217,13 @@ export class SBasePipe extends SBasePolylineEdit {
                 return {
                     x: pos.x,
                     y: pos.y
-                }
+                };
             });
             data.pointList = Line;
         }
 
         data.style.default.strokeColor = this.strokeColor.value;
         data.style.default.lineWidth = this.lineWidth;
-        return data
+        return data;
     }
-}
+}

+ 1 - 1
persagy-web-big/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@persagy-web/big",
-    "version": "2.2.36",
+    "version": "2.2.37",
     "description": "博锐尚格建筑信息化库",
     "main": "lib/index.js",
     "types": "lib/index.d.js",

+ 7 - 5
persagy-web-big/src/index.ts

@@ -49,10 +49,11 @@ import unzipBase64 from "./utils/unzipBase64";
 import { SPipeItem } from "./items/SPipeItem";
 import { SEquipItem } from "./items/SEquipItem";
 import { SSceneMaskItem } from "./items/SSceneMaskItem";
-import { ElementData } from "./types/ElementData"
-import { Legend } from "./types/Legend"
-import { Marker } from "./types/Marker"
-import { Relation } from "./types/Relation"
+import { ElementData } from "./types/ElementData";
+import { Legend } from "./types/Legend";
+import { Marker } from "./types/Marker";
+import { Relation } from "./types/Relation";
+import SCircleCornerPolylineItem from "./items/SCircleCornerPolylineItem";
 
 export {
     ElementData,
@@ -83,5 +84,6 @@ export {
     unzipBase64,
     SPipeItem,
     SEquipItem,
-    SSceneMaskItem
+    SSceneMaskItem,
+    SCircleCornerPolylineItem
 };

+ 174 - 0
persagy-web-big/src/items/SCircleCornerPolylineItem.ts

@@ -0,0 +1,174 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2021.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+import {
+    SGraphItem,
+    SGraphStyleItem,
+    SLineStyle
+} from "@persagy-web/graph/lib";
+import { SPoint, SPainter, SRect, SPath } from "@persagy-web/draw";
+
+/**
+ * 圆角折线 item
+ *
+ * @author 郝建龙 <haojianlong@sagacloud.com>
+ */
+export default class SCircleCornerPolylineItem extends SGraphStyleItem {
+    /** X 坐标最小值 */
+    private minX = Number.MAX_SAFE_INTEGER;
+    /** X 坐标最大值 */
+    private maxX = Number.MIN_SAFE_INTEGER;
+    /** Y 坐标最小值 */
+    private minY = Number.MAX_SAFE_INTEGER;
+    /** Y 坐标最大值 */
+    private maxY = Number.MIN_SAFE_INTEGER;
+    /** 折点信息 */
+    pointList: SPoint[] = [];
+    /** 圆角半径 */
+    private _radius: number = 5;
+    get radius(): number {
+        return this._radius;
+    }
+    set radius(v: number) {
+        if (v === this._radius) {
+            return;
+        }
+        this._radius = v;
+        this.update();
+    }
+    /** 圆角半径是否需要转像素值 */
+    radiusIsPx: boolean = false;
+    /** path 对象 */
+    path: SPath = new SPath();
+
+    /**
+     * 构造函数
+     */
+    constructor(parent: SGraphItem | null, list: SPoint[]) {
+        super(parent);
+        this.pointList = list;
+    }
+
+    /**
+     * Item 对象边界区域
+     *
+     * @return 外接矩阵
+     */
+    boundingRect(): SRect {
+        if (this.pointList.length) {
+            this.minX = this.pointList[0].x;
+            this.maxX = this.pointList[0].x;
+            this.minY = this.pointList[0].y;
+            this.maxY = this.pointList[0].y;
+            this.pointList.forEach((it): void => {
+                let x = it.x,
+                    y = it.y;
+
+                // 如果数据 x 坐标小于 x 坐标最小值
+                if (x < this.minX) {
+                    this.minX = x;
+                }
+
+                // 如果数据 y 坐标小于 y 坐标最小值
+                if (y < this.minY) {
+                    this.minY = y;
+                }
+
+                // 如果数据 x 坐标大于 x 坐标最小值
+                if (x > this.maxX) {
+                    this.maxX = x;
+                }
+
+                // 如果数据 y 坐标大于 y 坐标最小值
+                if (y > this.maxY) {
+                    this.maxY = y;
+                }
+            });
+        }
+
+        return new SRect(
+            this.minX,
+            this.minY,
+            this.maxX - this.minX,
+            this.maxY - this.minY
+        );
+    }
+
+    /**
+     * 根据点集生成 path 对象
+     *
+     * @param list  需要生成的点击
+     * @param r     拐点处圆角半径
+     */
+    generatePath(list: SPoint[], r: number = 0): void {
+        const len = list.length;
+        if (len) {
+            this.path = new SPath();
+            this.path.moveTo(list[0].x, list[0].y);
+            for (let i = 1; i < len - 1; i++) {
+                const temp = list[i];
+                const next = list[i + 1];
+                this.path.arcTo(temp.x, temp.y, next.x, next.y, r);
+            }
+            const last = list[len - 1];
+            this.path.lineTo(last.x, last.y);
+        }
+    }
+
+    /**
+     * Item 绘制操作
+     *
+     * @param painter   绘制对象
+     */
+    onDraw(painter: SPainter): void {
+        // 绘制基本图形
+        painter.pen.color = this.strokeColor;
+        if (this.lineStyle == SLineStyle.Dashed) {
+            painter.pen.lineDash = [
+                painter.toPx(this.lineWidth * 3),
+                painter.toPx(this.lineWidth * 7)
+            ];
+        } else if (this.lineStyle == SLineStyle.Dotted) {
+            painter.pen.lineDash = [
+                painter.toPx(this.lineWidth),
+                painter.toPx(this.lineWidth)
+            ];
+        }
+
+        const temp = JSON.parse(JSON.stringify(this.pointList));
+
+        if (temp.length > 2) {
+            let radius = this.radius;
+            if (this.radiusIsPx) {
+                radius = painter.toPx(this.radius);
+            }
+            this.generatePath(temp, radius);
+            painter.drawPath(this.path);
+        } else {
+            painter.drawPolyline(temp);
+        }
+    }
+}

+ 54 - 28
persagy-web-draw/src/SPainter.ts

@@ -49,7 +49,6 @@ import { SShadow } from "./SShadow";
  * @author 庞利祥 <sybotan@126.com>
  */
 export class SPainter extends SObject {
-
     /** 绘制引擎 */
     private readonly engine: SPaintEngine;
 
@@ -118,7 +117,8 @@ export class SPainter extends SObject {
         if (engine instanceof SCanvasView) {
             // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
             this.engine = new SCanvasPaintEngine(engine.canvas!!);
-        } else { // 入参是 绘制引擎
+        } else {
+            // 入参是 绘制引擎
             this.engine = engine;
         }
 
@@ -271,11 +271,14 @@ export class SPainter extends SObject {
         // 重载   clearRect(rect: SRect): void;
         if (x instanceof SRect) {
             this.engine.clearRect(x);
-        } else if (x instanceof SPoint && y instanceof SPoint) { // 重载 clearRect(leftTop: SPoint, rightBottom: SPoint): void;
+        } else if (x instanceof SPoint && y instanceof SPoint) {
+            // 重载 clearRect(leftTop: SPoint, rightBottom: SPoint): void;
             this.engine.clearRect(new SRect(x, y));
-        } else if (x instanceof SPoint && y instanceof SSize) { // 重载  clearRect(leftTop: SPoint, size: SSize): void;
+        } else if (x instanceof SPoint && y instanceof SSize) {
+            // 重载  clearRect(leftTop: SPoint, size: SSize): void;
             this.engine.clearRect(new SRect(x, y));
-        } else { // 重载 clearRect(x: number, y: number, w: number, h: number): void;
+        } else {
+            // 重载 clearRect(x: number, y: number, w: number, h: number): void;
             this.engine.clearRect(
                 new SRect(x as number, y as number, w as number, h as number)
             );
@@ -335,14 +338,17 @@ export class SPainter extends SObject {
             if (x.x < x.right && x.y < x.bottom) {
                 this.engine.drawRect(x);
             }
-        } else if (x instanceof SPoint && y instanceof SPoint) { // 重载 drawRect(leftTop: SPoint, rightBottom: SPoint): void;
+        } else if (x instanceof SPoint && y instanceof SPoint) {
+            // 重载 drawRect(leftTop: SPoint, rightBottom: SPoint): void;
             // 判断2点是否有效
             if (x.x < y.x && x.y < y.y) {
                 this.engine.drawRect(new SRect(x, y));
             }
-        } else if (x instanceof SPoint && y instanceof SSize) { // 重载 drawRect(leftTop: SPoint, size: SSize): void;
+        } else if (x instanceof SPoint && y instanceof SSize) {
+            // 重载 drawRect(leftTop: SPoint, size: SSize): void;
             this.engine.drawRect(new SRect(x, y));
-        } else { // 重载 drawRect(x: number, y: number, w: number, h: number): void;
+        } else {
+            // 重载 drawRect(x: number, y: number, w: number, h: number): void;
             this.engine.drawRect(
                 new SRect(x as number, y as number, w as number, h as number)
             );
@@ -409,13 +415,16 @@ export class SPainter extends SObject {
         if (x instanceof SRect) {
             rect = x;
             r = y as number;
-        } else if (x instanceof SPoint && y instanceof SSize) { // 重载 drawRoundRect(pos: SPoint, size: SSize, r: number): void;
+        } else if (x instanceof SPoint && y instanceof SSize) {
+            // 重载 drawRoundRect(pos: SPoint, size: SSize, r: number): void;
             rect = new SRect(x, y);
             r = w as number;
-        } else if (x instanceof SPoint && y instanceof SPoint) { // 重载 drawRoundRect(p1: SPoint, p2: SPoint, r: number): void;
+        } else if (x instanceof SPoint && y instanceof SPoint) {
+            // 重载 drawRoundRect(p1: SPoint, p2: SPoint, r: number): void;
             rect = new SRect(x, y);
             r = w as number;
-        } else { // 重载 drawRoundRect(x: number, y: number, w: number, h: number, r: number): void;
+        } else {
+            // 重载 drawRoundRect(x: number, y: number, w: number, h: number, r: number): void;
             rect = new SRect(
                 x as number,
                 y as number,
@@ -496,9 +505,11 @@ export class SPainter extends SObject {
         // 重载 drawLine(line: SLine): void;
         if (x1 instanceof SLine) {
             this.engine.drawLine(x1);
-        } else if (x1 instanceof SPoint && y1 instanceof SPoint) { // 重载 drawLine(p1: SPoint, p2: SPoint): void;
+        } else if (x1 instanceof SPoint && y1 instanceof SPoint) {
+            // 重载 drawLine(p1: SPoint, p2: SPoint): void;
             this.engine.drawLine(new SLine(x1, y1));
-        } else { // 重载 drawLine(x1: number, y1: number, x2: number, y2: number): void;
+        } else {
+            // 重载 drawLine(x1: number, y1: number, x2: number, y2: number): void;
             this.engine.drawLine(
                 new SLine(
                     x1 as number,
@@ -643,10 +654,12 @@ export class SPainter extends SObject {
         if (x1 instanceof SLine) {
             line = x1;
             style = y1 as SArrow;
-        } else if (x1 instanceof SPoint && y1 instanceof SPoint) { // 重载 drawArrowLine(p1: SPoint, p2: SPoint, style?: SArrow): void;
+        } else if (x1 instanceof SPoint && y1 instanceof SPoint) {
+            // 重载 drawArrowLine(p1: SPoint, p2: SPoint, style?: SArrow): void;
             line = new SLine(x1, y1);
             style = x2 as SArrow;
-        } else { // 重载 drawArrowLine(x1: number, y1: number, x2: number, y2: number,style?: SArrow): void;
+        } else {
+            // 重载 drawArrowLine(x1: number, y1: number, x2: number, y2: number,style?: SArrow): void;
             line = new SLine(
                 x1 as number,
                 y1 as number,
@@ -664,13 +677,17 @@ export class SPainter extends SObject {
                 // 起始点为基本类型
                 if (style.begin == SArrowStyleType.Basic) {
                     this.drawBasicArrow(line, false);
-                } else if (style.begin == SArrowStyleType.Triangle) { // 起始点为三角型类型
+                } else if (style.begin == SArrowStyleType.Triangle) {
+                    // 起始点为三角型类型
                     this.drawTriangleArrow(line, false);
-                } else if (style.begin == SArrowStyleType.Diamond) { // 起始点为菱形类型
+                } else if (style.begin == SArrowStyleType.Diamond) {
+                    // 起始点为菱形类型
                     this.drawDiamondArrow(line, false);
-                } else if (style.begin == SArrowStyleType.Square) { // 起始点为正方形类型
+                } else if (style.begin == SArrowStyleType.Square) {
+                    // 起始点为正方形类型
                     this.drawSquareArrow(line, false);
-                } else if (style.begin == SArrowStyleType.Circle) { // 起始点为圆形类型
+                } else if (style.begin == SArrowStyleType.Circle) {
+                    // 起始点为圆形类型
                     this.drawCircleArrow(line, false);
                 }
             }
@@ -679,13 +696,17 @@ export class SPainter extends SObject {
                 // 结束点为基本类型
                 if (style.end == SArrowStyleType.Basic) {
                     this.drawBasicArrow(line, true);
-                } else if (style.end == SArrowStyleType.Triangle) { // 结束点为三角型类型
+                } else if (style.end == SArrowStyleType.Triangle) {
+                    // 结束点为三角型类型
                     this.drawTriangleArrow(line, true);
-                } else if (style.end == SArrowStyleType.Diamond) { // 结束点为菱形类型
+                } else if (style.end == SArrowStyleType.Diamond) {
+                    // 结束点为菱形类型
                     this.drawDiamondArrow(line, true);
-                } else if (style.end == SArrowStyleType.Square) { // 结束点为正方形类型
+                } else if (style.end == SArrowStyleType.Square) {
+                    // 结束点为正方形类型
                     this.drawSquareArrow(line, true);
-                } else if (style.end == SArrowStyleType.Circle) { // 结束点为圆形类型
+                } else if (style.end == SArrowStyleType.Circle) {
+                    // 结束点为圆形类型
                     this.drawCircleArrow(line, true);
                 }
             }
@@ -719,7 +740,8 @@ export class SPainter extends SObject {
                 new SPoint(0, 0),
                 new SPoint(-x1, -y1)
             ]);
-        } else { // 终点不绘画箭头
+        } else {
+            // 终点不绘画箭头
             this.translate(line.x1, line.y1);
             this.rotate((fo * 180) / Math.PI);
             this.engine.drawPolyline([
@@ -758,7 +780,8 @@ export class SPainter extends SObject {
                 new SPoint(0, 0),
                 new SPoint(-x1, -y1)
             ]);
-        } else { // 终点不绘制箭头
+        } else {
+            // 终点不绘制箭头
             this.translate(line.x1, line.y1);
             this.rotate((fo * 180) / Math.PI);
             this.engine.drawPolygon([
@@ -798,7 +821,8 @@ export class SPainter extends SObject {
                 new SPoint(-x1, -y1),
                 new SPoint(-Math.sqrt(2) * d, 0)
             ]);
-        } else { // 终点不绘画箭头
+        } else {
+            // 终点不绘画箭头
             this.translate(line.x1, line.y1);
             this.rotate((fo * 180) / Math.PI);
             this.engine.drawPolygon([
@@ -835,7 +859,8 @@ export class SPainter extends SObject {
                 new SPoint(0, -d / 2),
                 new SPoint(-d, -d / 2)
             ]);
-        } else { // 终点不绘画箭头
+        } else {
+            // 终点不绘画箭头
             this.translate(line.x1, line.y1);
             this.rotate((fo * 180) / Math.PI);
             this.engine.drawPolygon([
@@ -867,7 +892,8 @@ export class SPainter extends SObject {
             this.translate(line.x2, line.y2);
             this.rotate((fo * 180) / Math.PI);
             this.engine.drawCircle(-d / 2, 0, d / 2);
-        } else { // 不绘画箭头
+        } else {
+            // 不绘画箭头
             this.translate(line.x1, line.y1);
             this.rotate((fo * 180) / Math.PI);
             this.engine.drawCircle(d / 2, 0, d / 2);

+ 1 - 1
persagy-web-draw/src/engines/SCanvasPaintEngine.ts

@@ -627,6 +627,7 @@ export class SCanvasPaintEngine extends SPaintEngine {
                 this._canvas.moveTo(cmd.args[0], cmd.args[1]);
             } else if (cmd.command == "Z") {
                 this._canvas.closePath();
+                this._canvas.fill();
             } else if (cmd.command == "Q") {
                 this._canvas.quadraticCurveTo(
                     cmd.args[0],
@@ -672,7 +673,6 @@ export class SCanvasPaintEngine extends SPaintEngine {
                 );
             }
         });
-        this._canvas.fill();
         this._canvas.stroke();
     }
 }

+ 2 - 2
persagy-web-edit/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@persagy-web/edit",
-    "version": "2.2.10",
+    "version": "2.2.11",
     "description": "博锐尚格二维图形编辑器。",
     "main": "lib/index.js",
     "types": "lib/index.d.js",
@@ -40,7 +40,7 @@
         "typescript": "^3.5.3"
     },
     "dependencies": {
-        "@persagy-web/big": "2.2.36",
+        "@persagy-web/big": "2.2.37",
         "@types/uuid": "^8.0.0"
     }
 }

+ 25 - 2
persagy-web-edit/src/index.ts

@@ -35,7 +35,7 @@ import { SBaseCircleEdit } from "./items/SBaseCircleEdit";
 import { SBaseTriangleEdit } from "./items/SBaseTriangleEdit";
 import { SBasePolylineEdit } from "./items/SBasePolylineEdit";
 import { SBaseIconTextEdit } from "./items/SBaseIconTextEdit";
-import { SBaseTextEdit } from "./items/SBaseTextEdit" ;
+import { SBaseTextEdit } from "./items/SBaseTextEdit";
 import { SGraphCommand } from "./commands/SGraphCommand";
 import { SGraphAddCommand } from "./commands/SGraphAddCommand";
 import { SGraphMoveCommand } from "./commands/SGraphMoveCommand";
@@ -44,5 +44,28 @@ import { SGraphPointListDelete } from "./commands/SGraphPointListDelete";
 import { SGraphPointListInsert } from "./commands/SGraphPointListInsert";
 import { SGraphPointListUpdate } from "./commands/SGraphPointListUpdate";
 import { SGraphPropertyCommand } from "./commands/SGraphPropertyCommand";
+import SBaseCirclePolylineEdit from "./items/SBaseCirclePolylineEdit";
 
-export {SGraphDeleteListCommand,SBasePolylineEdit, SBaseIconTextEdit, SBasePolygonEdit, SBaseTextEdit, SBaseImageEdit, SBaseLineEdit, SBaseCircleEdit, SBaseTriangleEdit, SBaseRectEdit, SBaseArrowEdit, SGraphEditScene, SGraphEdit, SGraphCommand, SGraphAddCommand, SGraphMoveCommand, SGraphPointListDelete, SGraphPointListInsert, SGraphPointListUpdate, SGraphPropertyCommand }
+export {
+    SGraphDeleteListCommand,
+    SBasePolylineEdit,
+    SBaseIconTextEdit,
+    SBasePolygonEdit,
+    SBaseTextEdit,
+    SBaseImageEdit,
+    SBaseLineEdit,
+    SBaseCircleEdit,
+    SBaseTriangleEdit,
+    SBaseRectEdit,
+    SBaseArrowEdit,
+    SGraphEditScene,
+    SGraphEdit,
+    SGraphCommand,
+    SGraphAddCommand,
+    SGraphMoveCommand,
+    SGraphPointListDelete,
+    SGraphPointListInsert,
+    SGraphPointListUpdate,
+    SGraphPropertyCommand,
+    SBaseCirclePolylineEdit
+};

+ 762 - 0
persagy-web-edit/src/items/SBaseCirclePolylineEdit.ts

@@ -0,0 +1,762 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2016-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+import {
+    SColor,
+    SLine,
+    SPainter,
+    SPoint,
+    SRect,
+    SPath
+} from "@persagy-web/draw";
+import { SKeyCode, SMouseEvent, SUndoStack } from "@persagy-web/base";
+import { SItemStatus } from "@persagy-web/big";
+import { SMathUtil } from "@persagy-web/big/lib/utils/SMathUtil";
+import {
+    SGraphPointListDelete,
+    SGraphPointListInsert,
+    SGraphPointListUpdate,
+    SLineStyle,
+    SGraphItem
+} from "@persagy-web/graph/";
+import { SGraphEdit } from "..";
+import { Marker } from "../type/Marker";
+
+/**
+ * 折线编辑类
+ *
+ * @author  韩耀龙 <han_yao_long@163.com>
+ */
+export default class SBaseCirclePolylineEdit extends SGraphEdit {
+    /** 传入数据 */
+    _data: Marker | null = null;
+    get data(): Marker | null {
+        return this._data;
+    } // Get data
+    set data(v: Marker | null) {
+        this._data = v;
+        this.initData();
+        this.update();
+    } // Set data
+
+    /** X 坐标最小值 */
+    private minX = Number.MAX_SAFE_INTEGER;
+    /** X 坐标最大值 */
+    private maxX = Number.MIN_SAFE_INTEGER;
+    /** Y 坐标最小值 */
+    private minY = Number.MAX_SAFE_INTEGER;
+    /** Y 坐标最大值 */
+    private maxY = Number.MIN_SAFE_INTEGER;
+    /** 折点信息 */
+    pointList: SPoint[] = [];
+    /** 是否绘制完成 */
+    _status: SItemStatus = SItemStatus.Normal;
+    get status(): SItemStatus {
+        return this._status;
+    }
+    set status(v: SItemStatus) {
+        this._status = v;
+        this.undoStack.clear();
+        this.update();
+    }
+
+    /** 鼠标移动时的点 */
+    private lastPoint: SPoint | null = null;
+    /** 线条颜色 */
+    _strokeColor: SColor = SColor.Black;
+    get strokeColor(): SColor {
+        return this._strokeColor;
+    }
+    set strokeColor(v: SColor) {
+        this._strokeColor = v;
+        this.update();
+    }
+
+    /** 填充色 */
+    _fillColor: SColor = new SColor("#2196f3");
+    get fillColor(): SColor {
+        return this._fillColor;
+    }
+    set fillColor(v: SColor) {
+        this._fillColor = v;
+        this.update();
+    }
+
+    /** 边框样式 */
+    _lineStyle: SLineStyle = SLineStyle.Solid;
+    get lineStyle(): SLineStyle {
+        return this._lineStyle;
+    }
+    set lineStyle(v: SLineStyle) {
+        this._lineStyle = v;
+        this.update();
+    }
+
+    /** 线条宽度 */
+    _lineWidth: number = 1;
+    get lineWidth(): number {
+        return this._lineWidth;
+    }
+    set lineWidth(v: number) {
+        this._lineWidth = v;
+        this.update();
+    }
+
+    /** 是否垂直水平绘制 */
+    private _verAndLeve: boolean = false;
+    get verAndLeve(): boolean {
+        return this._verAndLeve;
+    }
+    set verAndLeve(bool: boolean) {
+        this._verAndLeve = bool;
+        this.update();
+    }
+
+    /** 全局灵敏度 */
+    dis: number = 10;
+    /** 灵敏度转换为场景长度 */
+    private sceneDis: number = 10;
+    /** 当前点索引 */
+    private curIndex: number = -1;
+    /** 当前点 */
+    private curPoint: SPoint | null = null;
+    /** undo / redo 堆栈 */
+    private undoStack: SUndoStack = new SUndoStack();
+    /** 圆角半径 */
+    private _radius: number = 5;
+    get radius(): number {
+        return this._radius;
+    }
+    set radius(v: number) {
+        if (v === this._radius) {
+            return;
+        }
+        this._radius = v;
+        this.update();
+    }
+    /** 圆角半径是否需要转像素值 */
+    radiusIsPx: boolean = false;
+    /** path 对象 */
+    path: SPath = new SPath();
+
+    /**
+     * 构造函数
+     *
+     * @param parent    父级
+     * @param data      折线数据
+     */
+    constructor(parent: null | SGraphItem, data: Marker | null = null) {
+        super(parent);
+        if (data) {
+            this.data = data;
+        }
+    } // Constructor
+
+    /**
+     * 初始化 data 数据
+     */
+    initData() {
+        const data = this.data;
+        if (!data) return;
+        this.name = data.name;
+        this.moveTo(data.pos.x, data.pos.y);
+        this.showSelect = false;
+        if (data && data.style) {
+            if (data.style.outLine) {
+                let setPointList: SPoint[];
+                setPointList = data.style.outLine.map(i => {
+                    return new SPoint(i.x, i.y);
+                });
+                this.pointList = setPointList;
+            }
+
+            this.moveable = data.style.isMove ? data.style.isMove : false;
+            if (data.style && data.style.default) {
+                // 颜色
+                if (data.style.default.strokeColor) {
+                    this.strokeColor = new SColor(
+                        data.style.default.strokeColor
+                    );
+                }
+
+                // 线宽
+                if (data.style.default.lineWidth) {
+                    this.lineWidth = data.style.default.lineWidth;
+                }
+
+                // 线样式
+                if (data.style.default.lineStyle) {
+                    this.lineStyle = data.style.default.lineStyle;
+                }
+            }
+        }
+    } // Function initData()
+
+    /**
+     * 添加点至数组中
+     *
+     * @param p       添加的点
+     * @param index   添加到的索引
+     */
+    private addPoint(p: SPoint, index?: number): void {
+        if (index && this.canHandle(index)) {
+            this.pointList.splice(index, 0, p);
+            this.recordAction(SGraphPointListInsert, [
+                this.pointList,
+                p,
+                index
+            ]);
+        } else {
+            this.pointList.push(p);
+            this.recordAction(SGraphPointListInsert, [this.pointList, p]);
+        }
+
+        this.update();
+    }
+
+    /**
+     * 是否可以添加点到数组中
+     *
+     * @param index     要添加到的索引
+     * @return 是否可添加
+     */
+    private canHandle(index: number): boolean {
+        return index >= 0 && index <= this.pointList.length;
+    }
+
+    /**
+     * 根据索引删除点
+     *
+     * @param index   删除点
+     */
+    deletePoint(index: number): void {
+        if (this.canHandle(index) && this.pointList.length > 2) {
+            const p = new SPoint(
+                this.pointList[this.curIndex].x,
+                this.pointList[this.curIndex].y
+            );
+            this.pointList.splice(index, 1);
+            this.recordAction(SGraphPointListDelete, [
+                this.pointList,
+                p,
+                index
+            ]);
+            this.curIndex = -1;
+            this.curPoint = null;
+            this.update();
+        }
+    }
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param event   鼠标事件
+     * @return 是否处理事件
+     */
+    onMouseDown(event: SMouseEvent): boolean {
+        this.curIndex = -1;
+        this.curPoint = null;
+        if (event.shiftKey || this.verAndLeve) {
+            event = this.compare(event);
+        }
+
+        if (event.buttons == 1) {
+            if (this.status == SItemStatus.Create) {
+                this.addPoint(new SPoint(event.x, event.y));
+                return true;
+            } else if (this.status == SItemStatus.Edit) {
+                // 查询鼠标最近的索引
+                this.findNearestPoint(new SPoint(event.x, event.y));
+                // 增加点
+                if (this.curIndex < 0) {
+                    this.findAddPos(new SPoint(event.x, event.y));
+                }
+
+                // 删除点
+                if (event.altKey && this.canHandle(this.curIndex)) {
+                    this.deletePoint(this.curIndex);
+                }
+
+                this.update();
+                return true;
+            } else {
+                return super.onMouseDown(event);
+            }
+        }
+
+        return super.onMouseDown(event);
+    }
+
+    /**
+     * 鼠标移动事件
+     *
+     * @param event   鼠标事件
+     * @return 是否处理事件
+     */
+    onMouseMove(event: SMouseEvent): boolean {
+        if (event.shiftKey || this.verAndLeve) {
+            event = this.compare(event);
+        }
+
+        if (this.status == SItemStatus.Create) {
+            if (this.lastPoint) {
+                this.lastPoint.x = event.x;
+                this.lastPoint.y = event.y;
+            } else {
+                this.lastPoint = new SPoint(event.x, event.y);
+            }
+
+            this.update();
+            return true;
+        } else if (this.status == SItemStatus.Edit) {
+            if (event.buttons == 1) {
+                if (this.canHandle(this.curIndex)) {
+                    this.pointList[this.curIndex].x = event.x;
+                    this.pointList[this.curIndex].y = event.y;
+                }
+            }
+
+            this.update();
+            return true;
+        } else {
+            return super.onMouseMove(event);
+        }
+    }
+
+    /**
+     * 鼠标移动事件
+     *
+     * @param event   鼠标事件
+     * @return 是否处理事件
+     */
+    onMouseUp(event: SMouseEvent): boolean {
+        if (this.status == SItemStatus.Edit) {
+            if (this.curIndex > -1) {
+                const p = new SPoint(
+                    this.pointList[this.curIndex].x,
+                    this.pointList[this.curIndex].y
+                );
+                this.recordAction(SGraphPointListUpdate, [
+                    this.pointList,
+                    this.curPoint,
+                    p,
+                    this.curIndex
+                ]);
+            }
+        } else if (this.status == SItemStatus.Normal) {
+            super.onMouseUp(event);
+            return true;
+        }
+
+        return true;
+    }
+
+    /**
+     * 鼠标双击事件
+     *
+     * @param event     事件参数
+     * @return 是否处理事件
+     */
+    onDoubleClick(event: SMouseEvent): boolean {
+        // 如果为show状态 双击改对象则需改为编辑状态
+        if (this.status == SItemStatus.Normal) {
+            this.status = SItemStatus.Edit;
+            this.grabItem(this);
+        } else if (this.status == SItemStatus.Edit) {
+            // 编辑状态
+            this.status = SItemStatus.Normal;
+            this.releaseItem();
+        } else if (this.status == SItemStatus.Create) {
+            // 创建状态
+            if (this.pointList.length > 1) {
+                this.status = SItemStatus.Normal;
+                this.releaseItem();
+                this.$emit("finishCreated");
+            }
+        }
+
+        this.$emit("onDoubleClick", event);
+        return true;
+    }
+
+    /**
+     * 键盘按键弹起事件
+     *
+     * @param event     事件参数
+     */
+    onKeyUp(event: KeyboardEvent): void {
+        if (event.keyCode == SKeyCode.Enter) {
+            if (this.pointList.length > 1) {
+                if (this.status == SItemStatus.Create) {
+                    this.$emit("finishCreated");
+                }
+
+                this.status = SItemStatus.Normal;
+                this.releaseItem();
+            }
+        }
+        // delete删除点
+        if (
+            event.keyCode == SKeyCode.Delete &&
+            this.status == SItemStatus.Edit
+        ) {
+            this.deletePoint(this.curIndex);
+        }
+    }
+
+    /**
+     * 移动后处理所有坐标,并肩原点置为场景原点
+     */
+    moveToOrigin(): void {
+        this.pointList = this.pointList.map(t => {
+            t.x = t.x + this.x;
+            t.y = t.y + this.y;
+            return t;
+        });
+        this.x = 0;
+        this.y = 0;
+    }
+
+    /**
+     * 获取点击点与点集中距离最近点
+     *
+     * @param p   鼠标点击点
+     */
+    findNearestPoint(p: SPoint): void {
+        let len = this.sceneDis;
+        for (let i = 0; i < this.pointList.length; i++) {
+            let dis = SMathUtil.pointDistance(
+                p.x,
+                p.y,
+                this.pointList[i].x,
+                this.pointList[i].y
+            );
+            if (dis < len) {
+                len = dis;
+                this.curIndex = i;
+                this.curPoint = new SPoint(
+                    this.pointList[this.curIndex].x,
+                    this.pointList[this.curIndex].y
+                );
+            }
+        }
+    }
+
+    /**
+     * 计算增加点的位置
+     *
+     * @param p   鼠标点击点
+     */
+    findAddPos(p: SPoint): void {
+        let len = SMathUtil.pointToLine(
+                p,
+                new SLine(this.pointList[0], this.pointList[1])
+            ),
+            index = 0;
+        if (this.pointList.length > 2) {
+            for (let i = 1; i < this.pointList.length - 1; i++) {
+                let dis = SMathUtil.pointToLine(
+                    p,
+                    new SLine(this.pointList[i], this.pointList[i + 1])
+                );
+                if (dis.MinDis < len.MinDis) {
+                    len = dis;
+                    index = i;
+                }
+            }
+        }
+
+        if (len.MinDis < this.sceneDis) {
+            if (len.Point) {
+                this.addPoint(len.Point, index + 1);
+            }
+        }
+    }
+
+    /**
+     * shift 垂直水平创建或编辑
+     *
+     * @param event   事件
+     * @return 处理后的鼠标事件
+     */
+    compare(event: SMouseEvent): SMouseEvent {
+        if (this.pointList.length) {
+            let last = new SPoint(event.x, event.y);
+            if (this.status == SItemStatus.Create) {
+                last = this.pointList[this.pointList.length - 1];
+            } else if (this.status == SItemStatus.Edit) {
+                if (this.curIndex > 1) {
+                    last = this.pointList[this.curIndex - 1];
+                }
+            }
+
+            const dx = Math.abs(event.x - last.x);
+            const dy = Math.abs(event.y - last.y);
+            if (dy > dx) {
+                event.x = last.x;
+            } else {
+                event.y = last.y;
+            }
+        }
+
+        return event;
+    }
+
+    /**
+     * 记录相关动作并推入栈中
+     *
+     * @param SGraphCommand     相关命令类
+     * @param any               对应传入参数
+     */
+    protected recordAction(SGraphCommand: any, any: any[]): void {
+        // 记录相关命令并推入堆栈中
+        const command = new SGraphCommand(this.scene, this, ...any);
+        this.undoStack.push(command);
+    }
+
+    /**
+     * Item 对象边界区域
+     *
+     * @return 对象边界区域
+     */
+    boundingRect(): SRect {
+        if (this.pointList.length) {
+            this.minX = this.pointList[0].x;
+            this.maxX = this.pointList[0].x;
+            this.minY = this.pointList[0].y;
+            this.maxY = this.pointList[0].y;
+            this.pointList.forEach(it => {
+                let x = it.x,
+                    y = it.y;
+                if (x < this.minX) {
+                    this.minX = x;
+                }
+
+                if (y < this.minY) {
+                    this.minY = y;
+                }
+
+                if (x > this.maxX) {
+                    this.maxX = x;
+                }
+
+                if (y > this.maxY) {
+                    this.maxY = y;
+                }
+            });
+        }
+
+        return new SRect(
+            this.minX,
+            this.minY,
+            this.maxX - this.minX,
+            this.maxY - this.minY
+        );
+    }
+
+    /**
+     * 判断点是否在区域内
+     *
+     * @param x     x 坐标
+     * @param y     y 坐标
+     * @return 是否在区域内
+     */
+    contains(x: number, y: number): boolean {
+        let p = new SPoint(x, y);
+        for (let i = 1; i < this.pointList.length; i++) {
+            let PTL = SMathUtil.pointToLine(
+                p,
+                new SLine(
+                    this.pointList[i - 1].x,
+                    this.pointList[i - 1].y,
+                    this.pointList[i].x,
+                    this.pointList[i].y
+                )
+            );
+            if (PTL.MinDis < this.sceneDis) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 撤销操作
+     */
+    undo(): void {
+        if (this._status != SItemStatus.Normal) {
+            this.undoStack.undo();
+        }
+    }
+
+    /**
+     * 重做操作
+     */
+    redo(): void {
+        if (this._status != SItemStatus.Normal) {
+            this.undoStack.redo();
+        }
+    }
+
+    /**
+     * 取消操作执行
+     */
+    cancelOperate(): void {
+        if (this.status == SItemStatus.Create) {
+            this.parent = null;
+            this.releaseItem();
+        } else if (this.status == SItemStatus.Edit) {
+            this.status = SItemStatus.Normal;
+            this.releaseItem();
+        }
+    }
+
+    /**
+     * 根据点集生成 path 对象
+     *
+     * @param list  需要生成的点击
+     * @param r     拐点处圆角半径
+     */
+    generatePath(list: SPoint[], r: number = 0): void {
+        const len = list.length;
+        if (len) {
+            this.path = new SPath();
+            this.path.moveTo(list[0].x, list[0].y);
+            for (let i = 1; i < len - 1; i++) {
+                const temp = list[i];
+                const next = list[i + 1];
+                this.path.arcTo(temp.x, temp.y, next.x, next.y, r);
+            }
+            const last = list[len - 1];
+            this.path.lineTo(last.x, last.y);
+        }
+    }
+
+    /**
+     * 绘制基本图形
+     *
+     * @param painter   绘制对象
+     */
+    drawBaseLine(painter: SPainter): void {
+        // 绘制基本图形
+        painter.pen.color = this.strokeColor;
+        if (this.lineStyle == SLineStyle.Dashed) {
+            painter.pen.lineDash = [
+                painter.toPx(this.lineWidth * 3),
+                painter.toPx(this.lineWidth * 7)
+            ];
+        } else if (this.lineStyle == SLineStyle.Dotted) {
+            painter.pen.lineDash = [
+                painter.toPx(this.lineWidth),
+                painter.toPx(this.lineWidth)
+            ];
+        }
+
+        const temp = JSON.parse(JSON.stringify(this.pointList));
+        if (this.status == SItemStatus.Create && this.lastPoint) {
+            temp.push(this.lastPoint);
+        }
+
+        if (temp.length > 2) {
+            let radius = this.radius;
+            if (this.radiusIsPx) {
+                radius = painter.toPx(this.radius);
+            }
+            this.generatePath(temp, radius);
+            painter.drawPath(this.path);
+        } else {
+            painter.drawPolyline(temp);
+        }
+    }
+
+    /**
+     * 返回对象储存的相关数据
+     *
+     * @return 对象储存的相关数据
+     */
+    toData(): any {
+        this.moveToOrigin();
+        if (!this.data) return;
+        const Line = this.pointList.map(pos => {
+            return {
+                x: pos.x,
+                y: pos.y
+            };
+        });
+        this.data.style.outLine = Line;
+        this.data.style.default.lineWidth = this.lineWidth;
+        this.data.style.default.lineStyle = this.lineStyle;
+        this.data.style.default.strokeColor = this.strokeColor.value;
+        return this.data;
+    }
+
+    /**
+     * Item 绘制操作
+     *
+     * @param painter 绘制对象
+     */
+    onDraw(painter: SPainter): void {
+        // 缓存场景长度
+        this.sceneDis = painter.toPx(this.dis);
+        // 创建状态
+        painter.pen.lineWidth = painter.toPx(this.lineWidth);
+        if (this.status == SItemStatus.Create && this.lastPoint) {
+            // 绘制基本图形
+            this.drawBaseLine(painter);
+            // 编辑状态
+            this.pointList.forEach((t, i): void => {
+                painter.brush.color = SColor.White;
+                if (i == this.curIndex) {
+                    painter.brush.color = this.fillColor;
+                }
+                painter.drawCircle(t.x, t.y, painter.toPx(5));
+            });
+        } else if (this.status == SItemStatus.Edit) {
+            // 绘制基本图形
+            this.drawBaseLine(painter);
+            // 编辑状态
+            this.pointList.forEach((t, i): void => {
+                painter.brush.color = SColor.White;
+                if (i == this.curIndex) {
+                    painter.brush.color = this.fillColor;
+                }
+                painter.drawCircle(t.x, t.y, painter.toPx(5));
+            });
+        } else {
+            // 查看状态,是否选中
+            if (this.selected) {
+                painter.pen.lineWidth = painter.toPx(this.lineWidth * 2);
+                painter.shadow.shadowBlur = 10;
+                painter.shadow.shadowColor = new SColor(`#00000033`);
+                painter.shadow.shadowOffsetX = 5;
+                painter.shadow.shadowOffsetY = 5;
+            }
+            // 绘制基本图形
+            this.drawBaseLine(painter);
+        }
+    }
+}