在线CAD(云CAD)实现自定义实体的详细方法 HarmonyOS 鸿蒙Next

在线CAD(云CAD)实现自定义实体的详细方法 HarmonyOS 鸿蒙Next

前言

自定义实体在CAD二次开发中使用的频率较高,本章节主要阐述网页CAD中使用自定义实体的方法,mxcad可以根据用户的具体需求来创建和管理自定义实体,可以通过从自定义实体类 McDbCustomEntity() 中继承实体的名称、属性、方法,也可结合自身需求对自定义实体类中的属性或方法进行重写。

设置自定义实体

下面以自定义直线为例来介绍如何使用自定义实体,效果如下:

  1. 定义自定义实体类继承 McDbCustomEntity 类,代码如下:

    class McDbTestLineCustomEntity extends McDbCustomEntity {
        private pt1: McGePoint3d = new McGePoint3d();
        private pt2: McGePoint3d = new McGePoint3d();
        
        constructor(imp?: any) {
            super(imp);
        }
    
        public create(imp: any) {
            return new McDbTestLineCustomEntity(imp)
        }
    
        public getTypeName(): string {
            return "McDbTestLineCustomEntity";
        }
    }
    
  2. 通过重写 dwgInFields() 函数读取自定义实体数据,dwgOutFields() 函数写入自定义实体数据(在从文件读取实体或把实体写入文件时,复制实体等地方都会调用这两个函数),代码如下:

    public dwgInFields(filter: IMcDbDwgFiler): boolean {
        this.pt1 = filter.readPoint("pt1").val;
        this.pt2 = filter.readPoint("pt2").val;
        return true;
    }
    
    public dwgOutFields(filter: IMcDbDwgFiler): boolean {
        filter.writePoint("pt1", this.pt1);
        filter.writePoint("pt2", this.pt2);
        return true;
    }
    
  3. getGripPoints() 方法是在点击这个渲染好的图形时提供一个操作点位,即返回自定义的编辑夹点,并在点击操作点移动的回调函数 moveGripPointsAt() 中处理夹点编辑结果,参考代码如下:

    public moveGripPointsAt(iIndex: number, dXOffset: number, dYOffset: number, dZOffset: number) {
        this.assertWrite();
        if (iIndex == 0) {
            this.pt1.x += dXOffset;
            this.pt1.y += dYOffset;
            this.pt1.z += dZOffset;
        } else if (iIndex == 1) {
            this.pt2.x += dXOffset;
            this.pt2.y += dYOffset;
            this.pt2.z += dZOffset;
        }
    }
    
    public getGripPoints(): McGePoint3dArray {
        let ret = new McGePoint3dArray()
        ret.append(this.pt1);
        ret.append(this.pt2);
        return ret;
    }
    
  4. 每次触发动态绘制 worldDraw,就会将原本的实例对象删掉(同时也会删除渲染的three.js物体对象),通过create方法重新创建实例,参考代码如下:

    public worldDraw(draw: MxCADWorldDraw): void {
        let tmpline = new McDbLine(this.pt1, this.pt2);
        draw.drawEntity(tmpline);
    }
    
  5. 在程序启动的时候,调用 rxInit 函数,自定义实体的类型信息注册到系统中,参考代码如下:

    MxFun.on("mxcadApplicationCreatedMxCADObject", (param) => {
        //McDbTestLineCustomEntity 自定义实体
        new McDbTestLineCustomEntity().rxInit();
    })
    

完整代码如下:

import { IMcDbDwgFiler, McDbCustomEntity, McDbLine, McGePoint3d, McGePoint3dArray, MxCADUiPrPoint, MxCADWorldDraw, MxCpp } from "mxcad";
export class McDbTestLineCustomEntity extends McDbCustomEntity {
    private pt1: McGePoint3d = new McGePoint3d();
    private pt2: McGePoint3d = new McGePoint3d();

    constructor(imp?: any) {
        super(imp);
    }

    public create(imp: any) {
        return new McDbTestLineCustomEntity(imp)
    }

    public getTypeName(): string {
        return "McDbTestLineCustomEntity";
    }

    public dwgInFields(filter: IMcDbDwgFiler): boolean {
        this.pt1 = filter.readPoint("pt1").val;
        this.pt2 = filter.readPoint("pt2").val;
        return true;
    }

    public dwgOutFields(filter: IMcDbDwgFiler): boolean {
        filter.writePoint("pt1", this.pt1);
        filter.writePoint("pt2", this.pt2);
        return true;
    }

    public moveGripPointsAt(iIndex: number, dXOffset: number, dYOffset: number, dZOffset: number) {
        this.assertWrite();
        if (iIndex == 0) {
            this.pt1.x += dXOffset;
            this.pt1.y += dYOffset;
            this.pt1.z += dZOffset;
        } else if (iIndex == 1) {
            this.pt2.x += dXOffset;
            this.pt2.y += dYOffset;
            this.pt2.z += dZOffset;
        }
    };

    public getGripPoints(): McGePoint3dArray {
        let ret = new McGePoint3dArray()
        ret.append(this.pt1);
        ret.append(this.pt2);
        return ret;
    };

    public worldDraw(draw: MxCADWorldDraw): void {
        let tmpline = new McDbLine(this.pt1, this.pt2);
        draw.drawEntity(tmpline);
    }

    public setPoint1(pt1: McGePoint3d) {
        this.assertWrite();
        this.pt1 = pt1.clone();
    }

    public setPoint2(pt2: McGePoint3d) {
        this.assertWrite();
        this.pt2 = pt2.clone();
    }

    public getPoint1() {
        return this.pt1;
    }

    public getPoint2() {
        return this.pt2;
    }
}

export async function MxTest_DrawCustomEntity() {
    let mxcad = MxCpp.getCurrentMxCAD();
    const getPoint = new MxCADUiPrPoint();
    getPoint.setMessage("\n指定一点:");
    let pt1 = (await getPoint.go());
    if (!pt1) return;
    getPoint.setBasePt(pt1);
    getPoint.setUseBasePt(true);
    getPoint.setMessage("\n指定二点:");
    let pt2 = (await getPoint.go());
    if (!pt2) return;
    let myline = new McDbTestLineCustomEntity();
    myline.setPoint1(pt1);
    myline.setPoint2(pt2);
    mxcad.drawEntity(myline);
}

实际演练

上面的代码是最简单的画直线的操作,更复杂点的自定义实体例子,可以打开在线DEMO查看,如下图:

  1. 根据上述自定义实体的方法,我们通过继承 McDbCustomEntity 类来初始化我们的自定义实体,代码如下:

    // 新创建 McDbLineText 类,继承McDbCustomEntity
    class McDbLineText extends McDbCustomEntity {
        private pt1: McGePoint3d = new McGePoint3d();
        private pt2: McGePoint3d = new McGePoint3d();
        private _text: string = "";
        private _textsize: number = 10;
    
        // 构造函数
        constructor(imp?: any) {
            super(imp);
        }
    
        // 创建函数
        public create(imp: any) {
            return new McDbLineText(imp)
        }
    
        // 获取类型
        public getTypeName(): string {
            return "McDbLineText";
        }
    
        // 设置或获取文本值
        public set text(val: string) {
            this._text = val;
        }
    
        public get text(): string {
            return this._text;
        }
    
        // 设置或获取文本大小
        public set textsize(val: number) {
            this._textsize = val;
        }
    
        public get textsize(): number {
            return this._textsize;
        }
    
        // 读取自定义实体数据pt1、pt2、_text、_textsize
        public dwgInFields(filter: IMcDbDwgFiler): boolean {
            this.pt1 = filter.readPoint("pt1").val;
            this.pt2 = filter.readPoint("pt2").val;
            this._text = filter.readString("text").val;
            this._textsize = filter.readDouble("textsize").val;
            return true;
        }
    
        // 写入自定义实体数据pt1、pt2、_text、_textsize
        public dwgOutFields(filter: IMcDbDwgFiler): boolean {
            filter.writePoint("pt1", this.pt1);
            filter.writePoint("pt2", this.pt2);
            filter.writeString("text", this._text);
            filter.writeDouble("textsize", this._textsize);
            return true;
        }
    
        // 自定义同步函数,当其他对象与该对象相连时同步数据
        private fineLink(pt: McGePoint3d): any {
            let ret: any = {};
            let myId = this.getObjectID();
            let dSearch = this._textsize * 0.5;
            let filter = new MxCADResbuf();
            filter.AddString("McDbCustomEntity", 5020);
            let ss = new MxCADSelectionSet();
            ss.crossingSelect(pt.x - dSearch, pt.y - dSearch, pt.x + dSearch, pt.y + dSearch, filter);
            ss.forEach((id) => {
                if (id == myId)
                    return;
                let ent = id.getMcDbEntity();
                if (!ent) return;
                if (ent instanceof McDbLineText) {
                    let line = (ent as McDbLineText);
                    let linkPoint = line.getPoint1();
                    let link_pos = 0;
                    let dist = line.getPoint1().distanceTo(pt);
                    if (dist > line.getPoint2().distanceTo(pt)) {
                        dist = line.getPoint2().distanceTo(pt);
                        linkPoint = line.getPoint2();
                        link_pos = 1;
                    }
                    if (dist < dSearch) {
                        ret[id.id] = { link_point: linkPoint, link_pos: link_pos };
                    }
                }
            });
            return ret;
        }
    
        // 处理夹点编辑效果
        public moveGripPointsAt(iIndex: number, dXOffset: number, dYOffset: number, dZOffset: number) {
            this.assertWrite();
            let pt: McGePoint3d = this.pt1.clone();
            let new_pt: McGePoint3d = pt;
            if (iIndex == 0) {
                this.pt1.x += dXOffset;
                this.pt1.y += dYOffset;
                this.pt1.z += dZOffset;
                new_pt = this.pt1;
            } else if (iIndex == 1) {
                pt = this.pt2.clone();
                this.pt2.x += dXOffset;
                this.pt2.y += dYOffset;
                this.pt2.z += dZOffset;
                new_pt = this.pt2;
            }
            if (this.getObjectID().isValid()) {
                let linkobj = this.fineLink(pt)
                Object.keys(linkobj).forEach((id_val: any) => {
                    let idFind = new McObjectId(id_val);
                    let lineFind = (idFind.getMcDbEntity() as McDbLineText);
                    if (linkobj[id_val].link_pos == 0) {
                        lineFind.setPoint1(new_pt);
                    } else {
                        lineFind.setPoint2(new_pt);
                    }
                });
            }
        };
    
        // 设置对象编辑点位
        public getGripPoints(): McGePoint3dArray {
            let ret = new McGePoint3dArray()
            ret.append(this.pt1);
            ret.append(this.pt2);
            return ret;
        };
    
        // 动态绘制
        public worldDraw(draw: MxCADWorldDraw): void {
            let circle_r = this._textsize * 0.4;
            let vec2 = this.pt2.sub(this.pt1);
            vec2.normalize().mult(circle_r);
            let tmpline = new McDbLine(this.pt1.clone().addvec(vec2), this.pt2.clone().subvec(vec2));
            draw.drawEntity(tmpline);
            let vec = this.pt2.sub(this.pt1).mult(0.5);
            let midpt = this.pt1.clone().addvec(vec);
            if (vec.dotProduct(McGeVector3d.kXAxis) < 0) {
                vec.negate();
            }
            let ange = vec.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
            let str = this._text;
            if (str.length == 0) {
                str = this.pt1.distanceTo(this.pt2).toFixed(2);
            }
            vec.perpVector();
            if (vec.dotProduct(McGeVector3d.kYAxis) < 0) {
                vec.negate();
            }
            vec.normalize().mult(this._textsize * 0.2);
            let text = new McDbText();
            text.textString = str;
            text.position = midpt.clone().addvec(vec);
            text.alignmentPoint = midpt.clone().addvec(vec);
            text.rotation = ange;
            text.verticalMode = McDb.TextVertMode.kTextBottom;
            text.horizontalMode = McDb.TextHorzMode.kTextCenter;
            text.height = this._textsize;
            draw.drawEntity(text)
            let circle1 = new McDbCircle();
            circle1.center = this.pt1;
            circle1.radius = circle_r;
            draw.drawEntity(circle1);
            let circle2 = new McDbCircle();
            circle2.center = this.pt2;
            circle2.radius = circle_r;
            draw.drawEntity(circle2);
        };
    
        // 设置pt1
        public setPoint1(pt1: McGePoint3d) {
            this.assertWrite();
            this.pt1 = pt1.clone();
        }
    
        // 设置pt2
        public setPoint2(pt2: McGePoint3d) {
            this.assertWrite();
            this.pt2 = pt2.clone();
        }
    
        // 获取pt1
        public getPoint1() {
            return this.pt1;
        }
    
        // 获取pt2
        public getPoint2() {
            return this.pt2;
        }
    }
    
  2. 调用上述实现的自定义类 McDbLineText,实现绘制函数。

2.1 基础绘制:用户自定义直线两端点,代码如下:

```javascript
// 基础绘制函数
function Mx_baseLineText(){
    let mxcad = MxCpp.getCurrentMxCAD();
    const getPoint = new MxCADUiPrPoint();
    getPoint.setMessage("\n指定一点:");
    let pt1 = (await getPoint.go());
    if (!pt1) return; 
    getPoint.setBasePt(pt1);
    getPoint.setUseBasePt(true); 
    getPoint.setMessage("\n指定二点:");
    let pt2 = (await getPoint.go());
    if (!pt2) return;
    let myline = new McDbLineText();
    new McDbLineText().rxInit();
    myline.setPoint1(pt1);
    myline.setPoint2(pt2);
    myline.textsize = mxcad.mxdraw.screenCoordLong2Doc(10);
    mxcad.drawEntity(myline);
}
```

其实现效果如下:

2.2 端点联动:设置多条线段相连为一个整体,节点之间相互关联,代码如下:

```javascript
function Mx_testLineText(){
    let mxcad = MxCpp.getCurrentMxCAD();
    // 设置联动端点
    let pt1 = new McGePoint3d(100,100,0);
    let pt2 = new McGePoint3d(200,150,0);
    let pt3 = new McGePoint3d(400,50,0);
    let pt4 = new McGePoint3d(600,60,0);
    let pt5 = new McGePoint3d(200,300,0);
    let textsize = 5;
    // 第一条线
    let myline1 = new McDbLineText();
    myline1.setPoint1(pt1);
    myline1.setPoint2(pt2);
    myline1.textsize = textsize;
    myline1.text = "自定义文本";
    mxcad.drawEntity(myline1);
    // 第二条线
    let myline2 = new McDbLineText();
    myline2.setPoint1(pt2);
    myline2.setPoint2(pt3);
    myline2.textsize = textsize;
    mxcad.drawEntity(myline2);
    // 第三条线
    let myline3 = new McDbLineText();
    myline3.setPoint1(pt3);
    myline3.setPoint2(pt4);
    myline3.textsize = textsize;
    mxcad.drawEntity(myline3); 
    // 第四条线
    let myline4 = new McDbLineText();
    myline4.setPoint1(pt2);
    myline4.setPoint2(pt5);
    myline4.textsize = textsize;
    mxcad.drawEntity(myline4); 
    // 把所有的实体都放到当前显示视区
    mxcad.zoomW(new McGePoint3d(-300,-300,0),new McGePoint3d(650,500,0));
    // 更新视区显示
    mxcad.updateDisplay();
}
```

更多关于在线CAD(云CAD)实现自定义实体的详细方法 HarmonyOS 鸿蒙Next的实战教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

在鸿蒙Next中实现在线CAD(云CAD)自定义实体,可以通过以下步骤进行:

  1. 定义实体类:首先,创建一个继承自ohos.agp.components.Component的类,作为自定义实体的基类。该类需要包含实体的属性和方法,如位置、大小、颜色等。

  2. 实现绘制逻辑:在自定义实体类中,重写onDraw方法,使用ohos.agp.render.Canvas进行绘制。可以通过Canvas提供的API绘制线条、矩形、圆形等基本图形,或使用Path绘制复杂图形。

  3. 处理用户交互:重写onTouchEvent方法,处理用户的触摸事件,如点击、拖动等。根据用户的操作,更新实体的位置或属性,并调用invalidate方法触发重绘。

  4. 序列化与反序列化:为了支持云存储和加载,实现实体的序列化和反序列化。可以使用ohos.utils.Parcelohos.utils.XmlSerializer将实体数据转换为字节流或XML格式,反之亦然。

  5. 集成到云CAD系统:将自定义实体类集成到云CAD系统中,确保其能够与其他实体协同工作。可以通过注册实体类型、管理实体生命周期等方式实现。

  6. 测试与优化:在鸿蒙设备上测试自定义实体的功能,确保其在不同分辨率和设备上表现一致。根据测试结果进行优化,如调整绘制性能、处理边界情况等。

通过以上步骤,可以在鸿蒙Next中实现在线CAD(云CAD)的自定义实体功能。

更多关于在线CAD(云CAD)实现自定义实体的详细方法 HarmonyOS 鸿蒙Next的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中实现在线CAD(云CAD)自定义实体的方法如下:

  1. 定义实体类:创建一个继承自CADEntity的类,包含实体的属性和方法。
  2. 实现序列化:重写toJSONfromJSON方法,确保实体能在网络中传输。
  3. 注册自定义实体:使用CAD.registerEntity方法将实体注册到CAD系统中,确保系统能识别和渲染。
  4. 绘制实体:在draw方法中实现实体的绘制逻辑,使用Canvas API进行渲染。
  5. 事件处理:重写onClickonHover等方法,处理用户交互事件。

通过这些步骤,您可以在HarmonyOS鸿蒙Next中实现自定义CAD实体,并确保其在云环境中正常运行。

回到顶部