فهرست منبع

更新版本 增加算法

haojianlong 4 سال پیش
والد
کامیت
8f2205ff81
4فایلهای تغییر یافته به همراه490 افزوده شده و 4 حذف شده
  1. 3 3
      package.json
  2. 375 0
      src/utils/graph/CustomWall.ts
  3. 112 0
      src/utils/graph/generate.ts
  4. 0 1
      src/views/maintain/space/components/spaceGraph.vue

+ 3 - 3
package.json

@@ -15,9 +15,9 @@
   "dependencies": {
     "@babel/runtime": "^7.12.5",
     "@persagy-web/base": "2.2.1",
-    "@persagy-web/big": "2.2.48",
-    "@persagy-web/draw": "2.2.12",
-    "@persagy-web/graph": "2.2.44",
+    "@persagy-web/big": "2.2.49",
+    "@persagy-web/draw": "2.2.13",
+    "@persagy-web/graph": "2.2.45",
     "axios": "^0.21.1",
     "core-js": "^3.6.5",
     "element-ui": "^2.15.0",

+ 375 - 0
src/utils/graph/CustomWall.ts

@@ -0,0 +1,375 @@
+import { SMouseEvent, SUndoStack } from "@persagy-web/base/lib";
+import { SItemStatus } from "@persagy-web/big/lib";
+import { SMathUtil } from "@persagy-web/big/lib/utils/SMathUtil"
+import { SColor, SLine, SPainter, SPoint, SRect } from "@persagy-web/draw/lib";
+import { SGraphItem, SGraphPointListInsert, SGraphStyleItem } from "@persagy-web/graph/lib";
+
+/**
+ * 用户自定义墙
+*/
+export class CustomWall 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[] = [];
+    /** 是否绘制完成 */
+    _status: SItemStatus = SItemStatus.Normal;
+    get status(): SItemStatus {
+        return this._status;
+    }
+    set status(v: SItemStatus) {
+        this._status = v;
+        this.undoStack.clear();
+        this.update();
+    }
+
+    /** 鼠标移动时的点 */
+    protected lastPoint: SPoint | null = null;
+
+    /** 全局灵敏度 */
+    dis: number = 10;
+    /** 灵敏度转换为场景长度 */
+    protected sceneDis: number = 10;
+    /** 当前点索引 */
+    protected curIndex: number = -1;
+    /** 当前点 */
+    private curPoint: SPoint | null = null;
+    /** undo/redo 堆栈 */
+    private undoStack: SUndoStack = new SUndoStack();
+
+    /**
+     * 构造函数
+     *
+     * @param parent    父级
+     * @param list      坐标集合
+     */
+    constructor(parent: null | SGraphItem, list: SPoint[]);
+
+    /**
+     * 构造函数
+     *
+     * @param parent    父级
+     * @param list      第一个坐标
+     */
+    constructor(parent: null | SGraphItem, list: SPoint);
+
+    /**
+     * 构造函数
+     *
+     * @param parent    父级
+     * @param list      第一个坐标|坐标集合
+     */
+    constructor(parent: null | SGraphItem, list: SPoint | SPoint[]) {
+        super(parent);
+        // 数据类型为 SPoint
+        if (list instanceof SPoint) {
+            this.pointList.push(list);
+        } else {
+            this.pointList = list;
+        }
+    }
+
+    /**
+     * 添加点至数组中
+     *
+     * @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 {
+        // 索引值有效且折线列表有2个以上点
+        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(SGraphPointListInsert, [
+                this.pointList,
+                p,
+                index
+            ]);
+            this.curIndex = -1;
+            this.curPoint = null;
+            this.update();
+        }
+    }
+
+    /**
+     * 移动后处理所有坐标,并肩原点置为场景原点
+     *
+     * @param x     x 坐标
+     * @param y     y 坐标
+     */
+    moveToOrigin(x: number, y: number): void {
+        super.moveToOrigin(x, y);
+        // 遍历点集列表
+        this.pointList = this.pointList.map(
+            (t): SPoint => {
+                t.x = t.x + x;
+                t.y = t.y + 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;
+        // 折线点集多余2
+        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 && 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): 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
+        );
+    }
+
+    /**
+     * 判断点是否在区域内
+     *
+     * @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();
+        }
+    }
+
+    /**
+     * Item 绘制操作
+     *
+     * @param painter   绘制对象
+     */
+    onDraw(painter: SPainter): void {
+        // 缓存场景长度
+        this.sceneDis = painter.toPx(this.dis);
+    }
+}

+ 112 - 0
src/utils/graph/generate.ts

@@ -0,0 +1,112 @@
+import { SPoint } from "@persagy-web/draw/lib";
+
+export class generate {
+    /**
+     * 计算角平分线上的距离这两条直线l的点  
+     * 
+     * @param p1 第一个点
+     * @param p2 第二个点 也是2条线的交点
+     * @param p3 第三个点
+     * @param l  距离2条线的距离
+    */
+    static getBisector(p1: SPoint, p2: SPoint, p3: SPoint, l: number) {
+        const dy1 = p1.y - p2.y;
+        const dx1 = p1.x - p2.x;
+
+        const dy2 = p3.y - p2.y;
+        const dx2 = p3.x - p2.x;
+
+        // 线1的斜率
+        const k1 = dy1 / dx1;
+        // 线2的斜率
+        const k2 = dy2 / dx2;
+        // 线1与x轴的夹角
+        const temp1 = Math.atan(k1)
+        const a1 = k1 >= 0 ? temp1 : temp1 + Math.PI;
+        // 线2与x轴的夹角
+        const temp2 = Math.atan(k2)
+        const a2 = k2 >= 0 ? temp2 : temp2 + Math.PI;
+        // 角平分线斜率
+        const k = Math.tan((a1 + a2) / 2);
+        // 角平分线b
+        const b = p2.y - k * p2.x;
+        // 距离两条线l的点 到交点p2的距离
+        const Lb2 = l / Math.sin(Math.abs(a1 - a2) / 2);
+        // 将距离公式与直线方程连立,并且将直线方程代入,得到一元二次方程 Ax^2 + Bx + C = 0;然后根据得根公式得到2个解
+        const A = k * k + 1;
+        const B = 2 * k * b - 2 * p2.x - 2 * k * p2.y;
+        const C = b * b - Lb2 * Lb2 + p2.x * p2.x + p2.y * p2.y - 2 * b * p2.y;
+        // 求解
+        const X1 = (-B + Math.sqrt(B * B - 4 * A * C)) / (2 * A);
+        const X2 = (-B - Math.sqrt(B * B - 4 * A * C)) / (2 * A);
+        const Y1 = k * X1 + b;
+        const Y2 = k * X2 + b;
+
+        return [Number(X1.toFixed(2)), Number(Y1.toFixed(2)), Number(X2.toFixed(2)), Number(Y2.toFixed(2))]
+    }
+
+    /**
+     * 计算一条线的垂线上距离线l的2个点
+     * 
+     * @param p1 点1
+     * @param p2 点2
+     * @param l  距离这条线的距离
+    */
+    static getVertical(p1: SPoint, p2: SPoint, l: number) {
+        const dy1 = p1.y - p2.y;
+        const dx1 = p1.x - p2.x;
+
+        // 线1的斜率
+        const k1 = dy1 / dx1;
+        // 垂线的斜率
+        const k = -1 / k1;
+        // 垂线的b
+        const b = p1.y - k * p1.x;
+        // 将距离公式与直线方程连立,并且将直线方程代入,得到一元二次方程 Ax^2 + Bx + C = 0;然后根据得根公式得到2个解
+        const A = k * k + 1;
+        const B = 2 * k * b - 2 * p1.x - 2 * k * p1.y;
+        const C = b * b - l * l + p1.x * p1.x + p1.y * p1.y - 2 * b * p1.y;
+        // 求解
+        const X1 = (-B + Math.sqrt(B * B - 4 * A * C)) / (2 * A);
+        const X2 = (-B - Math.sqrt(B * B - 4 * A * C)) / (2 * A);
+        const Y1 = k * X1 + b;
+        const Y2 = k * X2 + b;
+
+        return [Number(X1.toFixed(2)), Number(Y1.toFixed(2)), Number(X2.toFixed(2)), Number(Y2.toFixed(2))]
+    }
+
+    /**
+     * 计算线段交点
+     *
+     * @param   line1   线段1
+     * @param   line2   线段2
+     * @return  SPoint  交点 null 平行但不重合 'repeat' 重合
+     */
+    static lineIntersection(
+        p1: SPoint, p2: SPoint, p3: SPoint, p4: SPoint
+    ): SPoint | null | string {
+        let k1 = (p2.y - p1.y) / (p2.x - p1.x);
+        let b1 = p2.y - k1 * p2.x;
+        let k2 = (p4.y - p3.y) / (p4.x - p3.x);
+        let b2 = p3.y - k2 * p3.x;
+        if (k1 == k2) {
+            if (b1 == b2) {
+                return "repeat";
+            }
+            return null;
+        }
+        let intersectionX = (b2 - b1) / (k1 - k2);
+        let intersectionY = k1 * intersectionX + b1;
+        // 取线段上的最大最小值可以上下换
+        let minX = Math.min(p1.x, p2.x);
+        let maxX = Math.max(p3.x, p4.x);
+        if (intersectionX >= minX && intersectionX <= maxX) {
+            return new SPoint(intersectionX, intersectionY);
+        }
+        return null;
+    }
+
+    /**
+     * 去除中间多于的点
+    */
+}

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

@@ -256,7 +256,6 @@ export default class spaceGraph extends Vue {
         this.scene!.isSpaceSelectable = true;
         this.scene!.isZoneSelectable = false;
         this.view?.update();
-        console.log(12312312);
     }
     // 创建新的业务空间
     createNewZone() {