以梦为马,不负韶华

搜索
查看: 172|回复: 12
收起左侧

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

[复制链接]
 楼主| 发表于 2024-5-29 10:30:22 显示全部楼层 |阅读模式
本帖最后由 lihao2014 于 2024-5-29 10:28 编辑

前言

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

设置自定义实体

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

1. 定义自定义实体类继承McDbCustomEntity 类,代码如下:
  1.    class McDbTestLineCustomEntity extends McDbCustomEntity {
  2.      private pt1: McGePoint3d = new McGePoint3d();
  3.      private pt2: McGePoint3d = new McGePoint3d();
  4.      constructor(imp?: any) {
  5.        super(imp);
  6.      }
  7.      public create(imp: any) {
  8.        return new McDbTestLineCustomEntity(imp)
  9.      }
  10.      public getTypeName(): string {
  11.        return "McDbTestLineCustomEntity";
  12.      }
  13.    }
复制代码

2. 通过重写 dwgInFields()函数读取自定义实体数据,dwgOutFields()函数写入自定义实体数据(在从文件读取实体或把实体写入文件时,复制实体等地方都会调用这两个函数),代码如下:
  1.     public dwgInFields(filter: IMcDbDwgFiler): boolean {
  2.        this.pt1 = filter.readPoint("pt1").val;
  3.        this.pt2 = filter.readPoint("pt2").val;
  4.        return true;
  5.      }
  6.      public dwgOutFields(filter: IMcDbDwgFiler): boolean {
  7.        filter.writePoint("pt1", this.pt1);
  8.        filter.writePoint("pt2", this.pt2);
  9.        return true;
  10.      }
复制代码

3. getGripPoints()方法是在点击这个渲染好的图形时提供一个操作点位,即返回自定义的编辑夹点,并在点击操作点移动的回调函数moveGripPointsAt()中处理夹点编辑结果,参考代码如下:
  1.   public moveGripPointsAt(iIndex: number, dXOffset: number, dYOffset: number, dZOffset: number) {
  2.        this.assertWrite();
  3.        if (iIndex == 0) {
  4.          this.pt1.x += dXOffset;
  5.          this.pt1.y += dYOffset;
  6.          this.pt1.z += dZOffset;
  7.        }
  8.        else if (iIndex == 1) {
  9.          this.pt2.x += dXOffset;
  10.          this.pt2.y += dYOffset;
  11.          this.pt2.z += dZOffset;
  12.        }
  13.      };
  14.      public getGripPoints(): McGePoint3dArray {
  15.        let ret = new McGePoint3dArray()
  16.        ret.append(this.pt1);
  17.        ret.append(this.pt2);
  18.        return ret;
  19.      };
复制代码

4. 每次触发动态绘制worldDraw,就会将原本的实例对象删掉(同时也会删除渲染的three.js物体对象),通过create方法重新创建实例,参考代码如下:
  1.      public worldDraw(draw: MxCADWorldDraw): void {
  2.        let tmpline = new McDbLine(this.pt1, this.pt2);
  3.        draw.drawEntity(tmpline);
  4.      }
复制代码

5. 在程序启动的时候,调用rxInit函数,自定义实体的类型信息注册到系统中,参考代码如下:
  1.     MxFun.on("mxcadApplicationCreatedMxCADObject", (param) => {
  2.         //McDbTestLineCustomEntity 自定义实体
  3.         new McDbTestLineCustomEntity().rxInit();
  4.     })
复制代码

完整代码如下:
  1. import { IMcDbDwgFiler, McDbCustomEntity, McDbLine, McGePoint3d, McGePoint3dArray, MxCADUiPrPoint, MxCADWorldDraw, MxCpp } from "mxcad";
  2. export class McDbTestLineCustomEntity extends McDbCustomEntity {
  3.     private pt1: McGePoint3d = new McGePoint3d();
  4.     private pt2: McGePoint3d = new McGePoint3d();
  5.     constructor(imp?: any) {
  6.         super(imp);
  7.     }
  8.     public create(imp: any) {
  9.         return new McDbTestLineCustomEntity(imp)
  10.     }
  11.     public getTypeName(): string {
  12.         return "McDbTestLineCustomEntity";
  13.     }
  14.     public dwgInFields(filter: IMcDbDwgFiler): boolean {
  15.         this.pt1 = filter.readPoint("pt1").val;
  16.         this.pt2 = filter.readPoint("pt2").val;
  17.         return true;
  18.     }
  19.     public dwgOutFields(filter: IMcDbDwgFiler): boolean {
  20.         filter.writePoint("pt1", this.pt1);
  21.         filter.writePoint("pt2", this.pt2);
  22.         return true;
  23.     }
  24.     public moveGripPointsAt(iIndex: number, dXOffset: number, dYOffset: number, dZOffset: number) {
  25.         this.assertWrite();
  26.         if (iIndex == 0) {
  27.             this.pt1.x += dXOffset;
  28.             this.pt1.y += dYOffset;
  29.             this.pt1.z += dZOffset;
  30.         }
  31.         else if (iIndex == 1) {
  32.             this.pt2.x += dXOffset;
  33.             this.pt2.y += dYOffset;
  34.             this.pt2.z += dZOffset;
  35.         }
  36.     };
  37.     public getGripPoints(): McGePoint3dArray {
  38.         let ret = new McGePoint3dArray()
  39.         ret.append(this.pt1);
  40.         ret.append(this.pt2);
  41.         return ret;
  42.     };
  43.     public worldDraw(draw: MxCADWorldDraw): void {
  44.         let tmpline = new McDbLine(this.pt1, this.pt2);
  45.         draw.drawEntity(tmpline);
  46.     }
  47.     public setPoint1(pt1: McGePoint3d) {
  48.         this.assertWrite();
  49.         this.pt1 = pt1.clone();
  50.     }
  51.     public setPoint2(pt2: McGePoint3d) {
  52.         this.assertWrite();
  53.         this.pt2 = pt2.clone();
  54.     }
  55.     public getPoint1() {
  56.         return this.pt1;
  57.     }
  58.     public getPoint2() {
  59.         return this.pt2;
  60.     }
  61. }
  62. export async function MxTest_DrawCustomEntity() {
  63.     let mxcad = MxCpp.getCurrentMxCAD();
  64.     const getPoint = new MxCADUiPrPoint();
  65.     getPoint.setMessage("\n指定一点:");
  66.     let pt1 = (await getPoint.go());
  67.     if (!pt1) return;
  68.     getPoint.setBasePt(pt1);
  69.     getPoint.setUseBasePt(true);
  70.     getPoint.setMessage("\n指定二点:");
  71.     let pt2 = (await getPoint.go());
  72.     if (!pt2) return;
  73.     let myline = new McDbTestLineCustomEntity();
  74.     myline.setPoint1(pt1);
  75.     myline.setPoint2(pt2);
  76.     mxcad.drawEntity(myline);
  77. }
复制代码

实际演练

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

首先我们自定义一条自带文本且两个端点以圆结束的直线,其中线段自带的文本可自定义设置,默显示认为线段长度,其实现方法如下:

1. 根据上述自定义实体的方法,我们通过继承 McDbCustomEntity 类来初始化我们的自定义实体,代码如下:
  1. // 新创建 McDbLineText 类,继承McDbCustomEntity
  2.    class McDbLineText extends McDbCustomEntity {
  3.        //设置McDbLineText类中的两个直线端点 pt1、pt2
  4.        //以及显示线段长度的文字_text和文字大小_textsize
  5.        private pt1: McGePoint3d = new McGePoint3d();
  6.        private pt2: McGePoint3d = new McGePoint3d();
  7.        private _text: string = "";
  8.        private _textsize: number = 10;
  9.        //构造函数
  10.        constructor(imp?: any) {
  11.            super(imp);
  12.        }
  13.        //创建函数
  14.        public create(imp: any) {
  15.            return new McDbLineText(imp)
  16.        }
  17.        //获取类型
  18.        public getTypeName(): string {
  19.            return "McDbLineText";
  20.        }
  21.        //设置或获取文本值
  22.        public set text(val: string) {
  23.            this._text = val;
  24.        }
  25.        public get text(): string {
  26.            return this._text;
  27.        }
  28.        //设置或获取文本大小
  29.        public set textsize(val: number) {
  30.            this._textsize = val;
  31.        }
  32.        public get textsize(): number {
  33.            return this._textsize;
  34.        }
  35.        //读取自定义实体数据pt1、pt2、_text、_textsize
  36.        public dwgInFields(filter: IMcDbDwgFiler): boolean {
  37.            this.pt1 = filter.readPoint("pt1").val;
  38.            this.pt2 = filter.readPoint("pt2").val;
  39.            this._text = filter.readString("text").val;
  40.            this._textsize = filter.readDouble("textsize").val;
  41.            return true;
  42.        }
  43.        //写入自定义实体数据pt1、pt2、_text、_textsize
  44.        public dwgOutFields(filter: IMcDbDwgFiler): boolean {
  45.            filter.writePoint("pt1", this.pt1);
  46.            filter.writePoint("pt2", this.pt2);
  47.            filter.writeString("text", this._text);
  48.            filter.writeDouble("textsize", this._textsize);
  49.            return true;
  50.        }
  51.        //自定义同步函数,当其他对象与该对象相连时同步数据
  52.        private fineLink(pt: McGePoint3d): any {
  53.            let ret: any = {};
  54.            let myId = this.getObjectID();
  55.            let dSearch = this._textsize * 0.5;;
  56.            let filter = new MxCADResbuf();
  57.            filter.AddString("McDbCustomEntity", 5020);
  58.            let ss = new MxCADSelectionSet();
  59.            ss.crossingSelect(pt.x - dSearch, pt.y - dSearch, pt.x + dSearch, pt.y + dSearch, filter);
  60.            ss.forEach((id) => {
  61.                if (id == myId)
  62.                    return;
  63.                let ent = id.getMcDbEntity();
  64.                if (!ent) return;
  65.                if (ent instanceof McDbLineText) {
  66.                    let line = (ent as McDbLineText);
  67.                    let linkPoint = line.getPoint1();
  68.                    let link_pos = 0;
  69.                    // 得到直线与图块连接的端点坐标.
  70.                    let dist = line.getPoint1().distanceTo(pt);
  71.                    if (dist > line.getPoint2().distanceTo(pt)) {
  72.                        dist = line.getPoint2().distanceTo(pt);
  73.                        linkPoint = line.getPoint2();
  74.                        link_pos = 1;
  75.                    }
  76.                    if (dist < dSearch) {
  77.                        ret[id.id] = { link_point: linkPoint,link_pos:link_pos };
  78.                    }
  79.                }
  80.            });
  81.            return ret;
  82.        }
  83.        //处理夹点编辑效果
  84.        public moveGripPointsAt(iIndex: number, dXOffset: number, dYOffset: number, dZOffset: number) {
  85.            this.assertWrite();
  86.            let pt:McGePoint3d = this.pt1.clone();
  87.            let new_pt:McGePoint3d = pt;
  88.            if (iIndex == 0) {
  89.                this.pt1.x += dXOffset;
  90.                this.pt1.y += dYOffset;
  91.                this.pt1.z += dZOffset;
  92.                new_pt = this.pt1;
  93.            }
  94.            else if (iIndex == 1) {
  95.                pt = this.pt2.clone();
  96.                this.pt2.x += dXOffset;
  97.                this.pt2.y += dYOffset;
  98.                this.pt2.z += dZOffset;
  99.                new_pt = this.pt2;
  100.            }
  101.            if (this.getObjectID().isValid()) {
  102.                // 同步,与连接的其它对象。
  103.                let linkobj = this.fineLink(pt)
  104.                Object.keys(linkobj).forEach((id_val:any)=>{
  105.                    let idFind = new McObjectId(id_val);
  106.                    let lineFind = (idFind.getMcDbEntity() as McDbLineText);
  107.                    if(linkobj[id_val].link_pos == 0){
  108.                        lineFind.setPoint1(new_pt);
  109.                    }
  110.                    else{
  111.                        lineFind.setPoint2(new_pt);
  112.                    }
  113.                  });
  114.            }
  115.        };
  116.        //设置对象编辑点位
  117.        public getGripPoints(): McGePoint3dArray {
  118.            let ret = new McGePoint3dArray()
  119.            ret.append(this.pt1);
  120.            ret.append(this.pt2);
  121.            return ret;
  122.        };
  123.        //动态绘制
  124.        public worldDraw(draw: MxCADWorldDraw): void {
  125.            let circle_r = this._textsize * 0.4;
  126.            let vec2 = this.pt2.sub(this.pt1);
  127.            vec2.normalize().mult(circle_r);
  128.            let tmpline = new McDbLine(this.pt1.clone().addvec(vec2),
  129.                                       this.pt2.clone().subvec(vec2));
  130.            draw.drawEntity(tmpline);
  131.            let vec = this.pt2.sub(this.pt1).mult(0.5);
  132.            let midpt = this.pt1.clone().addvec(vec);
  133.            if (vec.dotProduct(McGeVector3d.kXAxis) < 0) {
  134.                vec.negate();
  135.            }
  136.            let ange = vec.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
  137.            let str = this._text;
  138.            if (str.length == 0) {
  139.                str = this.pt1.distanceTo(this.pt2).toFixed(2);
  140.            }
  141.            vec.perpVector();
  142.            if (vec.dotProduct(McGeVector3d.kYAxis) < 0) {
  143.                vec.negate();
  144.            }
  145.            vec.normalize().mult(this._textsize * 0.2);
  146.            let text = new McDbText();
  147.            text.textString = str;
  148.            text.position = midpt.clone().addvec(vec);
  149.            text.alignmentPoint = midpt.clone().addvec(vec);
  150.            text.rotation = ange;
  151.            text.verticalMode = McDb.TextVertMode.kTextBottom;
  152.            text.horizontalMode = McDb.TextHorzMode.kTextCenter;
  153.            text.height = this._textsize;
  154.            draw.drawEntity(text)
  155.            let circle1 = new McDbCircle();
  156.            circle1.center = this.pt1;
  157.            circle1.radius = circle_r;
  158.            draw.drawEntity(circle1);
  159.    
  160.            let circle2= new McDbCircle();
  161.            circle2.center = this.pt2;
  162.            circle2.radius = circle_r;
  163.            draw.drawEntity(circle2);
  164.        }
  165.        // 设置pt1
  166.        public setPoint1(pt1: McGePoint3d) {
  167.            this.assertWrite();
  168.            this.pt1 = pt1.clone();
  169.        }
  170.        // 设置pt2
  171.        public setPoint2(pt2: McGePoint3d) {
  172.            this.assertWrite();
  173.            this.pt2 = pt2.clone();
  174.        }
  175.        // 获取pt1
  176.        public getPoint1() {
  177.            return this.pt1;
  178.        }
  179.        //获取pt2
  180.        public getPoint2() {
  181.            return this.pt2;
  182.        }
  183.    }
复制代码

2. 调用上述实现的自定义类 McDbLineText ,实现绘制函数。

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

  1.   //基础绘制函数

  2.    function Mx_baseLineText(){

  3.      let mxcad = MxCpp.getCurrentMxCAD();

  4.      const getPoint = new MxCADUiPrPoint();

  5.      getPoint.setMessage("\n指定一点:");

  6.      let pt1 = (await getPoint.go());

  7.      if (!pt1) return;  

  8.      getPoint.setBasePt(pt1);

  9.      getPoint.setUseBasePt(true);

  10.      getPoint.setMessage("\n指定二点:");

  11.      let pt2 = (await getPoint.go());

  12.      if (!pt2) return;

  13.      let myline = new McDbLineText();

  14.      new McDbLineText().rxInit();

  15.      myline.setPoint1(pt1);

  16.      myline.setPoint2(pt2);

  17.      myline.textsize = mxcad.mxdraw.screenCoordLong2Doc(10);

  18.      mxcad.drawEntity(myline);

  19.    }
复制代码

其实现效果如下:
图片3.png


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

  2.        let mxcad = MxCpp.getCurrentMxCAD();

  3.        //设置联动端点

  4.        let pt1 = new McGePoint3d(100,100,0);

  5.        let pt2 = new McGePoint3d(200,150,0);

  6.        let pt3 = new McGePoint3d(400,50,0);

  7.        let pt4 = new McGePoint3d(600,60,0);

  8.        let pt5 = new McGePoint3d(200,300,0);

  9.        let textsize = 5;

  10.        //第一条线

  11.        let myline1 = new McDbLineText();

  12.        myline1.setPoint1(pt1);

  13.        myline1.setPoint2(pt2);

  14.        myline1.textsize = textsize;

  15.        myline1.text = "自定义文本";

  16.        mxcad.drawEntity(myline1);

  17.        //第二条线

  18.        let myline2 = new McDbLineText();

  19.        myline2.setPoint1(pt2);

  20.        myline2.setPoint2(pt3);

  21.        myline2.textsize = textsize;

  22.        mxcad.drawEntity(myline2);

  23.        //第三条线

  24.        let myline3 = new McDbLineText();

  25.        myline3.setPoint1(pt3);

  26.        myline3.setPoint2(pt4);

  27.        myline3.textsize = textsize;

  28.        mxcad.drawEntity(myline3);

  29.        //第四条线

  30.        let myline4 = new McDbLineText();

  31.        myline4.setPoint1(pt2);

  32.        myline4.setPoint2(pt5);

  33.        myline4.textsize = textsize;

  34.        mxcad.drawEntity(myline4);  

  35.        //把所有的实体都放到当前显示视区

  36.        mxcad.zoomW(new McGePoint3d(-300,-300,0),new McGePoint3d(650,500,0));

  37.        //更新视区显示

  38.        mxcad.updateDisplay();

  39.    }
复制代码

其实现效果如下:
图片4.png

DEMO源码下载地址


https://gitee.com/mxcadx/mxdraw-article/blob/master/%E7%BD%91%E9%A1%B5CAD%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%9E%E4%BD%93/demo.7z





[发帖际遇]: lihao2014 乐于助人,帮助不愿意过马路的老奶奶过马路,奖励 2 个 韶华币. 幸运榜 / 衰神榜
发表于 2024-5-29 13:14:14 显示全部楼层

感谢楼主的无私分享!要想马后炮化工好 就靠你我他
[发帖际遇]: FC小虾米 成功获得红包 1 个 韶华币. 幸运榜 / 衰神榜
回复 支持 反对

使用道具 举报

发表于 2024-5-29 13:52:47 显示全部楼层
谢谢楼主分享
回复 支持 反对

使用道具 举报

 成长值: 5950

发表于 2024-5-29 13:56:50 显示全部楼层

感谢楼主的无私分享!要想马后炮化工好 就靠你我他
[发帖际遇]: ccwd2008 捡到一块切糕,卖给了小马,赚了 4 个 韶华币. 幸运榜 / 衰神榜
回复 支持 反对

使用道具 举报

发表于 2024-5-30 05:52:26 显示全部楼层

这个帖子不回对不起自己!我想我是一天也不能离开马后炮化工
[发帖际遇]: llzhao 乐于助人,帮助不愿意过马路的老奶奶过马路,奖励 2 个 韶华币. 幸运榜 / 衰神榜
回复 支持 反对

使用道具 举报

发表于 2024-5-30 08:04:42 显示全部楼层
楼主太厉害了!楼主,I*老*虎*U!我觉得马后炮化工真是个好地方!
回复 支持 反对

使用道具 举报

发表于 2024-5-30 08:17:59 显示全部楼层
谢谢楼主分享,一天离不开马后炮
[发帖际遇]: gcq889395 成功获得红包 9 个 韶华币. 幸运榜 / 衰神榜
回复 支持 反对

使用道具 举报

不想打字就选择快捷回复吧
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|以梦为马,不负韶华

GMT+8, 2025-4-5 07:58

Powered by 以梦为马,不负韶华

© 2024-2099 Meng.Horse

快速回复 返回顶部 返回列表