首页 > 其他分享 >(H5前端CAD)在线CAD如何实现图形识别功能

(H5前端CAD)在线CAD如何实现图形识别功能

时间:2024-09-26 18:01:48浏览次数:1  
标签:const minPt 前端 H5 item length let maxPt CAD

前言

CAD图形识别功能可帮助用户快速识别和提取CAD图纸中的各种图形,从而加速设计过程。​可应用在识别与分类阶段,自动识别图纸中的不同元素,通过特定特征进行区分,减少了手动分类的工作量;也可应用在数量统计阶段,统计图纸中各种构件的数量用于预算;还可运用在图纸定位和应用阶段,快速定位图纸上的特定元素,便于快速查找和修改。

mxcad 为用户提供了图形识别功能和API,用户可根据自身需求对该功能进行拓展或二次开发,更多开发文档关注公众号:梦想云图网页CAD。

图形识别步骤

1. 打开mxcad在线示例demo:https://demo.mxdraw3d.com:3000/mxcad/

2. 点击【工具(A)】菜单栏,找到图形识别功能,选择【图形识别】按钮,如下图:

3. 根据命令行提示,点击鼠标左键点选图元或拉框选择需要识别的图形(图形识别功能目前支持识别直线类、多段线类、弧线类、圆类,以及块这五类图元),如下图:

4. 点击右键结束选择后,在弹出的图形识别框内设置查找图形详情,设置图形名称可方便后续查找已识别图形;设置重新选择图形时,会覆盖当前选中图形;设置区域查找时,需要用户框选查找范围,若未框选范围则会默认为全图纸查找图形,如下图:

5. 在图形识别框内点击【开始识别】按钮,开始在目标范围内查找图形,并将查找结果展示在图形识别列表中。点击图形识别列表下的坐标对应的【查看】按钮会自动定位到对应的图形位置,并圈选出目标图形,如下图:

6. 当识别多个图形后,可点击目标图形统计表格对应的操作列中的【详情】按钮查看识别详情,图形坐标列表将转换为该目标图形的识别结果,如下图:

 

识别注意事项:

如果需要识别的图形较为复杂,为保证识别速度与精度,我们需尽可能查找图形中的特殊部分而不是选取整个图形(图形对象过多时可能会导致卡顿,影响用户使用效率)。

图形识别支持区域选择,用户可根据自身需求精确定位图形筛选区域。

 

功能开发

mxcad 图形识别功能中运用的核心思想是通过[McDbEntity]实体中每个实体的特征在图纸中进行查找,查找的实体中包括了[直线类][多段线类][弧线类][圆类],以及[块]

下列为基于mxcad 封装的图形识别类 SearchSamePattern 用户可根据该类的使用方法利用 mxcad 进行二次开发,代码如下:

 

import {
    DxfCode, McCmColor, McDbArc, McDbBlockReference, McDbCircle, McDbLine, McDbPolyline, McDbWipeout, McGePoint3d,
    McGePoint3dArray, McGeVector3d, MxCADResbuf, MxCADSelectionSet, MxCADUtility, MxCpp
} from "mxcad";
import { MxFun } from "mxdraw";
export class SearchSamePattern {
    /** 识别图元数 */
    private objectCount: number = 0;
    /** 选择图形集合 Map<className,[Ent]>*/
    private selectObjects: any;
    private dTol = 0.000001;
    /**
     * pt1:左下角
     * pt2:右上角
     * pt3:左上角
     * pt4:右下角
     */
    private pt1: McGePoint3d;
    private pt2: McGePoint3d;
    private pt3: McGePoint3d;
    private pt4: McGePoint3d;//包围盒大小
    private pl: McDbPolyline;
    /** 识别图形类名集合 [string] */
    private classNames: any
    /** 识别图形图片地址 */
    private patternImgUrl: String
    /** 识别对象详情  Map<ClassName,[{}]>*/
    private objectsDetails: any = {};
    // 下列为识别结果数据
    /** 识别后的图形位置数组集合 */;
    private graphBoxsPos: any[] = [];
    private formPt: McGePoint3d;
    private toPt: McGePoint3d;
    /** 点精度值 */
    private dTolPt: any;
    /** 识别后图形列表 */
    private patternList: any = {};
    /** 选择的图形集合 */
    public setSelectset(ss: MxCADSelectionSet): Promise<String> {
        if (!ss?.count()) return;
        const mxcad = MxCpp.getCurrentMxCAD();
        // 获取当前图纸的空间块表记录包围盒
        const { maxPt, minPt } = mxcad.getDatabase().currentSpace.getBoundingBox();
        const currentDrawOrder = mxcad.getDatabase().currentSpace.getMinMaxDrawOrder()
        const n = MxFun.screenCoordLong2Doc(Math.abs(maxPt.x - minPt.x));
        minPt.x -= n; minPt.y -= n; maxPt.x += n; maxPt.y += n;
        let points = new McGePoint3dArray();
        points.append(minPt);
        points.append(new McGePoint3d(minPt.x, maxPt.y, 0));
        points.append(new McGePoint3d(maxPt.x, maxPt.y, 0));
        points.append(new McGePoint3d(maxPt.x, minPt.y, 0));
        this.objectCount = ss.count();
        // 获取遮罩层drawOrder
        let wipeout = new McDbWipeout
        wipeout.setVertices(points);
        const wipeoutId = mxcad.drawEntity(wipeout);
        const e = wipeoutId.getMcDbEntity();
        e.drawOrder = currentDrawOrder.maxDrawOrder + 1;
        this.selectObjects = {};
        const oldDrawOder = [];
        ss.forEach(id => {
            const ent = id.getMcDbEntity();
            /** 记录识别图形的信息 */
            if (!this.selectObjects[ent.objectName]) this.selectObjects[ent.objectName] = [];
            this.selectObjects[ent.objectName].push(ent);
            if (ent.objectName === 'McDbLine') {
                if (!this.objectsDetails['McDbLine']) this.objectsDetails['McDbLine'] = [];
                this.objectsDetails['McDbLine'].push({ entity: ent, length: (ent as McDbLine).getLength().val });
            } else if (ent.objectName === 'McDbArc') {
                if (!this.objectsDetails['McDbArc']) this.objectsDetails['McDbArc'] = [];
                const length = (ent as McDbArc).getLength().val;
                const { bugle } = this.getBugle((ent as McDbArc), length)
                this.objectsDetails['McDbArc'].push({ entity: ent, length, bugle });
            } else if (ent.objectName === 'McDbCircle') {
                if (!this.objectsDetails['McDbCircle']) this.objectsDetails['McDbCircle'] = [];
                const radius = (ent as McDbCircle).radius;
                this.objectsDetails['McDbCircle'].push({ entity: ent, radius });
            } else if (ent.objectName === 'McDbBlockReference') {
                if (!this.objectsDetails['McDbBlockReference']) this.objectsDetails['McDbBlockReference'] = [];
                const blkName = (ent as McDbBlockReference).blockName;
                this.objectsDetails['McDbBlockReference'].push({ entity: ent, blkName });
            } else if (ent.objectName === 'McDbPolyline') {
                if (!this.objectsDetails['McDbPolyline']) this.objectsDetails['McDbPolyline'] = [];
                const length = (ent as McDbPolyline).getLength().val;
                this.objectsDetails['McDbPolyline'].push({ entity: ent, length });
            }
            /** 渲染顺序 */
            oldDrawOder.push({ obj: ent, drawOrder: ent.drawOrder });
            ent.drawOrder = e.drawOrder + 1;
            const { maxPt, minPt } = ent.getBoundingBox();
            if (!this.pt1 || !this.pt2) {
                this.pt1 = minPt;
                this.pt2 = maxPt;
            } else {
                if (minPt.x < this.pt1.x) this.pt1.x = minPt.x;
                if (minPt.y < this.pt1.y) this.pt1.y = minPt.y;
                if (maxPt.x > this.pt2.x) this.pt2.x = maxPt.x;
                if (maxPt.y > this.pt2.y) this.pt2.y = maxPt.y;
            }
        });
        this.classNames = Object.keys(this.selectObjects);
        /**
         * 计算包围盒其他两个角点
         */
        this.pt3 = new McGePoint3d(this.pt1.x, this.pt2.y);
        this.pt4 = new McGePoint3d(this.pt2.x, this.pt1.y);
        /** 绘制识别图形包围盒 */
        this.pl = new McDbPolyline()
        this.pl.addVertexAt(this.pt1);
        this.pl.addVertexAt(this.pt3);
        this.pl.addVertexAt(this.pt2);
        this.pl.addVertexAt(this.pt4);
        this.pl.isClosed = true;
        const num = MxFun.screenCoordLong2Doc(10);
        this.pt1.x -= num; this.pt1.y -= num; this.pt2.x += num; this.pt2.y += num;
        // 生成预览图
        let w = Math.abs(this.pt1.x - this.pt2.x);
        let h = Math.abs(this.pt1.y - this.pt2.y);
        if (w < 1 || h < 1) return;
        let jpg_width = 300;
        let jpg_height = 240;
        return new Promise<String>((resolve, reject) => {
            MxFun.getCurrentDraw().createCanvasImageData(
                (imageData: String) => {
                    this.patternImgUrl = imageData;
                    // 恢复绘制顺序
                    oldDrawOder.forEach(item => {
                        item.obj.drawOrder = item.drawOrder;
                    })
                    wipeoutId.erase();
                    resolve(imageData)
                },
                {
                    width: jpg_width, // 图片宽
                    height: jpg_height, // 图片高
                    range_pt1: this.pt1.toVector3(), // 截图范围角点1
                    range_pt2: this.pt2.toVector3(), // 截图范围角点2
                }
            );
        });
    };
    // 开始识别图形
    /**
     * 1.查找初始包围盒
     * 2.筛选符合条件的包围盒
     * 3.比较包围盒内的每一个图元与需要识别的图元是否匹配
     */
    public identifyGraphics(corner1?: McGePoint3d, corner2?: McGePoint3d): any {
        /** 记录图形缩略图 */
        this.patternList.patternImgUrl = this.patternImgUrl;
        /**初始化图形位置列表 */
        this.graphBoxsPos.length = 0;
        const mxcad = MxCpp.getCurrentMxCAD();
        this.dTolPt = mxcad.mxdraw.viewCoordLong2Cad(0.5);// 设置精度值
        if (!this.classNames) return;
        const array = this.classNames.map(name => this.selectObjects[name].length);
        const minIndex = array.findIndex(element => element === Math.min(...array));
        const str = this.classNames[minIndex];
        /** 识别块 */
        if (str === 'McDbBlockReference') {
            const refBlk = this.selectObjects['McDbBlockReference'][0].clone() as McDbBlockReference;
            const blkName = refBlk.blockName; // 图块名
            const { position } = this.getBlkMidPt(refBlk);
            this.formPt = position;
            const rotate = refBlk.rotation;
            // 设置图块过滤器
            const filter = new MxCADResbuf([DxfCode.kEntityType, "INSERT"])
            const ss = new MxCADSelectionSet()
            if (corner1 && corner2) {
                // 局部选择
                ss.crossingSelect(corner1.x, corner1.y, corner2.x, corner2.y, filter);
            } else {
                // 全局选择
                ss.allSelect(filter);
            };
            // 筛选出符合条件的块
            const boxs: any[] = [];
            ss.forEach(id => {
                const _blk = id.getMcDbEntity().clone() as McDbBlockReference;
                /** 块名相等=》计算旋转角度=》计算包围盒大小 */
                if (blkName === _blk.blockName) {
                    const _rotate = _blk.rotation;
                    const angle = rotate >= 0 ? _rotate - rotate : rotate - _rotate;
                    const { position: _position, minPt, maxPt } = this.getBlkMidPt(_blk);
                    if (this.classNames.length === 1) {
                        this.toPt = _position;
                        this.identificationBBox(minPt, maxPt, 0)
                    } else {
                        // 平移旋转初始包围盒。计算出最新包围盒的大小
                        const _pl = this.pl.clone() as McDbPolyline;
                        _pl.move(this.formPt, _position);
                        _pl.rotate(_position, angle);
                        let x = [], y = [];
                        for (let i = 0; i < _pl.numVerts(); i++) {
                            const pt = _pl.getPointAt(i).val;
                            x.push(pt.x);
                            y.push(pt.y);
                        }
                        const _minPt = new McGePoint3d(Math.min(...x) - 1, Math.min(...y) - 1);
                        const _maxPt = new McGePoint3d(Math.max(...x) + 1, Math.max(...y) + 1);
                        const res = boxs.filter(item => {
                            let ret1 = _minPt.distanceTo(item.pt1) < this.dTolPt && _maxPt.distanceTo(item.pt2) < this.dTolPt
                            let ret2 = _minPt.distanceTo(item.pt2) < this.dTolPt && _maxPt.distanceTo(item.pt1) < this.dTolPt
                            return ret1 || ret2
                        })
                        if (!res.length) {
                            boxs.push({ pt1: _minPt, pt2: _maxPt });
                            this.toPt = _position;
                            // MxCpp.getCurrentMxCAD().drawLine(_maxPt.x, _maxPt.y, _minPt.x, _minPt.y);
                            this.identificationBBox(_minPt, _maxPt, angle);
                        }
                    }
                }
            });
        }
        /** 识别弧线 */
        else if (str === 'McDbArc') {
            // 识别直线
            const refArc = this.selectObjects['McDbArc'][0].clone() as McDbArc;
            const length = refArc.getLength().val; // 弧线线长度
            // 圆弧凸度
            const { bugle, midPt } = this.getBugle(refArc, length);
            const center = refArc.center;
            const vec = center.sub(midPt);
            // 设置弧线过滤器
            const filter = new MxCADResbuf([DxfCode.kEntityType, "ARC"])
            const ss = new MxCADSelectionSet()
            if (corner1 && corner2) {
                // 局部选择
                ss.crossingSelect(corner1.x, corner1.y, corner2.x, corner2.y, filter);
            } else {
                // 全局选择
                ss.allSelect(filter);
            };
            // 筛选出符合条件的弧线
            const boxs: any[] = [];
            ss.forEach(id => {
                const _arc = id.getMcDbEntity().clone() as McDbArc;
                const _length = _arc.getLength().val;
                const { bugle: _bugle, midPt: _midPt } = this.getBugle(_arc, _length);
                /** 筛选出圆弧凸度与长度相同的弧线*/
                if (Math.abs(_length - length) < this.dTol && Math.abs(_bugle - bugle) < this.dTol) {
                    /** 分两种情况
                     * 1.直接旋转平移得到
                     * 2.镜像得到=》移动到目标位置+旋转角度+镜像翻转=》比较起始点位置
                     */
                    // 平移+旋转
                    const _center = _arc.center;
                    const _vec = _center.sub(_midPt);
                    const angle = _vec.angleTo2(vec, McGeVector3d.kNegateZAxis);
                    // 旋转平移初始包围盒。计算出最新包围盒的大小
                    const _pl = this.pl.clone() as McDbPolyline;
                    _pl.move(midPt, _midPt);
                    _pl.rotate(_midPt, angle);
                    let x = [], y = [];
                    for (let i = 0; i < _pl.numVerts(); i++) {
                        const pt = _pl.getPointAt(i).val;
                        x.push(pt.x);
                        y.push(pt.y);
                    }
                    const _minPt = new McGePoint3d(Math.min(...x) - 1, Math.min(...y) - 1);
                    const _maxPt = new McGePoint3d(Math.max(...x) + 1, Math.max(...y) + 1);
                    const res = boxs.filter(item => {
                        let ret1 = _minPt.distanceTo(item.pt1) < this.dTolPt && _maxPt.distanceTo(item.pt2) < this.dTolPt
                        let ret2 = _minPt.distanceTo(item.pt2) < this.dTolPt && _maxPt.distanceTo(item.pt1) < this.dTolPt
                        let ret3 = _midPt.distanceTo(item.midPt) < this.dTolPt
                        return (ret1 || ret2) && ret3
                    })
                    if (!res.length) {
                        boxs.push({ pt1: _minPt, pt2: _maxPt, midPt: _midPt });
                        this.toPt = _midPt;
                        this.formPt = midPt;
                        this.identificationBBox(_minPt, _maxPt, angle);
                    }
                }
            });
        }
        /** 识别直线 */
        else if (str === 'McDbLine') {
            // 识别直线
            const refLine = this.selectObjects['McDbLine'][0].clone() as McDbLine;
            const length = refLine.getLength().val; // 直线长度
            const vec = refLine.endPoint.sub(refLine.startPoint); // 直线角度
            // 设置直线过滤器
            const filter = new MxCADResbuf([DxfCode.kEntityType, "LINE"])
            const ss = new MxCADSelectionSet()
            if (corner1 && corner2) {
                // 局部选择
                ss.crossingSelect(corner1.x, corner1.y, corner2.x, corner2.y, filter);
            } else {
                // 全局选择
                ss.allSelect(filter);
            };
            // 筛选出符合条件的直线
            const lineArr: McDbLine[] = [];
            ss.forEach(id => {
                const line = id.getMcDbEntity().clone() as McDbLine;
                const _length = line.getLength().val;
                /**
                 * 1.未旋转
                 * 2.旋转过
                 */
                if (Math.abs(_length - length) < this.dTol) {
                    lineArr.push(line);
                }
            });
            // 筛选出符合条件的直线后,根据包围盒范围再次筛选出符合条件的直线组合
            const boxs: any[] = [];
            if (lineArr.length > 0) {
                lineArr.forEach((item) => {
                    /**
                     * 将要识别的图形进行平移+旋转到目标位置
                     */
                    const ptArr = [item.startPoint, item.endPoint];

                    ptArr.forEach((point, index) => {
                        const _line = refLine.clone() as McDbLine;
                        _line.move(refLine.startPoint, point);
                        const _vec = ptArr[1 - index].sub(point);
                        const angle = _vec.angleTo2(vec, McGeVector3d.kNegateZAxis);
                        _line.rotate(point, angle)
                        const pt = _line.endPoint;
                        const _pl = this.pl.clone() as McDbPolyline;
                        _pl.move(refLine.startPoint, point);
                        _pl.rotate(point, angle);
                        const _refBoxPt1 = _pl.getPointAt(0).val;
                        const _refBoxPt2 = _pl.getPointAt(1).val;
                        const refVec1 = _refBoxPt1.sub(_line.startPoint);
                        const refVec2 = _refBoxPt2.sub(_line.startPoint);
                        if (pt.distanceTo(ptArr[1 - index]) < this.dTolPt && refVec1.isEqualTo(_refBoxPt1.sub(point)) && refVec2.isEqualTo(_refBoxPt2.sub(point))) {
                            let x = [], y = [];
                            for (let i = 0; i < _pl.numVerts(); i++) {
                                const pt = _pl.getPointAt(i).val;
                                x.push(pt.x);
                                y.push(pt.y);
                            }
                            const _minPt = new McGePoint3d(Math.min(...x) - 1, Math.min(...y) - 1);
                            const _maxPt = new McGePoint3d(Math.max(...x) + 1, Math.max(...y) + 1);
                            const res = boxs.filter(item => {
                                let r1 = _minPt.distanceTo(item.pt1) < this.dTolPt && _maxPt.distanceTo(item.pt2) < this.dTolPt
                                let r2 = _minPt.distanceTo(item.pt2) < this.dTolPt && _maxPt.distanceTo(item.pt1) < this.dTolPt
                                return r1 || r2
                            })
                            if (!res.length) {
                                boxs.push({ pt1: _minPt, pt2: _maxPt });
                                this.toPt = point;
                                this.formPt = refLine.startPoint;
                                // MxCpp.getCurrentMxCAD().drawLine(_maxPt.x, _maxPt.y, _minPt.x, _minPt.y);
                                this.identificationBBox(_minPt, _maxPt, angle);
                            }
                        }
                    })
                })
            };
        }
        /** 识别圆 */
        else if (str === 'McDbCircle') {
            const refCricle = this.selectObjects['McDbCircle'][0].clone() as McDbCircle;
            const radius = refCricle.radius; // 圆半径
            // 设置圆过滤器
            const filter = new MxCADResbuf([DxfCode.kEntityType, "CIRCLE"])
            const ss = new MxCADSelectionSet()
            if (corner1 && corner2) {
                // 局部选择
                ss.crossingSelect(corner1.x, corner1.y, corner2.x, corner2.y, filter);
            } else {
                // 全局选择
                ss.allSelect(filter);
            };
            // 筛选出符合条件的圆
            ss.forEach(id => {
                const _circle = id.getMcDbEntity().clone() as McDbCircle;
                const _radius = _circle.radius;
                /** 筛选出圆半径相同的圆*/
                if (Math.abs(_radius - radius) < this.dTol) {
                    const _center = _circle.center;
                    this.toPt = _center;
                    this.formPt = refCricle.center;
                    // 平移初始包围盒。计算出最新包围盒的大小
                    const _pl = this.pl.clone() as McDbPolyline;
                    _pl.move(this.formPt, this.toPt);
                    let x = [], y = [];
                    for (let i = 0; i < _pl.numVerts(); i++) {
                        const pt = _pl.getPointAt(i).val;
                        x.push(pt.x);
                        y.push(pt.y);
                    }
                    const _minPt = new McGePoint3d(Math.min(...x) - 1, Math.min(...y) - 1);
                    const _maxPt = new McGePoint3d(Math.max(...x) + 1, Math.max(...y) + 1);
                    // MxCpp.getCurrentMxCAD().drawLine(_maxPt.x, _maxPt.y, _minPt.x, _minPt.y);
                    this.identificationBBox(_minPt, _maxPt, 0);
                }
            });
        }
        /** 识别多段线 */
        else if (str === 'McDbPolyline') {
            /** 长度+顶点数+顶点对应的凸度相等=》旋转+平移后的顶点相等+有凸度线段的中点相等*/
            const refPoly = this.selectObjects['McDbPolyline'][0].clone() as McDbPolyline;
            const length = refPoly.getLength().val;
            const { lengthArr, bugleArr, vertNum, vec, point } = this.getPlDetails(refPoly);
            // 设置多段线过滤器
            const filter = new MxCADResbuf([DxfCode.kEntityType, "LWPOLYLINE"])
            const ss = new MxCADSelectionSet()
            if (corner1 && corner2) {
                // 局部选择
                ss.crossingSelect(corner1.x, corner1.y, corner2.x, corner2.y, filter);
            } else {
                // 全局选择
                ss.allSelect(filter);
            };
            // 筛选出符合条件的多段线
            const boxs: any[] = [];
            ss.forEach(id => {
                const _poly = id.getMcDbEntity().clone() as McDbPolyline;
                const _length = _poly.getLength().val;
                /** 筛选 长度+顶点数+顶点对应的凸度 相同的多段线*/
                if (Math.abs(_length - length) < this.dTol && _poly.isClosed === refPoly.isClosed) {
                    const { lengthArr: _lengthArr, bugleArr: _bugleArr, vertNum: _vertNum, vec: _vec, point: _point } = this.getPlDetails(_poly);
                    const revBugleArr = _bugleArr.map(item => item === 0 ? item : -item);
                    // 全等
                    const r1 = lengthArr.toString() === _lengthArr.toString() && bugleArr.toString() === _bugleArr.toString();
                    let r2 = false;
                    if (!r1) {
                        // 反向
                        r2 = lengthArr.toString() === [..._lengthArr].reverse().toString() && bugleArr.toString() === [...revBugleArr].reverse().toString();
                    }
                    const r3 = vertNum === _vertNum;
                    // 镜像
                    const r4 = lengthArr.toString() === _lengthArr.toString() && bugleArr.toString() === [...revBugleArr].reverse().toString();
                    if ((r1 || r2 || r4) && r3) {
                        const poly = refPoly.clone() as McDbPolyline;
                        const angle1 = vec.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
                        // 如果是反向则方向取反
                        const angle2 = (r2 ? _vec.negate() : _vec).angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
                        const angle = angle2 - angle1;
                        poly.move(point, _point);
                        poly.rotate(_point, angle);
                        poly.trueColor = new McCmColor(255, 0, 0);
                        let num = 0;
                        for (let i = 0; i < vertNum; i++) {
                            const pt = poly.getPointAt(i).val;
                            const width = poly.getWidthsAt(i);
                            const _pt = r2 ? _poly.getPointAt(vertNum - 1 - i).val : _poly.getPointAt(i).val;
                            const _width = r2 ? _poly.getWidthsAt(vertNum - 1 - i) : _poly.getWidthsAt(i);
                            if (pt.distanceTo(_pt) < this.dTolPt && width.val1 === _width.val1 && width.val2 === _width.val2) {
                                num += 1
                            } else {
                                // 两个点且反向的情况
                                if (vertNum === 2 && i === 0) {
                                    const _pt1 = _poly.getPointAt(0).val;
                                    const _pt2 = _poly.getPointAt(1).val;
                                    const pt2 = poly.getPointAt(1).val;
                                    const _width = _poly.getWidthsAt(0);
                                    if (pt2.distanceTo(_pt2) < this.dTolPt && pt.distanceTo(_pt1) < this.dTolPt && width.val1 === _width.val2 && width.val2 === _width.val1) {
                                        num += 2;
                                    }
                                }
                            }

                        };
                        if (num === vertNum) {
                            // 旋转平移初始包围盒。计算出最新包围盒的大小
                            const _pl = this.pl.clone() as McDbPolyline;
                            _pl.move(point, _point);
                            _pl.rotate(_point, angle);
                            let x = [], y = [];
                            for (let i = 0; i < _pl.numVerts(); i++) {
                                const pt = _pl.getPointAt(i).val;
                                x.push(pt.x);
                                y.push(pt.y);
                            }
                            const _minPt = new McGePoint3d(Math.min(...x) - 1, Math.min(...y) - 1);
                            const _maxPt = new McGePoint3d(Math.max(...x) + 1, Math.max(...y) + 1);
                            const res = boxs.filter(item => {
                                let ret1 = _minPt.distanceTo(item.pt1) < this.dTolPt && _maxPt.distanceTo(item.pt2) < this.dTolPt
                                let ret2 = _minPt.distanceTo(item.pt2) < this.dTolPt && _maxPt.distanceTo(item.pt1) < this.dTolPt
                                return ret1 || ret2
                            })
                            if (!res.length) {
                                boxs.push({ pt1: _minPt, pt2: _maxPt });
                                this.toPt = _point;
                                this.formPt = point;
                                // MxCpp.getCurrentMxCAD().drawLine(_maxPt.x, _maxPt.y, _minPt.x, _minPt.y);
                                this.identificationBBox(_minPt, _maxPt, angle);
                            }
                        }
                    }
                }
            });
        }
        /** 返回识别列表 */
        return this.patternList;
    }
    // 计算块中心点
    private getBlkMidPt = (blkRef: McDbBlockReference) => {
        const { minPt, maxPt } = blkRef.getBoundingBox();
        const pt = minPt.clone().addvec(maxPt.sub(minPt).normalize().mult(maxPt.distanceTo(minPt) / 2))
        return { position: pt, minPt, maxPt }
    }
    // 计算凸度
    private getBugle = (arc: McDbArc, length: number) => {
        const midPt = arc.getPointAtDist(length / 2).val;
        const bugle = MxCADUtility.calcBulge(arc.getPointAtDist(0).val, midPt, arc.getPointAtDist(length).val).val;
        return { bugle, midPt }
    }
    // 计算多段线的每一段线的长度和凸度
    private getPlDetails = (pl: McDbPolyline) => {
        const vertNum = pl.numVerts();
        const lengthArr: number[] = [];
        const bugleArr: number[] = [];
        let point1: McGePoint3d, point2: McGePoint3d;
        for (let i = 0; i < vertNum; i++) {
            if (i < vertNum - 1) {
                const p1 = pl.getPointAt(i).val;
                const p2 = pl.getPointAt(i + 1).val;
                const length = pl.getDistAtPoint(p2).val - pl.getDistAtPoint(p1).val;
                const bugle = pl.getBulgeAt(i);
                lengthArr.push(Number(length.toFixed(6)));
                bugleArr.push(Number(bugle.toFixed(6)));
                if (i === 0) {
                    point1 = p1;
                }
                if (i === vertNum - 2) {
                    point2 = p2;
                }
            }
        };
        const vec = point2.sub(point1)// 多段线角度
        const point = point1.clone().addvec(point2.sub(point1).normalize().mult(point2.distanceTo(point1) / 2))
        return { lengthArr, bugleArr, vertNum, vec, point }
    }
    // 将找到的实体数组与需要识别的实体数组比对
    private comparisonPhysical = (objects: any, maxPt: McGePoint3d, minPt: McGePoint3d, angle: number): any => {
        const calssNames = Object.keys(this.selectObjects);
        if (!calssNames) return;
        let num = 0;
        if (calssNames.includes('McDbLine')) {
            let ents = objects['McDbLine'];
            const lineObjects = this.objectsDetails['McDbLine'];
            for (let i = 0; i < lineObjects.length; i++) {
                const element = lineObjects[i];
                let _line = (element.entity as McDbLine).clone() as McDbLine;
                _line.move(this.formPt, this.toPt);
                _line.rotate(this.toPt, angle);
                const indexArr: number[] = [];
                const mLineArr = ents.filter((item: McDbLine, index: number) => {
                    if (Math.abs(element.length - item.getLength().val) < this.dTol) {
                        let r1 = item.startPoint.distanceTo(_line.startPoint) < this.dTolPt && item.endPoint.distanceTo(_line.endPoint) < this.dTolPt;
                        let r2 = item.startPoint.distanceTo(_line.endPoint) < this.dTolPt && item.endPoint.distanceTo(_line.startPoint) < this.dTolPt;
                        if (r1 || r2) indexArr.push(index);
                        return r1 || r2;
                    }
                });
                if (mLineArr.length > 0) {
                    num += 1;
                    ents.splice(indexArr[0], 1);
                    ents = [...ents];
                } else {
                    break;
                }
            }
        };
        if (calssNames.includes('McDbArc')) {
            let ents = objects['McDbArc'];
            const arcObjects = this.objectsDetails['McDbArc'];
            for (let i = 0; i < arcObjects.length; i++) {
                const element = arcObjects[i];
                let _arc = (element.entity as McDbArc).clone() as McDbArc;
                _arc.move(this.formPt, this.toPt);
                _arc.rotate(this.toPt, angle);
                const startPoint = _arc.getPointAtDist(0).val;
                const endPoint = _arc.getPointAtDist(element.length).val;
                const indexArr: number[] = [];
                const mArcArr = ents.filter((item: McDbArc, index: number) => {
                    const _length = item.getLength().val;
                    const { bugle: _bugle } = this.getBugle(item, _length)
                    if (Math.abs(element.length - item.getLength().val) < this.dTol && Math.abs(_bugle - element.bugle) < this.dTol) {
                        const _startPoint = item.getPointAtDist(0).val;
                        const _endPoint = item.getPointAtDist(_length).val;
                        let r1 = startPoint.distanceTo(_startPoint) < this.dTolPt && endPoint.distanceTo(_endPoint) < this.dTolPt;
                        let r2 = startPoint.distanceTo(_endPoint) < this.dTolPt && endPoint.distanceTo(_startPoint) < this.dTolPt;
                        if (r1 || r2) {
                            indexArr.push(index);
                        }
                        return r1 || r2;
                    }
                });
                if (mArcArr.length > 0) {
                    num += 1;
                    ents.splice(indexArr[0], 1);
                    ents = [...ents];
                } else {
                    break;
                }
            }
        };
        if (calssNames.includes('McDbCircle')) {
            let ents = objects['McDbCircle'];
            const circleObjects = this.objectsDetails['McDbCircle'];
            for (let i = 0; i < circleObjects.length; i++) {
                const element = circleObjects[i];
                let _circle = (element.entity as McDbCircle).clone() as McDbCircle;
                _circle.move(this.formPt, this.toPt);
                _circle.rotate(this.toPt, angle);
                const center = _circle.center;
                const radius = _circle.radius;
                const indexArr: number[] = [];
                const mCiecleArr = ents.filter((item: McDbCircle, index: number) => {
                    const _center = item.center;
                    const _radius = item.radius;
                    const r = Math.abs(_radius - radius) < this.dTol && center.distanceTo(_center) < this.dTolPt;
                    if (r) {
                        indexArr.push(index)
                    }
                    return r;
                });
                if (mCiecleArr.length > 0) {
                    num += 1;
                    ents.splice(indexArr[0], 1);
                    ents = [...ents];
                } else {
                    break;
                }
            }
        };
        if (calssNames.includes('McDbBlockReference')) {
            /** 平移+旋转=>比对包围盒大小+块名字 */
            let ents = objects['McDbBlockReference'];
            const blkObjects = this.objectsDetails['McDbBlockReference'];
            for (let i = 0; i < blkObjects.length; i++) {
                const element = blkObjects[i];
                let _blk = (element.entity as McDbBlockReference).clone() as McDbBlockReference;
                _blk.move(this.formPt, this.toPt);
                _blk.rotate(this.toPt, angle);
                const blkName = _blk.blockName;
                const { minPt: blkMinPt, maxPt: blkMaxPt } = this.getBlkMidPt(_blk);
                const indexArr: number[] = [];
               const mArcArr = ents.filter((item: McDbBlockReference, index: number) => {
                    const _blkName = item.blockName;
                    if (_blkName === blkName) {
                        if (this.classNames.length === 1) {
                            indexArr.push(index);
                            return true
                        } else {
                            const { minPt: _blkMinPt, maxPt: _blkMaxPt } = this.getBlkMidPt(item);
                            let r1 = blkMinPt.distanceTo(_blkMinPt) < this.dTolPt && blkMaxPt.distanceTo(_blkMaxPt) < this.dTolPt;
                            if (r1) {
                                indexArr.push(index);
                            }
                            return r1
                        }
                    }
                });
                if (mArcArr.length > 0) {
                    num += 1;
                    ents.splice(indexArr[0], 1);
                    ents = [...ents];
                } else {
                    return;
                }
            }
        }
        if (calssNames.includes('McDbPolyline')) {
            /** 平移+旋转=>比对凸度+点位置 */
            let ents = objects['McDbPolyline'];
            const plObjects = this.objectsDetails['McDbPolyline'];
            for (let i = 0; i < plObjects.length; i++) {
                const element = plObjects[i];
                let _poly = (element.entity as McDbPolyline).clone() as McDbPolyline;
                _poly.move(this.formPt, this.toPt);
                _poly.rotate(this.toPt, angle);
                const { lengthArr, bugleArr, vertNum } = this.getPlDetails(_poly);
                const indexArr: number[] = [];
                const mPlArr = ents.filter((item: McDbPolyline, index: number) => {
                    const _length = item.getLength().val;
                    if (Math.abs(_length - element.length) < this.dTol && _poly.isClosed === _poly.isClosed) {
                        const { lengthArr: _lengthArr, bugleArr: _bugleArr, vertNum: _vertNum } = this.getPlDetails(item);
                        const revBugleArr = _bugleArr.map(item => item === 0 ? item : -item);
                        const r1 = lengthArr.toString() === _lengthArr.toString() && bugleArr.toString() === _bugleArr.toString();
                        let r2 = false;
                        if (!r1) {
                            r2 = lengthArr.toString() === [..._lengthArr].reverse().toString() && bugleArr.toString() === [...revBugleArr].reverse().toString();
                        }
                        const r3 = vertNum === _vertNum;
                        if ((r1 || r2) && r3) {
                            let _num = 0;
                            for (let i = 0; i < vertNum; i++) {
                                const pt = _poly.getPointAt(i).val;
                                const width = _poly.getWidthsAt(i);
                                const _pt = r2 ? item.getPointAt(vertNum - 1 - i).val : item.getPointAt(i).val;
                                const _width = r2 ? item.getWidthsAt(vertNum - 1 - i) : item.getWidthsAt(i);
                                if (pt.distanceTo(_pt) < this.dTolPt && width.val1 === _width.val1 && width.val2 === _width.val2) {
                                    _num += 1
                                } else {
                                    // 两个点且反向的情况
                                    if (vertNum === 2 && i === 0) {
                                        const _pt1 = item.getPointAt(0).val;
                                        const _pt2 = item.getPointAt(1).val;
                                        const _width = item.getWidthsAt(0);
                                        const pt2 = _poly.getPointAt(1).val;
                                        if (pt2.distanceTo(_pt2) < this.dTolPt && pt.distanceTo(_pt1) < this.dTolPt && width.val1 === _width.val2 && width.val2 === _width.val1) {
                                           _num += 2;
                                        }
                                    }
                                }
                            };
                            if (_num === vertNum) {
                                indexArr.push(index);
                            }
                            return _num === vertNum;
                        }
                    }
                });
                if (mPlArr.length > 0) {
                    num += 1;
                    ents.splice(indexArr[0], 1);
                    ents = [...ents];
                } else {
                    break;
                }
            }
        }
        if (num === this.objectCount) {
            // 匹配成功
            const pt = minPt.clone().addvec(maxPt.clone().sub(minPt).normalize().mult(minPt.distanceTo(maxPt) / 2));
            const res = this.graphBoxsPos.filter(point => pt.distanceTo(point) < this.dTolPt);
            if (!res.length) {
                this.graphBoxsPos.push(pt)
            }
        };
        this.patternList.graphBoxsPos = [...this.graphBoxsPos];
    }
    // 识别包围盒是否符合要求
    private identificationBBox = (minPt: McGePoint3d, maxPt: McGePoint3d, angle: number): any => {
        const _ss = new MxCADSelectionSet()
        _ss.crossingSelect(minPt.x, minPt.y, maxPt.x, maxPt.y);
        const boxEntity = {};
        _ss.forEach(id => {
            const ent = id.getMcDbEntity();
            if (!boxEntity[ent.objectName]) boxEntity[ent.objectName] = [];
            boxEntity[ent.objectName].push(ent);
        });
        let r = this.classNames.filter(name => boxEntity[name] && this.selectObjects[name].length <= boxEntity[name].length);
        if (r.length === this.classNames.length) {
            this.comparisonPhysical(boxEntity, maxPt, minPt, angle);
        }
    }
};

调用 SearchSamePattern 类,实现图形识别功能,代码如下:

/** 获取需要识别的图形 */
const getIdentificationPattern = async () => {
    // 获取需要识别的图形
    const ss = new MxCADSelectionSet();// 选择集
    const filter = new MxCADResbuf([DxfCode.kEntityType, "LINE,ARC,CIRCLE,LWPOLYLINE,INSERT"])
    if (!await ss.userSelect("请点击左键或拉框选择需要识别的图形:", filter)) return;
    if (ss.count() == 0) return;
    // 搜索图形
    const searchPattern = new SearchSamePattern();
    // 搜索图形简图地址
    const patternImgUrl = await searchPattern.setSelectset(ss);
    if (!patternImgUrl.value) return;
    // 获取识别结果
    const searchList = searchPattern.identifyGraphics();
    console.log(searchList);
}

 

标签:const,minPt,前端,H5,item,length,let,maxPt,CAD
From: https://www.cnblogs.com/yzy0224/p/18434012

相关文章

  • 广州浮点中望CAD软件许可优化实施成功案例
    浮动版中望CAD软件许可证优化、降本增效实施行业:机械制造实施软件:中望CAD软件一、背景概述1.项目背景中望CAD软件是广泛应用于机械制造、建筑设计等多个领域的设计工具,其强大的功能与灵活的设计能力使得企业能够高效进行设计与生产。某大型制造企业在日常设计工作中依赖中望CAD进......
  • 前端知识点
    前言1.1、CS架构和BS架构web网页,我们都不陌生,相信大家都有使用web浏览上网的。那么,学习制作网页前,我们需要清晰哪些概念,准备哪些工具呢?接下来,我们要先了解下。在日常生活中,我们都会上网,或聊天、或阅读、或购物等,浏览很多的网站,而网站就是由网页组成。在互联网中,软件系统体系分两......
  • 微信小程序如何调起H5页面的支付?
    做过微信公众号支付(JSAPI)的同学,会比较熟悉,调起微信支付所需要的六个必须参数:appId、timeStamp、nonceStr、package、signType。JSAPI官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=7_7&index=6我们再来看一下小程序支付的官方介绍:https://pay.weixin.qq.co......
  • 如何利用大模型提升前端研发效率和代码质量
     随着人工智能技术的飞速发展,尤其是大模型(LargeLanguageModels,LLM)的崛起,前端开发者迎来了全新的工作方式。大模型不仅可以提升研发效率,还能够显著提高代码质量。本文将深入探讨前端开发者如何利用大模型及其相关工具,提升工作效率和代码质量,并探讨未来可能的应用场景和发展方向......
  • 【前端学习】 NestJS 之 控制器 (Controller)
    文章目录控制器(Controller)*路由(router)请求对象(requestobject)*资源(Resources)状态码(statuscode)标头&重定向(headers&redirection)*路由参数(routeparameters)*请求负载(requestpayloads)启动并运行小结控制器(Controller)控制器负责处理......
  • JavaScript 数据可视化:前端开发的核心工具
    随着互联网和大数据的快速发展,数据呈爆炸式增长,如何有效地展示和理解数据成为了一项关键技能。JavaScript作为前端开发的主要语言,不仅在构建网页方面无可替代,也在数据可视化领域发挥了重要作用。从简单的图表到复杂的交互式展示,JavaScript为开发者提供了丰富的工具和框架,帮助......
  • Centos安装前端开发常用软件
    1.下载指定的版本(根据程序要求下载对应的版本)wget--no-check-certificatehttps://npm.taobao.org/mirrors/node/v10.16.3/node-v10.16.3-linux-x64.tar.gz2.解压并创建系统链接tarxfnode-v10.16.3-linux-x64.tar.gz-C/usr/local/#解压到指定目录cd/usr/local/node-v10.......
  • 大文件上传1(前端)
    前言:近期要做一个视频网站,但是管理平台需要上传音/视频,记录一下这种大文件上传的方法吧。方案:断点续传(分片上传)实现断点续传的逻辑在上传前检查文件的哈希值,判断哪些分片已经上传,避免重复上传在所有分片上传完成后,合并分片技术栈:vue3+element-plus+vite+webworker......
  • 前端面试题(七)
    33.前端状态管理什么是状态管理?状态管理是指在应用程序中管理和维护不同组件之间共享的数据状态的过程。随着应用规模的扩大,状态管理变得愈发复杂,尤其是在单页应用(SPA)中。常见的状态管理库有哪些?Redux:一个流行的JavaScript状态管理库,基于单一状态树和不可变状态......
  • 前端面试题(八)
    39.现代前端框架当前流行的前端框架有哪些?React:由Facebook开发的一个用于构建用户界面的JavaScript库,采用组件化开发,支持虚拟DOM和单向数据流。主要特性:组件复用:将UI分割成独立的、可复用的组件。ReactHooks:允许在函数组件中使用状态和生命周期方法。......