在线CAD(云CAD)实现自定义实体的详细方法 HarmonyOS 鸿蒙Next
在线CAD(云CAD)实现自定义实体的详细方法 HarmonyOS 鸿蒙Next
前言
自定义实体在CAD二次开发中使用的频率较高,本章节主要阐述网页CAD中使用自定义实体的方法,mxcad可以根据用户的具体需求来创建和管理自定义实体,可以通过从自定义实体类 McDbCustomEntity() 中继承实体的名称、属性、方法,也可结合自身需求对自定义实体类中的属性或方法进行重写。
设置自定义实体
下面以自定义直线为例来介绍如何使用自定义实体,效果如下:
-
定义自定义实体类继承 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"; } }
-
通过重写 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; }
-
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; }
-
每次触发动态绘制 worldDraw,就会将原本的实例对象删掉(同时也会删除渲染的three.js物体对象),通过create方法重新创建实例,参考代码如下:
public worldDraw(draw: MxCADWorldDraw): void { let tmpline = new McDbLine(this.pt1, this.pt2); draw.drawEntity(tmpline); }
-
在程序启动的时候,调用 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查看,如下图:
-
根据上述自定义实体的方法,我们通过继承 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; } }
-
调用上述实现的自定义类 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
在鸿蒙Next中实现在线CAD(云CAD)自定义实体,可以通过以下步骤进行:
-
定义实体类:首先,创建一个继承自
ohos.agp.components.Component
的类,作为自定义实体的基类。该类需要包含实体的属性和方法,如位置、大小、颜色等。 -
实现绘制逻辑:在自定义实体类中,重写
onDraw
方法,使用ohos.agp.render.Canvas
进行绘制。可以通过Canvas
提供的API绘制线条、矩形、圆形等基本图形,或使用Path
绘制复杂图形。 -
处理用户交互:重写
onTouchEvent
方法,处理用户的触摸事件,如点击、拖动等。根据用户的操作,更新实体的位置或属性,并调用invalidate
方法触发重绘。 -
序列化与反序列化:为了支持云存储和加载,实现实体的序列化和反序列化。可以使用
ohos.utils.Parcel
或ohos.utils.XmlSerializer
将实体数据转换为字节流或XML格式,反之亦然。 -
集成到云CAD系统:将自定义实体类集成到云CAD系统中,确保其能够与其他实体协同工作。可以通过注册实体类型、管理实体生命周期等方式实现。
-
测试与优化:在鸿蒙设备上测试自定义实体的功能,确保其在不同分辨率和设备上表现一致。根据测试结果进行优化,如调整绘制性能、处理边界情况等。
通过以上步骤,可以在鸿蒙Next中实现在线CAD(云CAD)的自定义实体功能。
更多关于在线CAD(云CAD)实现自定义实体的详细方法 HarmonyOS 鸿蒙Next的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中实现在线CAD(云CAD)自定义实体的方法如下:
- 定义实体类:创建一个继承自
CADEntity
的类,包含实体的属性和方法。 - 实现序列化:重写
toJSON
和fromJSON
方法,确保实体能在网络中传输。 - 注册自定义实体:使用
CAD.registerEntity
方法将实体注册到CAD系统中,确保系统能识别和渲染。 - 绘制实体:在
draw
方法中实现实体的绘制逻辑,使用Canvas API进行渲染。 - 事件处理:重写
onClick
、onHover
等方法,处理用户交互事件。
通过这些步骤,您可以在HarmonyOS鸿蒙Next中实现自定义CAD实体,并确保其在云环境中正常运行。