Browse Source

增加多边形item

haojianlong 5 years ago
parent
commit
918f1bb3b2
1 changed files with 671 additions and 0 deletions
  1. 671 0
      saga-web-big/src/items/SPolygonItem.ts

+ 671 - 0
saga-web-big/src/items/SPolygonItem.ts

@@ -0,0 +1,671 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *                      :*$@@%$*:                         ;:                ;;    ;;
+ *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
+ *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
+ *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
+ *                   =@*                                  %!              @ $= % %@=   =%@!      %=
+ *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
+ *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
+ *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
+ *          $@*   ;@@@%=!:                *@*
+ *          =@$    ;;;!=%@@@@=!           =@!
+ *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
+ *            ;%@@$=$@@%*       *@@@$=%@@%;
+ *               ::;::             ::;::                                              All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+import {
+    SGraphItem,
+    SGraphPointListInsert,
+    SGraphPointListDelete,
+    SGraphPointListUpdate
+} from "@saga-web/graph/lib";
+import { SMouseEvent, SUndoStack } from "@saga-web/base/";
+import {
+    SColor,
+    SLineCapStyle,
+    SPainter,
+    SPoint,
+    SRect,
+    SLine,
+    SPolygonUtil
+} from "@saga-web/draw";
+import { SItemStatus } from "..";
+import { SMathUtil } from "../utils/SMathUtil";
+
+/**
+ * 编辑多边形
+ *
+ * @author  韩耀龙
+ */
+export class SPolygonItem extends SGraphItem {
+    /** 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;
+    /** 轮廓线坐标  */
+    private pointList: SPoint[] = [];
+    // 获取当前状态
+    get getPointList(): SPoint[] {
+        return this.pointList;
+    }
+    // 编辑当前状态
+    set setPointList(arr: SPoint[]) {
+        this.pointList = arr;
+        this.update();
+    }
+    /** 是否闭合    */
+    closeFlag: boolean = false;
+    // 当前状态
+    _status: number = SItemStatus.Normal;
+    // 获取当前状态
+    get getStatus(): number {
+        return this._status;
+    }
+    // 编辑当前状态
+    set setStatus(value: number) {
+        this._status = value;
+        // 如果状态为show则需清栈
+        if (value == SItemStatus.Normal) {
+            if (this.undoStack) {
+                this.undoStack.clear();
+            }
+        }
+        this.update();
+    }
+    /** 边框颜色 */
+    borderColor: SColor = new SColor("#0091FF");
+    /** 填充颜色 */
+    brushColor: SColor = new SColor("#1EE887");
+    /** border宽 只可输入像素宽*/
+    borderLine: number = 4;
+    /** 鼠标移动点  */
+    private lastPoint: SPoint | null = null;
+    /** 当前鼠标获取顶点对应索引 */
+    private curIndex: number = -1;
+    /** 当前鼠标获取顶点对应坐标 */
+    private curPoint: null | SPoint = null;
+    /** 灵敏像素 */
+    private len: number = 10;
+    /** 场景像素 内部将灵敏像素换算为场景实际距离  */
+    private scenceLen: number = 15;
+    /** 场景像素  */
+    private isAlt: boolean = false;
+    /** undo/redo堆栈 */
+    private undoStack: SUndoStack | null = new SUndoStack();
+
+    /**
+     * 构造函数
+     *
+     * @param parent    指向父对象
+     */
+    constructor(parent: SGraphItem | null) {
+        super(parent);
+    }
+
+    //////////////////
+    //  以下为对pointList 数组的操作方法
+
+    /**
+     * 储存新的多边形顶点
+     *
+     * @param x   点位得x坐标
+     * @param y   点位得y坐标
+     * @param i   储存所在索引
+     * @return SPoint。添加的顶点
+     */
+    insertPoint(x: number, y: number, i: number | null = null): SPoint {
+        const point = new SPoint(x, y);
+        if (i == null) {
+            this.pointList.push(point);
+        } else {
+            this.pointList.splice(i, 0, point);
+        }
+        this.update();
+        return point;
+    }
+
+    /**
+     * 删除点位
+     *
+     * @param i   删除点所在的索引
+     * @return    SPoint|null。索引不在数组范围则返回null
+     */
+    deletePoint(i: number | null = null): SPoint | null {
+        let point = null;
+        if (i != null) {
+            if (i >= this.pointList.length || i < 0) {
+                point = null;
+            } else {
+                point = new SPoint(this.pointList[i].x, this.pointList[i].y);
+                this.pointList.splice(i, 1);
+            }
+        } else {
+            if (this.pointList.length) {
+                point = this.pointList[this.pointList.length - 1];
+                this.pointList.pop();
+            } else {
+                point = null;
+            }
+        }
+        this.update();
+        return point;
+    }
+
+    /**
+     * 多边形顶点的移动位置
+     *
+     * @param x   点位得x坐标
+     * @param y   点位得y坐标
+     * @param i   点位得i坐标
+     * @return    移动对应得点。如果索引无法找到移动顶点,则返回null
+     */
+    movePoint(x: number, y: number, i: number): SPoint | null {
+        let point = null;
+        if (i >= this.pointList.length || i < 0) {
+            return null;
+        }
+        if (this.pointList.length) {
+            this.pointList[i].x = x;
+            this.pointList[i].y = y;
+        }
+        point = this.pointList[i];
+        return point;
+    }
+
+    /**
+     * 打印出多边形数组
+     *
+     * @return  顶点数组
+     */
+    PrintPointList(): SPoint[] {
+        return this.pointList;
+    }
+
+    ////////////
+    //  以下为三种状态下的绘制多边形方法
+
+    /**
+     * 展示状态 --绘制多边形数组
+     *
+     * @param painter      绘制类
+     * @param pointList    绘制多边形数组
+     */
+    protected drawShowPolygon(painter: SPainter, pointList: SPoint[]): void {
+        painter.pen.lineCapStyle = SLineCapStyle.Square;
+        painter.pen.color = this.borderColor;
+        painter.pen.lineWidth = painter.toPx(this.borderLine);
+        painter.brush.color = this.brushColor;
+        painter.drawPolygon([...pointList]);
+    }
+
+    /**
+     * 创建状态 --绘制多边形数组
+     *
+     * @param painter      绘制类
+     * @param pointList    绘制多边形数组
+     */
+    protected drawCreatePolygon(painter: SPainter, pointList: SPoint[]): void {
+        painter.pen.lineCapStyle = SLineCapStyle.Square;
+        painter.pen.color = this.borderColor;
+        painter.pen.lineWidth = painter.toPx(this.borderLine);
+        if (this.lastPoint && pointList.length) {
+            painter.drawLine(
+                pointList[pointList.length - 1].x,
+                pointList[pointList.length - 1].y,
+                this.lastPoint.x,
+                this.lastPoint.y
+            );
+        }
+        painter.drawPolyline(pointList);
+        painter.pen.color = SColor.Transparent;
+        painter.brush.color = this.brushColor;
+        painter.pen.lineWidth = painter.toPx(this.borderLine);
+        if (this.lastPoint) {
+            painter.drawPolygon([...pointList, this.lastPoint]);
+        } else {
+            painter.drawPolygon(pointList);
+        }
+    }
+
+    /**
+     *
+     * 编辑状态 --绘制多边形数组
+     *
+     * @param painter    绘制类
+     * @param pointList    绘制多边形数组
+     */
+    protected drawEditPolygon(painter: SPainter, pointList: SPoint[]): void {
+        // 展示多边形
+        this.drawShowPolygon(painter, pointList);
+        // 绘制顶点块
+        painter.pen.color = SColor.Black;
+        painter.brush.color = SColor.White;
+        pointList.forEach((item, index) => {
+            painter.drawCircle(item.x, item.y, painter.toPx(this.len / 2));
+        });
+    }
+
+    /**
+     * 编辑状态操作多边形数组
+     *
+     * @param event    鼠标事件
+     *
+     *
+     */
+    protected editPolygonPoint(event: SMouseEvent): void {
+        //  判断是否为删除状态 isAlt = true为删除状态
+        if (this.isAlt) {
+            // 1 判断是否点击在多边形顶点
+            let lenIndex = -1; // 当前点击到的点位索引;
+            let curenLen = this.scenceLen; // 当前的灵敏度
+            this.pointList.forEach((item, index) => {
+                let dis = SMathUtil.pointDistance(
+                    event.x,
+                    event.y,
+                    item.x,
+                    item.y
+                );
+                if (dis < curenLen) {
+                    curenLen = dis;
+                    lenIndex = index;
+                }
+            });
+            // 若点击到,对该索引对应的点做删除
+            if (lenIndex != -1) {
+                if (this.pointList.length <= 3) {
+                    return;
+                }
+                const delePoint = new SPoint(
+                    this.pointList[lenIndex].x,
+                    this.pointList[lenIndex].y
+                );
+                this.deletePoint(lenIndex);
+                // 记录顶点操作记录压入堆栈
+                this.recordAction(SGraphPointListDelete, [
+                    this.pointList,
+                    delePoint,
+                    lenIndex
+                ]);
+            }
+        } else {
+            // 1 判断是否点击在多边形顶点
+            this.curIndex = -1;
+            this.curPoint = null;
+            let lenIndex = -1; // 当前点击到的点位索引;
+            let curenLen = this.scenceLen; // 当前的灵敏度
+            this.pointList.forEach((item, index) => {
+                let dis = SMathUtil.pointDistance(
+                    event.x,
+                    event.y,
+                    item.x,
+                    item.y
+                );
+                if (dis < curenLen) {
+                    curenLen = dis;
+                    lenIndex = index;
+                }
+            });
+            this.curIndex = lenIndex;
+            // 2判断是否点击在多边形得边上
+            if (-1 == lenIndex) {
+                let len = SMathUtil.pointToLine(
+                        new SPoint(event.x, event.y),
+                        new SLine(this.pointList[0], this.pointList[1])
+                    ),
+                    index = 0;
+                if (this.pointList.length > 2) {
+                    for (let i = 1; i < this.pointList.length; i++) {
+                        let dis = SMathUtil.pointToLine(
+                            new SPoint(event.x, event.y),
+                            new SLine(this.pointList[i], this.pointList[i + 1])
+                        );
+                        if (i + 1 == this.pointList.length) {
+                            dis = SMathUtil.pointToLine(
+                                new SPoint(event.x, event.y),
+                                new SLine(this.pointList[i], this.pointList[0])
+                            );
+                        }
+                        if (dis.MinDis < len.MinDis) {
+                            len = dis;
+                            index = i;
+                        }
+                    }
+                }
+                // 如果再线上则为新增点
+                if (len.Point) {
+                    if (len.MinDis <= this.scenceLen) {
+                        this.pointList.splice(index + 1, 0, len.Point);
+                        // 记录新增顶点操作记录压入堆栈
+                        this.recordAction(SGraphPointListInsert, [
+                            this.pointList,
+                            len.Point,
+                            index + 1
+                        ]);
+                    }
+                }
+            } else {
+                // 当捕捉到顶点后 ,记录当前点的xy坐标,用于undo、redo操作
+                this.curPoint = new SPoint(
+                    this.pointList[this.curIndex].x,
+                    this.pointList[this.curIndex].y
+                );
+            }
+            // 刷新视图
+            this.update();
+        }
+    }
+
+    /////////////////////
+    // undo、redo相关操作
+
+    /**
+     * 记录相关动作并推入栈中
+     * @param	SGraphCommand         相关命令类
+     * @param	any                    对应传入参数
+     */
+    protected recordAction(SGraphCommand: any, any: any[]): void {
+        // 记录相关命令并推入堆栈中
+        const sgraphcommand = new SGraphCommand(this, ...any);
+        if (this.undoStack) {
+            this.undoStack.push(sgraphcommand);
+        }
+    }
+
+    /**
+     * 执行取消操作执行
+     */
+    undo(): void {
+        if (this._status == SItemStatus.Normal) {
+            return;
+        }
+        if (this.undoStack) {
+            this.undoStack.undo();
+        }
+    }
+
+    /**
+     * 执行重做操作执行
+     */
+    redo(): void {
+        if (this._status == SItemStatus.Normal) {
+            return;
+        }
+        if (this.undoStack) {
+            this.undoStack.redo();
+        }
+    }
+
+    ///////////////////////////////
+    // 以下为鼠标事件
+
+    /**
+     * 鼠标双击事件
+     *
+     * @param	event         事件参数
+     * @return	boolean
+     */
+    onDoubleClick(event: SMouseEvent): boolean {
+        // 如果位show状态 双击改对象则需改为编辑状态
+        if (SItemStatus.Normal == this._status) {
+            this._status = SItemStatus.Edit;
+            this.grabItem(this);
+        } else if (SItemStatus.Edit == this._status) {
+            this._status = SItemStatus.Normal;
+            this.releaseItem();
+            if (this.undoStack) {
+                this.undoStack.clear();
+            }
+        }
+        this.update();
+        return true;
+    } // Function onDoubleClick()
+
+    /**
+     * 键盘事件
+     *
+     * @param	event         事件参数
+     * @return	boolean
+     */
+
+    onKeyDown(event: KeyboardEvent): boolean {
+        if (this._status == SItemStatus.Normal) {
+            return false;
+        } else if (this._status == SItemStatus.Create) {
+            if (event.code == "Enter") {
+                // 当顶点大于二个时才又条件执行闭合操作并清空堆栈
+                if (this.pointList.length > 2) {
+                    this._status = SItemStatus.Normal;
+                    //3 传递完成事件状态
+                    this.$emit("finishCreated");
+                    //1 grabItem 置为null
+                    this.releaseItem();
+                    //2 清空undo
+                    if (this.undoStack) {
+                        this.undoStack.clear();
+                    }
+                }
+            }
+        } else if (this._status == SItemStatus.Edit) {
+            if (event.key == "Alt") {
+                this.isAlt = true;
+            }
+        }
+        this.update();
+        return true;
+    } // Function onKeyDown()
+
+    /**
+     * 键盘键抬起事件
+     *
+     * @param	event         事件参数
+     * @return	boolean
+     */
+    onKeyUp(event: KeyboardEvent): void {
+        if (this._status == SItemStatus.Edit) {
+            if (event.key == "Alt") {
+                this.isAlt = false;
+            }
+        }
+        this.update();
+    } // Function onKeyUp()
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param	event         事件参数
+     * @return	boolean
+     */
+    onMouseDown(event: SMouseEvent): boolean {
+        // 如果状态为编辑状态则添加点
+        if (this._status == SItemStatus.Create) {
+            // 新增顶点
+            this.insertPoint(event.x, event.y);
+            // 记录新增顶点操作记录压入堆栈
+            let pos = new SPoint(event.x, event.y);
+            this.recordAction(SGraphPointListInsert, [this.pointList, pos]);
+        } else if (this._status == SItemStatus.Edit) {
+            // 对多边形数组做编辑操作
+            this.editPolygonPoint(event);
+        } else {
+            // return super.onMouseDown(event)
+        }
+        return true;
+    } // Function onMouseDown()
+
+    /**
+     * 鼠标移入事件
+     *
+     * @param	event         事件参数
+     * @return	boolean
+     */
+    onMouseEnter(event: SMouseEvent): boolean {
+        return true;
+    } // Function onMouseEnter()
+
+    /**
+     * 鼠标移出事件
+     *
+     * @param	event         事件参数
+     * @return	boolean
+     */
+
+    onMouseLeave(event: SMouseEvent): boolean {
+        return true;
+    } // Function onMouseLeave()
+
+    /**
+     * 鼠标移动事件
+     *
+     * @param	event         事件参数
+     * @return	boolean
+     */
+
+    onMouseMove(event: SMouseEvent): boolean {
+        if (this._status == SItemStatus.Create) {
+            this.lastPoint = new SPoint();
+            this.lastPoint.x = event.x;
+            this.lastPoint.y = event.y;
+            this.update();
+        } else if (this._status == SItemStatus.Edit) {
+            if (-1 != this.curIndex) {
+                this.pointList[this.curIndex].x = event.x;
+                this.pointList[this.curIndex].y = event.y;
+            }
+            this.update();
+        } else {
+            // return super.onMouseMove(event)
+        }
+
+        return true;
+    } // Function onMouseMove()
+
+    /**
+     * 鼠标抬起事件
+     *
+     * @param	event         事件参数
+     * @return	boolean
+     */
+    onMouseUp(event: SMouseEvent): boolean {
+        if (this._status == SItemStatus.Edit) {
+            if (-1 != this.curIndex) {
+                const pos = new SPoint(
+                    this.pointList[this.curIndex].x,
+                    this.pointList[this.curIndex].y
+                );
+                this.recordAction(SGraphPointListUpdate, [
+                    this.pointList,
+                    this.curPoint,
+                    pos,
+                    this.curIndex
+                ]);
+            }
+            this.curIndex = -1;
+            this.curPoint = null;
+        } else {
+            // return super.onMouseUp(event)
+        }
+        return true;
+    } // Function onMouseUp()
+
+    /**
+     * 适配事件
+     *
+     * @param	event         事件参数
+     * @return	boolean
+     */
+
+    onResize(event: SMouseEvent): boolean {
+        return true;
+    } // Function onResize()
+
+    /**
+     * 取消操作
+     *
+     */
+    cancelOperate(): void {
+        // 当状态为展示状态
+        if (this._status == SItemStatus.Create) {
+            // 闭合多边形
+            this.parent = null;
+        } else if (this._status == SItemStatus.Edit) {
+            // 编辑状态
+            this._status = SItemStatus.Normal;
+        }
+        this.update();
+    } // Function cancelOperate()
+
+    /**
+     * Item对象边界区域
+     *
+     * @return SRect
+     */
+    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
+        );
+    } // Function boundingRect()
+
+    /**
+     * 判断点是否在区域内
+     *
+     * @param x
+     * @param y
+     */
+    contains(x: number, y: number): boolean {
+        let arr = this.pointList;
+        return !(arr.length < 3 || !SPolygonUtil.pointIn(x, y, arr));
+    } // Function contains()
+
+    /**
+     * Item绘制操作
+     *
+     * @param   painter       painter对象
+     */
+    onDraw(painter: SPainter): void {
+        this.scenceLen = painter.toPx(this.len);
+        // 当状态为展示状态
+        if (this._status == SItemStatus.Normal) {
+            // 闭合多边形
+            this.drawShowPolygon(painter, this.pointList);
+        } else if (this._status == SItemStatus.Create) {
+            // 创建状态
+            this.drawCreatePolygon(painter, this.pointList);
+        } else {
+            // 编辑状态
+            this.drawEditPolygon(painter, this.pointList);
+        }
+    } // Function onDraw()
+}