haojianlong 4 лет назад
Родитель
Сommit
c1945858e4

+ 39 - 20
src/utils/graph/DivideFloorScene.ts

@@ -6,12 +6,17 @@ import { HighlightItem } from "./HighlightItem";
 import { ShadeItem } from "./ShadeItem";
 // @ts-ignore
 import { intersect, polygon, segments, combine, selectIntersect, selectUnion, selectDifference, selectDifferenceRev, difference } from "polybooljs";
+import { DrawZoneItem } from "./DrawZoneItem";
+import { SItemStatus } from "@persagy-web/big/lib";
 
 export class DivideFloorScene extends FloorScene {
     /** 划分item  */
     cutItem: ShadeItem | null = null;
-    /** 是否开启切分  */
-    isCutting: boolean = false;
+
+    /** 绘制的分区item */
+    drawItem: DrawZoneItem | null = null;
+    /** 当前绘制命令 cut-交集区域绘制 zoneDraw-绘制业务空间 */
+    drawCmd: string = '';
 
     /** 是否开启吸附  */
     private _isAbsorbing: boolean = false;
@@ -29,13 +34,19 @@ export class DivideFloorScene extends FloorScene {
      * 清除划分区域
      */
     clearCut(): void {
-        if (this.cutItem) {
+        if (this.cutItem && this.drawCmd == 'cut') {
             this.grabItem = null;
             this.removeItem(this.cutItem);
             this.cutItem = null;
-            this.isCutting = false;
             this.view && this.view.update();
         }
+        if (this.drawItem && this.drawCmd == 'zoneDraw') {
+            this.grabItem = null;
+            this.removeItem(this.drawItem);
+            this.drawItem = null;
+            this.view && this.view.update();
+        }
+        this.drawCmd = '';
     } // Function clearCut()
 
     /**
@@ -46,24 +57,32 @@ export class DivideFloorScene extends FloorScene {
     */
     onMouseDown(event: SMouseEvent): boolean {
         if (event.buttons == 1) {
-            if (this.isCutting) {
-                // 判断是否开启吸附,并且有吸附的点
-                if (
-                    this.isAbsorbing &&
-                    this.highLight &&
-                    this.highLight.visible
-                ) {
-                    event.x = this.highLight.point.x;
-                    event.y = this.highLight.point.y;
+            // 判断是否开启吸附,并且有吸附的点
+            if (
+                this.isAbsorbing &&
+                this.highLight &&
+                this.highLight.visible
+            ) {
+                event.x = this.highLight.point.x;
+                event.y = this.highLight.point.y;
+            }
+            if (this.drawCmd == 'cut') {
+                if (!this.cutItem) {
+                    let point = new SPoint(event.x, event.y);
+                    let item = new ShadeItem(null, point);
+                    this.addItem(item);
+                    this.cutItem = item;
+                    this.grabItem = item;
+                    return true;
                 }
-                if (this.cutItem) {
-                    return this.cutItem.onMouseDown(event);
-                } else {
+            } else if (this.drawCmd == 'zoneDraw') {
+                if (!this.drawItem) {
                     let point = new SPoint(event.x, event.y);
-                    let cut = new ShadeItem(null, point);
-                    this.addItem(cut);
-                    this.cutItem = cut;
-                    this.grabItem = cut;
+                    let item = new DrawZoneItem(null, point);
+                    item.status = SItemStatus.Create;
+                    this.addItem(item);
+                    this.drawItem = item;
+                    this.grabItem = item;
                     return true;
                 }
             }

+ 680 - 0
src/utils/graph/DrawZoneItem.ts

@@ -0,0 +1,680 @@
+import { SKeyCode, SMouseEvent, SUndoStack } from "@persagy-web/base/lib";
+import { ItemOrder, SItemStatus } from "@persagy-web/big/lib";
+import { SMathUtil } from "@persagy-web/big/lib/utils/SMathUtil";
+import { SColor, SLine, SLineCapStyle, SPainter, SPoint, SPolygonUtil, SRect, SSize } from "@persagy-web/draw/lib";
+import { SGraphItem, SGraphPointListDelete, SGraphPointListInsert, SGraphPointListUpdate, SGraphStyleItem, SLineStyle } from "@persagy-web/graph/lib";
+
+export class DrawZoneItem 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;
+    /** 轮廓线坐标 */
+    private pointList: SPoint[] = [];
+
+    /** 当前状态 */
+    protected _status: number = SItemStatus.Normal;
+    get status(): SItemStatus {
+        return this._status;
+    }
+    set status(value: SItemStatus) {
+        this._status = value;
+        this.undoStack.clear();
+        this.update();
+    }
+
+    /** 是否闭合 */
+    closeFlag: boolean = false;
+    /** 鼠标移动点 */
+    private lastPoint: SPoint | null = null;
+    /** 当前鼠标获取顶点对应索引 */
+    private curIndex: number = -1;
+    /** 当前鼠标获取顶点对应坐标 */
+    private curPoint: null | SPoint = null;
+    /** 灵敏像素 */
+    private len: number = 10;
+    /** 场景像素内部将灵敏像素换算为场景实际距离 */
+    private scenceLen: number = 15;
+    /** 场景像素 */
+    private isShift: boolean = false;
+    /** undo/redo 堆栈 */
+    protected undoStack: SUndoStack = new SUndoStack();
+
+    /**
+     * 构造函数
+     *
+     * @param parent    指向父对象
+     * @param data      图例节点对象数据
+     */
+    constructor(parent: SGraphItem | null, data: any) {
+        super(parent);
+        this.zOrder = ItemOrder.polygonOrder;
+        this.data = data;
+        if (data instanceof Array) {
+            this.pointList = data;
+        } else {
+            this.pointList.push(data);
+            this.lastPoint = data;
+        }
+        this.showSelect = false;
+        this.fillColor = new SColor('#2cd1f780')
+    }
+
+    //////////////////
+    //  以下为对pointList 数组的操作方法
+
+    /**
+     * 储存新的多边形顶点
+     *
+     * @param x   点位得 x 坐标
+     * @param y   点位得 y 坐标
+     * @param i   储存所在索引
+     * @return 添加的顶点
+     */
+    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   索引不在数组范围则返回 null
+     */
+    deletePoint(i: number | null = null): SPoint | null {
+        let point;
+        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.curIndex = -1;
+        this.curPoint = 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
+        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;
+    }
+
+    /**
+     * 展示状态 -- 绘制多边形数组
+     *
+     * @param painter      绘制类
+     * @param pointList    绘制多边形数组
+     */
+    protected drawShowPolygon(painter: SPainter, pointList: SPoint[]): void {
+        painter.save();
+        const eachPx = painter.toPx(1)
+        painter.pen.lineCapStyle = SLineCapStyle.Square;
+        painter.pen.color = this.strokeColor;
+        painter.brush.color = this.fillColor;
+        painter.pen.lineWidth = eachPx * this.lineWidth;
+        if (this.lineStyle == SLineStyle.Dashed) {
+            painter.pen.lineDash = [3 * eachPx, 7 * eachPx];
+        } else if (this.lineStyle == SLineStyle.Dotted) {
+            painter.pen.lineDash = [eachPx * this.lineWidth, eachPx * this.lineWidth];
+        }
+        painter.drawPolygon([...pointList]);
+        painter.restore();
+    }
+
+    /**
+     * 创建状态 -- 绘制多边形数组
+     *
+     * @param painter      绘制类
+     * @param pointList    绘制多边形数组
+     */
+    protected drawCreatePolygon(painter: SPainter, pointList: SPoint[]): void {
+        painter.pen.lineCapStyle = SLineCapStyle.Square;
+        painter.pen.color = this.strokeColor;
+        painter.pen.lineWidth = painter.toPx(this.lineWidth);
+        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 = new SColor(this.fillColor.value);
+        painter.pen.lineWidth = painter.toPx(this.lineWidth);
+        if (this.lastPoint) {
+            painter.drawPolygon([...pointList, this.lastPoint]);
+            // 绘制顶点块
+            painter.pen.color = SColor.Black;
+            painter.brush.color = SColor.White;
+            pointList.forEach(item => {
+                painter.drawCircle(item.x, item.y, painter.toPx(this.len / 2));
+            });
+            // 如果最后一个点在第一个点的灵敏度范围内,第一个点填充变红
+            if (this.pointList.length) {
+                if (
+                    SMathUtil.pointDistance(
+                        this.lastPoint.x,
+                        this.lastPoint.y,
+                        this.pointList[0].x,
+                        this.pointList[0].y
+                    ) < this.scenceLen
+                ) {
+                    // 绘制第一个点的顶点块
+                    painter.pen.color = SColor.Black;
+                    painter.brush.color = SColor.Red;
+                    painter.drawCircle(
+                        this.pointList[0].x,
+                        this.pointList[0].y,
+                        painter.toPx(this.len / 2)
+                    );
+                }
+            }
+        } else {
+            painter.drawPolygon(pointList);
+        }
+    }
+
+    /**
+     *
+     * 编辑状态 -- 绘制多边形数组
+     *
+     * @param painter      绘制类
+     * @param pointList    绘制多边形数组
+     */
+    protected drawEditPolygon(painter: SPainter, pointList: SPoint[]): void {
+        // 展示多边形
+        painter.pen.lineCapStyle = SLineCapStyle.Square;
+        painter.pen.color = this.strokeColor;
+        painter.pen.lineWidth = painter.toPx(this.lineWidth);
+        painter.brush.color = new SColor(this.fillColor.value);
+        painter.drawPolygon([...pointList]);
+        // 绘制顶点块
+        painter.pen.color = SColor.Black;
+        painter.brush.color = SColor.White;
+        pointList.forEach((item, index) => {
+            painter.brush.color = SColor.White;
+            if (index == this.curIndex) {
+                painter.brush.color = new SColor("#2196f3");
+            }
+
+            painter.drawCircle(item.x, item.y, painter.toPx(this.len / 2));
+        });
+    }
+
+    /**
+     * 编辑状态操作多边形数组
+     *
+     * @param event    鼠标事件
+     */
+    protected editPolygonPoint(event: SMouseEvent): void {
+        //  判断是否为删除状态 isShift = true为删除状态
+        if (this.isShift) {
+            // 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 {
+                        //没点在多边形边上也没点在多边形顶点上
+                        super.onMouseDown(event);
+                    }
+                }
+            } else {
+                // 当捕捉到顶点后 ,记录当前点的xy坐标,用于undo、redo操作
+                this.curPoint = new SPoint(
+                    this.pointList[this.curIndex].x,
+                    this.pointList[this.curIndex].y
+                );
+            }
+
+            // 刷新视图
+            this.update();
+        }
+    }
+
+    /**
+     * 记录相关动作并推入栈中
+     *
+     * @param SGraphCommand      相关命令类
+     * @param any                对应传入参数
+     */
+    protected recordAction(SGraphCommand: any, any: any[]): void {
+        // 记录相关命令并推入堆栈中
+        const sgraphcommand = new SGraphCommand(this.scene, this, ...any);
+        this.undoStack.push(sgraphcommand);
+    }
+
+    /**
+     * 执行取消操作执行
+     */
+    undo(): void {
+        if (this.status == SItemStatus.Normal) {
+            return;
+        }
+        this.undoStack.undo();
+    }
+
+    /**
+     * 执行重做操作执行
+     */
+    redo(): void {
+        if (this.status == SItemStatus.Normal) {
+            return;
+        }
+
+        this.undoStack.redo();
+    }
+
+    /**
+     * 键盘事件
+     *
+     * @param event    事件参数
+     * @return  是否处理该事件
+     */
+    onKeyUp(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();
+                }
+            }
+        } else if (this.status == SItemStatus.Edit) {
+            if (event.keyCode == SKeyCode.Delete) {
+                // 当多边形的顶点大于三个允许删除点
+                if (this.pointList.length > 3) {
+                    this.deletePoint(this.curIndex);
+                }
+            }
+        }
+        this.update();
+        return true;
+    }
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param	event      事件参数
+     * @return	 是否处理该事件
+     */
+    onMouseDown(event: SMouseEvent): boolean {
+        if (event.shiftKey) {
+            event = this.compare(event);
+        }
+        // 如果状态为编辑状态则添加点
+        if (this.status == SItemStatus.Create) {
+            // 新增顶点
+            let len = -1;
+            if (this.pointList.length) {
+                len = SMathUtil.pointDistance(
+                    event.x,
+                    event.y,
+                    this.pointList[0].x,
+                    this.pointList[0].y
+                );
+            }
+            if (this.pointList.length > 2 && len > 0 && len < this.scenceLen) {
+                this.$emit("finishCreated");
+                this.status = SItemStatus.Normal;
+                this.releaseItem();
+            } else {
+                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 {
+            super.onMouseDown(event);
+        }
+        return true;
+    }
+
+    /**
+     * 鼠标移入事件
+     *
+     * @param event   事件参数
+     * @return 是否处理该事件
+     */
+    onMouseEnter(event: SMouseEvent): boolean {
+        return true;
+    }
+
+    /**
+     * 鼠标移出事件
+     *
+     * @param event    事件参数
+     * @return 是否处理该事件
+     */
+    onMouseLeave(event: SMouseEvent): boolean {
+        return true;
+    }
+
+    /**
+     * 鼠标移动事件
+     *
+     * @param event    事件参数
+     * @return 是否处理该事件
+     */
+    onMouseMove(event: SMouseEvent): boolean {
+        if (event.shiftKey) {
+            event = this.compare(event);
+        }
+        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 (event.buttons == 1) {
+                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;
+    }
+
+    /**
+     * shift 垂直水平创建或编辑
+     *
+     * @param event   事件
+     */
+    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 > 0) {
+                    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 event    事件参数
+     * @return 是否处理该事件
+     */
+    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
+                ]);
+            }
+        } else if (this.status == SItemStatus.Normal) {
+            return super.onMouseUp(event);
+        } else if (this.status == SItemStatus.Create) {
+
+        }
+        return true;
+    }
+
+    /**
+     * 取消操作
+     */
+    cancelOperate(): void {
+        // 当状态为展示状态
+        if (this.status == SItemStatus.Create) {
+            // 闭合多边形
+            this.parent = null;
+        } else if (this.status == SItemStatus.Edit) {
+            // 编辑状态
+            this.status = SItemStatus.Normal;
+        }
+        this.update();
+    }
+
+    /**
+     * 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 arr = this.pointList;
+        if (arr.length < 3 || !SPolygonUtil.pointIn(x, y, arr)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 选择器放缩控制大小变化'
+     * 
+     * @param old    旧的大小
+     * @param newS   新的大小
+    */
+    resize(old: SSize, newS: SSize): void {
+        const xs = newS.width / old.width;
+        const ys = newS.height / old.height;
+        this.pointList = this.pointList.map(item => {
+            item.x = item.x * xs;
+            item.y = item.y * ys;
+            return item
+        });
+    }
+
+    /**
+     * Item 绘制操作
+     *
+     * @param 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);
+        }
+    }
+}

+ 3 - 1
src/views/maintain/space/components/spaceGraph.vue

@@ -525,7 +525,9 @@ export default class spaceGraph extends Vue {
     }
     // 切割划分
     divide() {
-        this.scene.isCutting = true;
+        if (this.scene) {
+            this.scene.drawCmd = 'cut';
+        }
     }
     // 清除切割划分
     clearDivide() {