Explorar o código

交集算法添加

haojianlong %!s(int64=4) %!d(string=hai) anos
pai
achega
6125af8a22

+ 1 - 1
package.json

@@ -15,7 +15,7 @@
   "dependencies": {
     "@babel/runtime": "^7.12.5",
     "@persagy-web/base": "2.2.1",
-    "@persagy-web/big": "2.2.49",
+    "@persagy-web/big": "2.2.50",
     "@persagy-web/draw": "2.2.13",
     "@persagy-web/graph": "2.2.45",
     "axios": "^0.21.1",

+ 209 - 7
src/utils/graph/DivideFloorScene.ts

@@ -4,6 +4,8 @@ import { SPoint, SRect } from "@persagy-web/draw/lib";
 import { FloorScene } from "./FloorScene";
 import { HighlightItem } from "./HighlightItem";
 import { ShadeItem } from "./ShadeItem";
+// @ts-ignore
+import { intersect, polygon, segments, combine, selectIntersect, selectUnion, selectDifference, selectDifferenceRev, difference } from "polybooljs";
 
 export class DivideFloorScene extends FloorScene {
     /** 划分item  */
@@ -12,7 +14,7 @@ export class DivideFloorScene extends FloorScene {
     isCutting: boolean = false;
 
     /** 是否开启吸附  */
-    private _isAbsorbing: boolean = true;
+    private _isAbsorbing: boolean = false;
     get isAbsorbing(): boolean {
         return this._isAbsorbing;
     } // Get isAbsorbing
@@ -190,12 +192,12 @@ export class DivideFloorScene extends FloorScene {
     } // Function isPointInAbsorbArea()
 
     /**
-         *  吸附空间线
-         *
-         *  @param  event       鼠标事件对象
-         *  @param  absorbLen   吸附距离
-         *  @return PointToLine 吸附的线
-         */
+     *  吸附空间线
+     *
+     *  @param  event       鼠标事件对象
+     *  @param  absorbLen   吸附距离
+     *  @return PointToLine 吸附的线
+     */
     absorbSpaceLine(event: SMouseEvent, absorbLen: number): any {
         let minPointDis = Number.MAX_SAFE_INTEGER;
         let Point, Line;
@@ -232,4 +234,204 @@ export class DivideFloorScene extends FloorScene {
             Line: Line
         };
     } // Function absorbSpaceLine()
+
+    /**
+     * 计算选中的空间与业务空间的差集
+    */
+    getSpaceZoneIntersect(): SPoint[][] | undefined {
+        // 未选中空间- 不计算
+        if (!this.selectContainer.itemList.length) {
+            return
+        }
+        // 没有业务空间不计算
+        if (!this.zoneList.length) {
+            return
+        }
+        // 生成选中的空间计算时用的格式
+        const space = {
+            regions: [],
+            inverted: false
+        }
+        const sourceIdToDiff = {};
+        try {
+            let poly1, poly2;
+            const start = +new Date();
+            let rect1: SRect, list = [];
+            // 计算外接矩阵与选中空间相交的业务空间的所有轮廓
+            this.selectContainer.itemList.forEach(item => {
+                rect1 = item.boundingRect();
+                this.zoneList.forEach(t => {
+                    if (t.visible) {
+                        let rect2 = t.boundingRect();
+                        // 外接矩阵是否相交,缩小计算范围
+                        if (SMathUtil.rectIntersection(rect1, rect2)) {
+                            t.pointList.forEach((zoneLine): void => {
+                                let polygons = {
+                                    regions: [],
+                                    inverted: false
+                                };
+                                zoneLine.forEach((po): void => {
+                                    let point = SMathUtil.transferToArray(po);
+                                    polygons.regions.push(point);
+                                });
+                                list.push(polygons);
+                            })
+                        }
+                    }
+                })
+            })
+            // 
+            if (list.length) {
+                let seg1 = segments(list[0]),
+                    seg2,
+                    comb;
+                for (let i = 1; i < list.length; i++) {
+                    seg2 = segments(list[i]);
+                    comb = combine(seg1, seg2);
+                    seg1 = selectUnion(comb);
+                }
+                poly1 = seg1;
+                this.selectContainer.itemList.forEach(t => {
+                    let key = t.data.SourceId;
+                    poly2 = {
+                        regions: [],
+                        inverted: false
+                    }
+                    poly2.regions = t.pointList[0].map((item): number[][] => {
+                        return SMathUtil.transferToArray(item);
+                    });
+                    const comb = combine(segments(poly2), poly1)
+                    const diffObj = selectDifference(comb);
+                    const diffPoly = polygon(diffObj)
+                    // 理论上只要选择了空间 length就不会为0
+                    if (diffPoly.regions.length) {
+                        let outlineList = SMathUtil.getIntersectInArray(
+                            diffPoly.regions
+                        );
+                        console.log(outlineList);
+                        // @ts-ignore
+                        sourceIdToDiff[key] = outlineList.map(t => {
+                            let arr = [t.Outer];
+                            t.Inner.forEach((inner): void => {
+                                arr.push(inner);
+                            });
+                            return arr;
+                        });
+                    }
+                });
+                const end = +new Date()
+                console.log(sourceIdToDiff);
+                console.log(end - start, 'comb-diff');
+                return sourceIdToDiff
+            }
+        } catch (err) {
+            console.log(err);
+        }
+    }
+
+    /**
+     * 计算选中的空间与绘制的区域交集
+    */
+    getSpaceCutIntersect(seg?: any): SPoint[][] | undefined {
+        // 未选中空间- 不计算
+        if (!this.selectContainer.itemList.length) {
+            return
+        }
+        // 没有绘制不计算
+        if (!this.cutItem) {
+            return
+        }
+        const sourceIdToIntersect = {};
+        try {
+            const start = +new Date();
+            let cutPoly;
+            if (seg) {
+                cutPoly = seg
+            } else {
+                const poly = {
+                    regions: [SMathUtil.transferToArray(this.cutItem.pointList)],
+                    inverted: false
+                }
+                cutPoly = segments(poly)
+            }
+            this.selectContainer.itemList.forEach(item => {
+                const arr = item.pointList[0].map(t => {
+                    return SMathUtil.transferToArray(t);
+                })
+                const poly2 = {
+                    regions: arr,
+                    inverted: false
+                }
+                const seg = segments(poly2)
+                const comb = combine(cutPoly, seg)
+                if (item.data.SourceId) {
+                    const spoly = polygon(selectIntersect(comb));
+                    // 绘制切割区域可能与空间没有交集
+                    if (spoly.regions.length) {
+                        let outlineList = SMathUtil.getIntersectInArray(
+                            spoly.regions
+                        );
+                        console.log(outlineList);
+                        // @ts-ignore
+                        sourceIdToIntersect[item.data.SourceId] = outlineList.map(t => {
+                            let arr = [t.Outer];
+                            t.Inner.forEach((inner): void => {
+                                arr.push(inner);
+                            });
+                            return arr;
+                        });
+                        // 得到的结果
+                        // sourceIdToIntersect[item.data.SourceId] = polygon(selectIntersect(comb))
+                    } else {
+                        // 没有交集
+                    }
+                }
+            })
+            const end = +new Date()
+            console.log(end - start, 'comb-intersect', sourceIdToIntersect);
+            return sourceIdToIntersect
+        } catch (err) {
+            console.log(err);
+        }
+        return
+    }
+
+    /**
+     * 如果场景中既有绘制的区域也有创建好的业务空间,则在区域中将已创建的业务空间减去
+    */
+    getCurInZone() {
+        if (!this.cutItem) {
+            return
+        }
+        // 绘制区域外接矩阵
+        const rect2 = this.cutItem.boundingRect()
+        // 绘制区域的多边形 - 也是每次减去业务空间后剩下的多边形
+        let poly = segments({
+            regions: [SMathUtil.transferToArray(this.cutItem.pointList)],
+            inverted: false
+        })
+        this.zoneList.forEach(item => {
+            if (item.visible) {
+                const rect1 = item.boundingRect();
+                if (SMathUtil.rectIntersection(rect1, rect2)) {
+                    item.pointList.forEach((zoneLine): void => {
+                        let polygons = {
+                            regions: [],
+                            inverted: false
+                        }
+                        zoneLine.forEach((po): void => {
+                            let point = SMathUtil.transferToArray(po);
+                            polygons.regions.push(point);
+                        });
+                        const segZone = segments(polygons);
+                        const comb = combine(poly, segZone);
+                        poly = selectDifference(comb)
+                    })
+                }
+            }
+        })
+        console.log(poly);
+        // poly为segments类型
+        return poly;
+    }
 }

+ 5 - 1
src/utils/graph/FloorScene.ts

@@ -260,7 +260,11 @@ export class FloorScene extends SGraphScene {
     initZoneColor(): void {
         this.zoneList.forEach(
             (t: SZoneItem, i: number) => {
-                t.fillColor = new SColor(zoneColor[i % zoneColor.length]);
+                if (t.selectable) {
+                    t.fillColor = new SColor(zoneColor[i % zoneColor.length]);
+                } else {
+                    t.fillColor = ItemColor.zoneUnselectColor;
+                }
             }
         );
     }

+ 74 - 18
src/utils/graph/ShadeItem.ts

@@ -1,7 +1,7 @@
 import { SMouseEvent } from "@persagy-web/base/lib";
 import { ItemOrder } from "@persagy-web/big/lib";
 import { SMathUtil } from "@persagy-web/big/lib/utils/SMathUtil";
-import { SColor, SLineCapStyle, SPainter, SPoint, SPolygonUtil } from "@persagy-web/draw/lib";
+import { SColor, SLineCapStyle, SPainter, SPoint, SPolygonUtil, SRect } from "@persagy-web/draw/lib";
 import { SGraphItem, SGraphStyleItem } from "@persagy-web/graph/lib";
 
 /**
@@ -10,8 +10,16 @@ import { SGraphItem, SGraphStyleItem } from "@persagy-web/graph/lib";
  * @author  郝建龙
  */
 export class ShadeItem 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;
     /** 轮廓线坐标  */
-    outLine: SPoint[] = [];
+    pointList: SPoint[] = [];
     /** 是否闭合    */
     closeFlag = false;
     /** 鼠标移动点  */
@@ -42,10 +50,10 @@ export class ShadeItem extends SGraphStyleItem {
     constructor(parent: SGraphItem | null, data: SPoint[] | SPoint) {
         super(parent);
         if (data instanceof Array) {
-            this.outLine = data;
+            this.pointList = data;
             this.createMask();
         } else {
-            this.outLine.push(data);
+            this.pointList.push(data);
             this.lastPoint = data;
         }
         this.zOrder = ItemOrder.shadeOrder;
@@ -61,9 +69,9 @@ export class ShadeItem extends SGraphStyleItem {
     onMouseDown(event: SMouseEvent): boolean {
         if (!this.closeFlag && event.buttons == 1) {
             if (
-                this.lastPoint.x == this.outLine[0].x &&
-                this.lastPoint.y == this.outLine[0].y &&
-                this.outLine.length >= 3
+                this.lastPoint.x == this.pointList[0].x &&
+                this.lastPoint.y == this.pointList[0].y &&
+                this.pointList.length >= 3
             ) {
                 this.createMask();
                 return true;
@@ -71,7 +79,7 @@ export class ShadeItem extends SGraphStyleItem {
             let p = new SPoint(event.x, event.y);
             this.lastPoint.x = p.x;
             this.lastPoint.y = p.y;
-            this.outLine.push(p);
+            this.pointList.push(p);
         }
         this.update();
         return true;
@@ -86,18 +94,18 @@ export class ShadeItem extends SGraphStyleItem {
     onMouseMove(event: SMouseEvent): boolean {
         if (!this.closeFlag) {
             this.lastPoint = new SPoint(event.x, event.y);
-            if (this.outLine.length >= 3) {
+            if (this.pointList.length >= 3) {
                 let le = SMathUtil.pointDistance(
                     this.lastPoint.x,
                     this.lastPoint.y,
-                    this.outLine[0].x,
-                    this.outLine[0].y
+                    this.pointList[0].x,
+                    this.pointList[0].y
                 );
                 // @ts-ignore
                 let scale = this.parent.scene.view.scale;
                 if (le * scale < 30) {
-                    this.lastPoint.x = this.outLine[0].x;
-                    this.lastPoint.y = this.outLine[0].y;
+                    this.lastPoint.x = this.pointList[0].x;
+                    this.lastPoint.y = this.pointList[0].y;
                 }
             }
         }
@@ -121,7 +129,7 @@ export class ShadeItem extends SGraphStyleItem {
      * @param	event         事件参数
      */
     onKeyUp(event: KeyboardEvent): void {
-        if (event.keyCode == 13 && this.outLine.length >= 3) {
+        if (event.keyCode == 13 && this.pointList.length >= 3) {
             this.createMask();
         }
     } // Function onKeyUp()
@@ -133,18 +141,66 @@ export class ShadeItem extends SGraphStyleItem {
     createMask(): void {
         this.closeFlag = true;
         this.releaseItem();
+        this.calRect();
         this.$emit('createSuc')
         this.update();
     } // Function createMask()
 
     /**
+     * 计算最大最小值
+    */
+    calRect() {
+        // 点集存在
+        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;
+                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;
+                }
+            });
+        }
+    }
+
+    /**
+     * Item 对象边界区域
+     *
+     * @return 边界区域
+     */
+    boundingRect(): SRect {
+        return new SRect(
+            this.minX,
+            this.minY,
+            this.maxX - this.minX,
+            this.maxY - this.minY
+        );
+    }
+
+    /**
      * 判断点是否在区域内
      *
      * @param x
      * @param y
      */
     contains(x: number, y: number): boolean {
-        let arr = this.outLine;
+        let arr = this.pointList;
         return SPolygonUtil.pointIn(x, y, arr);
     } // Function contains()
 
@@ -158,13 +214,13 @@ export class ShadeItem extends SGraphStyleItem {
         if (this.closeFlag) {
             painter.pen.color = SColor.Transparent;
             painter.brush.color = this.fillColor;
-            painter.drawPolygon(this.outLine);
+            painter.drawPolygon(this.pointList);
         } else {
             painter.pen.color = new SColor("#ff0000");
             painter.pen.lineWidth = painter.toPx(3);
-            painter.drawPolyline(this.outLine);
+            painter.drawPolyline(this.pointList);
             painter.drawLine(
-                this.outLine[this.outLine.length - 1],
+                this.pointList[this.pointList.length - 1],
                 this.lastPoint
             );
         }

+ 105 - 25
src/views/maintain/space/components/spaceGraph.vue

@@ -258,8 +258,11 @@ export default class spaceGraph extends Vue {
         this.config.isEdit = true;
         this.config.groupSelect = false;
         this.config.divide = true;
-        this.scene!.isSpaceSelectable = true;
-        this.scene!.isZoneSelectable = false;
+        if (this.scene) {
+            this.scene.isSpaceSelectable = true;
+            this.scene.isZoneSelectable = false;
+            this.scene.selectContainer.clear()
+        }
         this.view?.update();
     }
     // 创建新的业务空间
@@ -278,8 +281,14 @@ export default class spaceGraph extends Vue {
     handleCommand() {}
     // 编辑业务空间
     refactorBSP() {
-        this.scene!.isZoneSelectable = false;
-        this.scene!.isSpaceSelectable = true;
+        this.config.isEdit = true;
+        this.config.groupSelect = false;
+        this.config.divide = true;
+        if (this.scene) {
+            this.scene.isZoneSelectable = false;
+            this.scene.isSpaceSelectable = true;
+            this.scene.selectContainer.clear();
+        }
         this.type = 4;
         this.curZoneItem.visible = false;
     }
@@ -347,27 +356,15 @@ export default class spaceGraph extends Vue {
     }
     // 重新划分业务空间保存
     saveRefactorBSP() {
-        const arr = this.scene?.selectContainer.itemList;
-        const zoneObj = {
+        let zoneObj = {
             id: this.curZoneItem.data.RoomID,
             classCode: this.curZoneType,
             outline: [],
         };
-        if (arr.length) {
-            arr?.forEach((t) => {
-                if (t instanceof SSpaceItem) {
-                    zoneObj.outline.push(t.data.OutLine);
-                }
-            });
+        if (this.scene) {
+            zoneObj = this.calIntersect(zoneObj);
+            this.handleUpdateZone(zoneObj);
         }
-        const pa = {
-            content: [zoneObj],
-        };
-        this.canvasLoading = true;
-        updateZone(pa).then((res) => {
-            this.$message.success("更新成功");
-            this.init(2);
-        });
     }
 
     groupCreateZone() {}
@@ -405,20 +402,86 @@ export default class spaceGraph extends Vue {
 
     // 根据图创建新的业务空间-弹窗返回确认创建
     createRoom(val) {
-        const zoneObj = {
+        let zoneObj = {
             outline: [],
             localName: val,
             buildingId: this.floor.buildingId,
             floorId: this.floor.id,
             classCode: this.curZoneType,
         };
-        let arr = this.scene?.selectContainer.itemList;
-        arr?.forEach((t) => {
-            zoneObj.outline.push(t.data.OutLine);
-        });
+        if (this.scene) {
+            zoneObj = this.calIntersect(zoneObj);
+            this.handleCreateZone(zoneObj);
+        }
+    }
+
+    /**
+     * 单个计算交集
+     */
+    calIntersect(zoneObj: any) {
+        // 画了切割区域
+        if (this.scene.cutItem && this.scene.cutItem.closeFlag) {
+            const seg = this.scene.getCurInZone();
+            const poly = this.scene.getSpaceCutIntersect(seg);
+            for (let key in poly) {
+                for (let i = 0; i < poly[key].length; i++) {
+                    const arr = poly[key][i].map((t) => {
+                        return t.map((it) => {
+                            return {
+                                X: Number(it.x.toFixed(2)),
+                                Y: -Number(it.y.toFixed(2)),
+                                Z: 0,
+                            };
+                        });
+                    });
+                    zoneObj.outline.push(arr);
+                }
+            }
+        } else if (this.scene.zoneList.length) {
+            // 没画切割区域,但是有业务空间
+            const poly = this.scene.getSpaceZoneIntersect();
+            // 与已有业务空间的外接矩阵有交集
+            if (poly) {
+                for (let key in poly) {
+                    for (let i = 0; i < poly[key].length; i++) {
+                        const arr = poly[key][i].map((t) => {
+                            return t.map((it) => {
+                                return {
+                                    X: Number(it.x.toFixed(2)),
+                                    Y: -Number(it.y.toFixed(2)),
+                                    Z: 0,
+                                };
+                            });
+                        });
+                        zoneObj.outline.push(arr);
+                    }
+                }
+            } else {
+                let arr = this.scene?.selectContainer.itemList;
+                arr?.forEach((t) => {
+                    zoneObj.outline.push(t.data.OutLine);
+                });
+            }
+        } else {
+            // 没有业务空间,也没有绘制切割区域
+            let arr = this.scene?.selectContainer.itemList;
+            arr?.forEach((t) => {
+                zoneObj.outline.push(t.data.OutLine);
+            });
+        }
+        return zoneObj;
+    }
+
+    /**
+     * 创建业务空间接口
+     */
+    handleCreateZone(zoneObj: any) {
         const pa = {
             content: [zoneObj],
         };
+        if (Array.isArray(zoneObj)) {
+            pa.content = zoneObj;
+        }
         this.canvasLoading = true;
         createZone(pa).then((res) => {
             this.$message.success("创建成功");
@@ -426,6 +489,23 @@ export default class spaceGraph extends Vue {
         });
     }
 
+    /**
+     * 更新业务空间接口
+     */
+    handleUpdateZone(zoneObj: any) {
+        const pa = {
+            content: [zoneObj],
+        };
+        if (Array.isArray(zoneObj)) {
+            pa.content = zoneObj;
+        }
+        this.canvasLoading = true;
+        updateZone(pa).then((res) => {
+            this.$message.success("更新成功");
+            this.init(2);
+        });
+    }
+
     // 适配底图到窗口
     fit() {
         this.view?.fitSceneToView();