在线CAD中实现测量圆、测量面积的方法(纯国产CAD)
一、前言
本章将介绍如何利用mxcad插件实现在CAD图纸中测量圆和测量面积的功能,用户点击目标圆对象将自动标记出这个圆的半径、面积值和周长值,同时可以自定义选择标注文字的位置,测量圆功能能够快速掌握目标圆对象的数据信息,方便统计工程量。
测量面积功能(多边形、矩形)以多边形的每一个顶点来确定多边形的形状和位置,通过点击矩形的量角点来确定矩形的大小和位置,也可以自定义标注文字的位置。
二、测量圆的功能实现
2.1.实现自定义圆标注类
为了方便后期管理与修改标注,可以通过继承McDbCustomEntity自定义实体类来扩展实现圆标注类。其中在 mxcad 中圆对象对应的实体类为McDbCircle ,该类提供了获取或设置圆相关信息的属性或方法,我们可以根据功能需求去选择调用。
在测量圆功能中需要获取圆对象的半径、面积与周长,因此可以调用McDbCircle.radius 属性获取圆半径,McDbCircle.getArea()方法或直接计算来获取圆面积,以及McDbCircle.getLength()方法获取圆周长。然后利用 McDbMText 构造测量信息多文本对象,将圆的标注信息绘制在页面中,参考代码如下:
// 自定义圆标注类 class McDbTestMeasuringCircle extends McDbCustomEntity { // 定义McDbTestMeasuringCircle内部的点对象 // 圆心 private center: McGePoint3d = new McGePoint3d(); // 标注点 private position: McGePoint3d = new McGePoint3d(); // 圆半径 private radius: number; // 构造函数 constructor(imp?: any) { super(imp); } // 创建函数 public create(imp: any) { return new McDbTestMeasuringCircle(imp) } // 获取类名 public getTypeName(): string { return "McDbTestMeasuringCircle"; } //设置或获取圆半径 public set circleRadius(val: number) { this.radius = val; } public get circleRadius(): number { return this.radius; } // 读取自定义实体数据center、position、radius public dwgInFields(filter: IMcDbDwgFiler): boolean { this.center = filter.readPoint("center").val; this.position = filter.readPoint("position").val; this.radius = filter.readDouble("radius").val; return true; } // 写入自定义实体数据center、position、radius public dwgOutFields(filter: IMcDbDwgFiler): boolean { filter.writePoint("center", this.center); filter.writePoint("position", this.position); filter.writeDouble("radius", this.radius); return true; } // 移动自定义对象的夹点 public moveGripPointsAt(iIndex: number, dXOffset: number, dYOffset: number, dZOffset: number) { this.assertWrite(); this.position.x += dXOffset; this.position.y += dYOffset; this.position.z += dZOffset; }; // 获取自定义对象的夹点 public getGripPoints(): McGePoint3dArray { let ret = new McGePoint3dArray() ret.append(this.position); return ret; }; // 绘制实体 public worldDraw(draw: MxCADWorldDraw): void { // 绘制标注圆与标注信息 const circle = new McDbCircle(); circle.center = this.center; circle.radius = this.radius; const length = circle.getLength().val; const radius = circle.radius; const area = Math.PI * radius * radius; const mText = new McDbMText(); mText.contents = `半径:${radius.toFixed(2)} \\P 周长:${length.toFixed(2)} \\P 面积:${area.toFixed(2)}` mText.textHeight = radius / 6; mText.attachment = McDb.AttachmentPoint.kMiddleCenter; mText.location = this.position; mText.trueColor = circle.trueColor = this.trueColor; draw.drawEntity(mText); draw.drawEntity(circle); } // 设置pt1 public setCenter(pt: McGePoint3d) { this.assertWrite(); this.center = pt.clone(); } // 获取pt1 public getCenter() { return this.center; } // 获取position public setPosition(pt: McGePoint3d) { this.assertWrite(); this.position = pt.clone(); } // 获取position public getPosition() { return this.position; } }
2.2 注册自定义类信息
运行代码:
new McDbTestMeasuringCircle().rxInit();
2.3 编写方法调用McDbTestMeasuringCircle自定义圆标注类实现测量圆功能
2.3.1 获取目标圆对象,得到相关数据信息
利用选择实体对象MxCADUiPrEntity()根据用户鼠标点击的坐标得到对应的实体,其中需要只选择圆对象,因此,我们再调用MxCADResbuf()为选择实体对象设置过滤器来过滤出目标实体,参考代码如下:
// 选择实体对象 const getEnt = new MxCADUiPrEntity(); // 设置提示信息 getEnt.setMessage("请选择一个圆对象"); // 设置过滤器 const filter = new MxCADResbuf([DxfCode.kEntityType, "CIRCLE"]); getEnt.setFilter(filter); // entId过滤选择后的圆实体对象ID const entId = await getEnt.go(); if (!entId.id) return; // 获取圆相关信息 const circle = entId.getMcDbEntity() as McDbCircle; const mCircle = new McDbTestMeasuringCircle(); mCircle.setCenter(circle.center); mCircle.circleRadius = circle.radius;
2.3.2 指定标注点并绘制圆标注对象
利用MxCADUiPrPoint取点对象在页面中交互取点。在取点过程中通过MxCADUiPrPoint.setUserDraw()方法动态绘制标注对象,使用户更加直观的观察到标注对象的位置变化,参考代码如下:
// 设置取点对象 const getPt = new MxCADUiPrPoint(); // 设置提示信息 getPt.setMessage('请指定文字位置'); // 动态绘制 getPt.setUserDraw((pt, pw) => { pw.setColor(0xFF0000); mCircle.setPosition(pt); pw.drawMcDbEntity(mCircle); }); const point = await getPt.go(); if (!point) return; // 设置标注文本位置 mCircle.setPosition(point); // 设置圆标注对象颜色 mCircle.trueColor = new McCmColor(255, 0, 0) const mxcad = MxCpp.getCurrentMxCAD(); // 绘制圆标注对象 mxcad.drawEntity(mCircle);
效果如下图:
三、测量面积的功能实现
3.1 实现自定义面积标注类
为了方便后期管理与修改标注,可以通过继承McDbCustomEntity自定义实体类来扩展实现面积标注类,在mxcad中多边形与矩形都是通过多段线绘制出来的,多段线对象对应的实体类为McDbPolyline,该类提供了获取或设置图形对象相关信息的属性或方法,根据功能需求去选择调用。
在测量面积(多边形、矩形)功能中,调用McDbPolyline.getLength()方法获取多段线对象的长度、以及在多段线闭合的情况下调用McDbPolyline.getArea()方法获取面积,然后利用McDbMText 构造测量信息多文本对象,将对象的标注信息绘制在页面中,参考代码如下:
// 自定义面积标注类 class McDbTestAreaComment extends McDbCustomEntity { /** 点数组 */ private ptArr: McGePoint3d[] = []; /** 标注点 */ private pt: McGePoint3d; /** 字高 */ private height: number = 50; /** 凸度数组 */ private dbulges: number[] = []; /** 测量对象是否为矩形 */ private isReactFlag: boolean = false; constructor(imp?: any) { super(imp); } public create(imp: any) { return new McDbTestAreaComment(imp) } /** 获取类名 */ public getTypeName(): string { return "McDbTestAreaComment"; } //设置或获取文本字高 public set textHeight(val: number) { this.height = val; } public get textHeight(): number { return this.height; } //设置或获取测量对象是否为矩形 public set isReact(val: boolean) { this.isReactFlag = val; } public get isReact(): boolean { return this.isReactFlag; } /** 读取数据 */ public dwgInFields(filter: IMcDbDwgFiler): boolean { this.ptArr = filter.readPoints('ptArr').val; this.pt = filter.readPoint('pt').val; const _dbulges = filter.readString("dbulges").val; this.dbulges = _dbulges.split(',').map(Number); this.isReactFlag = filter.readLong("isReactFlag").val ? true : false; return true; } /** 写入数据 */ public dwgOutFields(filter: IMcDbDwgFiler): boolean { filter.writePoints("ptArr", this.ptArr); filter.writePoint("pt", this.pt); const _dbulges = this.dbulges.toString(); filter.writeString("dbulges", _dbulges); filter.writeLong("isReactFlag", this.isReactFlag ? 1 : 0); return true; } /** 移动夹点 */ public moveGripPointsAt(iIndex: number, dXOffset: number, dYOffset: number, dZOffset: number) { this.assertWrite(); this.ptArr.forEach((point, index) => { if (index === iIndex) { point.x += dXOffset; point.y += dYOffset; point.z += dZOffset; } }); if (iIndex === this.ptArr.length) { this.pt.x += dXOffset; this.pt.y += dYOffset; this.pt.z += dZOffset; } }; /** 获取夹点 */ public getGripPoints(): McGePoint3dArray { let ret = new McGePoint3dArray(); this.ptArr.forEach(point => { ret.append(point); }) ret.append(this.pt); return ret; }; /** 动态绘制 */ public worldDraw(draw: MxCADWorldDraw): void { const pl = new McDbPolyline(); pl.isClosed = true; if (this.isReactFlag) { // 测量的是矩形,得到的就是矩形的两个角点 pl.addVertexAt(this.ptArr[0], 0, 0.1, 0.1); pl.addVertexAt(new McGePoint3d(this.ptArr[1].x, this.ptArr[0].y), 0, 0.1, 0.1); pl.addVertexAt(this.ptArr[1], 0, 0.1, 0.1); pl.addVertexAt(new McGePoint3d(this.ptArr[0].x, this.ptArr[1].y), 0, 0.1, 0.1); } else { // 测量的是多边形 this.ptArr.forEach((pt, index) => { pl.addVertexAt(pt, this.dbulges[index], 0.1, 0.1); }); } // 获取对象数据信息 const area = pl.getArea().val; const length = pl.getLength().val; // 构造对文本对象 const text = new McDbMText(); text.contents = `面积:${area.toFixed(2)}\\P周长:${length.toFixed(2)} `; text.attachment = McDb.AttachmentPoint.kMiddleCenter; text.location = this.pt; text.textHeight = MxFun.screenCoordLong2Doc(this.height); // 设置标注颜色 text.trueColor = this.trueColor; draw.trueColor = this.trueColor; // 绘制标注对象 draw.drawEntity(text); draw.drawEntity(pl); } /** 设置标注点 */ public setPoint(pt: McGePoint3d) { this.assertWrite(); this.pt = pt.clone(); } /** 增加顶点 */ public addVertex(pt: McGePoint3d, dbulge?: number) { this.assertWrite(); this.ptArr.push(pt.clone()); this.dbulges.push(dbulge ? dbulge : 0); } /** 获取标注点 */ public getPoint() { return this.pt; } }
3.2 注册自定义类信息
运行代码如下:
new McDbTestAreaComment().rxInit();
3.3 编写方法,调用McDbTestAreaComment自定义面积标注类实现测量面积(多边形、矩形)功能
3.3.1 测量多边形面积
测量多边形面积需要连续取点,且取点不少于3个。利用MxCADUiPrPoint取点对象在图纸中循环取点来确定需要测量多边形的位置与大小,并通过MxCADUiPrPoint.setKeyWords()为其设置关键字列表,使用户能够实现自主撤销操作或提前结束操作,方便交互,参考代码如下:
// 测量多边形面积 async function Mx_Area() { // 多边形顶点数值 const ptArr: McGePoint3d[] = []; // 获取多边形第一个顶点 const getPt1 = new MxCADUiPrPoint(); getPt1.setMessage('请选择面积的第一个点'); const pt1 = await getPt1.go(); if (!pt1) return; ptArr.push(pt1); // 循环取点 while (true) { const getPt = new MxCADUiPrPoint(); getPt.setMessage('请选择下一个点'); getPt.setKeyWords("[回退(B)/结束(O)]"); getPt.setUserDraw((pt, pw) => { const draw_pl = new McDbPolyline(); ptArr.forEach(point => { draw_pl.addVertexAt(point, 0, 0.1, 0.1); }); draw_pl.addVertexAt(pt); pw.drawMcDbEntity(draw_pl); }); const pt = await getPt.go(); const key = getPt.keyWordPicked(); if (key === 'B') { // 回退到上一个取点对象 if (ptArr.length > 1) { ptArr.pop(); } } else if (key === 'O') { // 结束取点 break; } else { if (!pt) break; ptArr.push(pt); } }; // 测量多边形至少取3个点 if(ptArr.length < 2) return; // 构造面积标注对象并设置标注位置 const mxcad = MxCpp.getCurrentMxCAD(); const area = new McDbTestAreaComment(); ptArr.forEach((point) => { area.addVertex(point, 0); }); const position = new MxCADUiPrPoint(); position.setMessage('请选择面积标注的位置'); position.setUserDraw((pt, pw) => { pw.setColor(0xFF0000) area.setPoint(pt); pw.drawMcDbEntity(area); }) const positionPt = await position.go(); if (!positionPt) return; area.setPoint(positionPt); area.trueColor = new McCmColor(255,0,0) mxcad.drawEntity(area); }
3.3.2 测量矩形面积
测量矩形面积只需要选取矩形的两个角点就可以确定矩形的大小与位置,参考代码如下:
// 矩形面积 async function Mx_ReactArea() { // 设置测量矩形的两个角点 const getPt1 = new MxCADUiPrPoint(); getPt1.setMessage('请选择矩形的角点1'); const pt1 = await getPt1.go(); if (!pt1) return; const getPt2 = new MxCADUiPrPoint(); getPt2.setMessage('请选择矩形的角点2'); // 动态绘制矩形 getPt2.setUserDraw((pt, pw) => { const pl = new McDbPolyline(); pl.isClosed = true; pl.addVertexAt(pt1); pl.addVertexAt(new McGePoint3d(pt.x, pt1.y)); pl.addVertexAt(pt); pl.addVertexAt(new McGePoint3d(pt1.x, pt.y)); pw.drawMcDbEntity(pl); }) const pt2 = await getPt2.go(); if (!pt2) return; // 构造面积标注 const reactArea = new McDbTestAreaComment(); reactArea.isReact = true; reactArea.addVertex(pt1); reactArea.addVertex(pt2); // 设置标注位置 const getPos = new MxCADUiPrPoint(); getPos.setMessage('请设置面积标注的位置'); getPos.setUserDraw((pt, pw) => { pw.setColor(0xFF0000) reactArea.setPoint(pt); pw.drawMcDbEntity(reactArea); }); const position = await getPos.go(); if (!position) return; reactArea.setPoint(position); // 设置标注颜色 reactArea.trueColor = new McCmColor(255, 0, 0) MxCpp.getCurrentMxCAD().drawEntity(reactArea); }
效果如下图: