MXCAD McDbCustomEntity 自定义实体
在 MXCAD 中,开发者可以通过继承 McDbCustomEntity 类来创建满足特定业务需求的自定义实体。自定义实体允许您定义独特的数据结构、渲染逻辑和交互行为。下文将详细介绍自定义实体中核心方法的用途、调用时机及通用实现规范。
点击 McDbCustomEntity 查看详细属性和方法说明。
数据持久化方法
自定义实体需要在文件保存、读取或复制时正确序列化和反序列化其内部数据。
1. dwgInFields(filter: IMcDbDwgFiler): boolean
- 用途:从文件或流中读取自定义实体的数据,用于恢复实体状态。
- 调用时机:打开图纸、插入块、复制实体等需要从存储介质加载数据的场景。
- 实现要点:
- 使用
filter对象按字段名读取数据(如readPoint,readString,readInt等)。 - 将读取的数据赋值给实体的私有属性。
- 若涉及复杂结构(如矩阵),需手动解析并重建(例如将 JSON 字符串解析为二维数组并填入矩阵)。
- 成功返回
true,失败返回false。
- 使用
ts
public dwgInFields(filter: IMcDbDwgFiler): boolean {
this.pt1 = filter.readPoint("pt1").val;
this.pt2 = filter.readPoint("pt2").val;
const matrixStr = filter.readString("matrix").val;
const matrixData = JSON.parse(matrixStr) as number[][];
// 重建矩阵数据
for (let i = 0; i < matrixData.length; i++) {
for (let j = 0; j < matrixData[i].length; j++) {
this.matrix.setData(i, j, matrixData[i][j]);
}
}
return true;
}2. dwgOutFields(filter: IMcDbDwgFiler): boolean
- 用途:将自定义实体的当前状态写入文件或流,用于持久化存储。
- 调用时机:保存图纸、复制实体、导出块等需要将内存数据写入存储的场景。
- 实现要点:
- 使用 filter 对象按字段名写入数据(如
writePoint,writeString,writeInt等)。 - 复杂数据类型(如矩阵)需转换为可序列化格式(如 JSON 字符串)。
- 成功返回
true,失败返回false。
- 使用 filter 对象按字段名写入数据(如
ts
public dwgOutFields(filter: IMcDbDwgFiler): boolean {
filter.writePoint("pt1", this.pt1);
filter.writePoint("pt2", this.pt2);
const matrix: number[][] = [];
for (let i = 0; i < 4; i++) {
matrix[i] = [];
for (let j = 0; j < 4; j++) {
matrix[i][j] = this.matrix.getData(i, j);
}
}
filter.writeString("matrix", JSON.stringify(matrix));
return true;
}交互与编辑方法
自定义实体需支持用户通过夹点(Grip Points)进行直观编辑。
1. getGripPoints(): McGePoint3dArray
- 用途:返回实体在视图中显示的编辑夹点位置集合。
- 调用时机:实体被选中时,系统调用此方法获取可操作的夹点。
- 实现要点:
- 返回一个
McGePoint3dArray对象,包含所有关键控制点。 - 夹点顺序对应
moveGripPointsAt中的索引iIndex(例如:索引 0 对应起点,索引 1 对应终点)。 - 通常返回起点、终点、中心点等关键几何位置。
- 返回一个
ts
// 以自定义直线实体为例
public getGripPoints(): McGePoint3dArray {
const points = new McGePoint3dArray();
points.append(this.pt1); // 索引 0
points.append(this.pt2); // 索引 1
return points;
}2. moveGripPointsAt(iIndex: number, dXOffset: number, dYOffset: number, dZOffset: number): void
- 用途:处理用户拖动某个夹点后的坐标偏移,更新实体几何数据。
- 调用时机:用户拖动夹点并释放鼠标时触发。
- 实现要点:
- 根据
iIndex判断是哪个夹点被移动。 - 对对应属性应用偏移量 (
dXOffset,dYOffset,dZOffset)。 - 必须调用
this.assertWrite()确保实体处于可写状态。 - 无需返回值,直接修改内部状态。
- 根据
ts
// 以自定义直线实体为例
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;
}
}渲染与显示方法
自定义实体需定义其在视图中的视觉表现形式。
1. worldDraw(draw: MxCADWorldDraw): void
- 用途:绘制实体在视图中的图形表示。
- 调用时机:视图刷新、实体创建、变换操作后自动触发。
- 实现要点:
- 利用 draw 对象绘制基础图元(如线、圆、多段线等)组合成自定义图形。
- 可应用变换矩阵、颜色、线型等属性。
- 不应在此方法中修改实体数据,仅负责可视化。
ts
// 以自定义直线实体为例
public worldDraw(draw: MxCADWorldDraw): void {
// 创建临时基础实体用于绘制
const line = new McDbLine(this.pt1, this.pt2);
// 应用自定义变换
line.transformBy(this.matrix);
// 委托给系统绘制
draw.drawEntity(line);
}TIP
注意:由于每次重绘都会重建实例,请确保所有状态都保存在实体属性中,而非局部变量或外部引用。
几何计算与变换方法
自定义实体需支持空间变换和边界计算,以兼容 CAD 系统的通用操作。
1. getBoundingBox(): { minPt: McGePoint3d; maxPt: McGePoint3d; ret: boolean }
- 用途:计算实体的最小包围盒。
- 调用时机:视图缩放至实体、选择集判断、碰撞检测等场景。
- 实现要点:
- 返回包含
minPt(最小角点)、maxPt(最大角点)和ret(是否成功)的对象。 - 需考虑实体所有几何部分的空间范围(包括变换后的坐标)。
- 若实体无有效几何,
ret应设为false。
- 返回包含
ts
public getBoundingBox(): { minPt: McGePoint3d; maxPt: McGePoint3d; ret: boolean } {
const minX = Math.min(this.pt1.x, this.pt2.x);
const minY = Math.min(this.pt1.y, this.pt2.y);
const maxX = Math.max(this.pt1.x, this.pt2.x);
const maxY = Math.max(this.pt1.y, this.pt2.y);
return {
minPt: new McGePoint3d(minX, minY, 0),
maxPt: new McGePoint3d(maxX, maxY, 0),
ret: true
};
}2. transformBy(mat: McGeMatrix3d): boolean
- 用途:对实体应用仿射变换(平移、旋转、缩放、镜像等)。
- 调用时机:执行 MOVE、ROTATE、SCALE、MIRROR 等编辑命令时。
- 实现要点:
- 将传入矩阵 mat 与实体当前变换矩阵相乘(通常使用 preMultBy 前乘或 postMultBy 后乘,依坐标系需求而定)。
- 调用 this.assertWrite() 标记实体为可写。
- 成功返回 true,失败返回 false。
ts
// 以自定义直线实体为例
public transformBy(mat: McGeMatrix3d): boolean {
// 将外部变换矩阵应用到内部存储的矩阵上
this.matrix = this.matrix.preMultBy(mat);
this.assertWrite();
return true;
}辅助方法与最佳实践
1. 构造函数与 create 方法
- 构造函数用于初始化实例。
- create(imp: any) 是工厂方法,用于在反序列化或克隆时创建新实例,必须重写。
ts
// 以自定义直线实体为例
constructor(imp?: any) {
super(imp);
}
public create(imp: any) {
return new McDbTestLineCustomEntity(imp);
}2. 类型标识
重写 getTypeName(): string 返回唯一类型名称,用于系统识别和调试。
ts
// 自定义实体类型名称
public getTypeName(): string {
return "McDbTestLineCustomEntity";
}3. 数据访问器
提供 getXXX() 和 setXXX() 方法供外部安全访问私有属性,并在 setter 中调用 assertWrite()。
ts
// 以自定义直线实体为例
public setPoint1(pt1: McGePoint3d) {
this.assertWrite();
this.pt1 = pt1.clone();
}
public getPoint1() {
return this.pt1;
}TIP
也可以设置其他方法访问名,只要保证访问的是公共方法即可。
自定义实体注册
根据上述步骤创建完成自定义实体后,需要在 mxcad 对象中注册该自定义实体,一个自定义实体只需要在 mxcad 对象中注册一次且自定义实体的类名是唯一的,若重复多次注册同一个自定义实体类名,控制台将会抛出 ”MxCADError:already has this class name McDbTestLineCustomEntity“ 的错误。因此,我们可以在 mxcad 初始化的时候进行注册。
ts
import { MxCpp } from "mxcad";
// McDbTestLineCustomEntity 为你创建的自定义实体类名
const mxcad = MxCpp.getCurrentMxCAD();
mxcad.on("init", () => {
new McDbTestLineCustomEntity().rxInit();
});功能实践
下面以创建一条自定义直线实体为例,演示完整的自定义实体创建。
- 继承
McDbCustomEntity定义实体
ts
import {
IMcDbDwgFiler,
McDbCustomEntity,
McDbLine,
McGePoint3d,
McGePoint3dArray,
MxCADUiPrPoint,
MxCADWorldDraw,
MxCpp,
McGeMatrix3d,
} from "mxcad";
class McDbTestLineCustomEntity extends McDbCustomEntity {
private pt1: McGePoint3d = new McGePoint3d();
private pt2: McGePoint3d = new McGePoint3d();
private matrix: McGeMatrix3d = new McGeMatrix3d();
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;
const matrix = filter.readString("matrix").val;
const matrixData = JSON.parse(matrix) as number[][];
for (let index = 0; index < matrixData.length; index++) {
const element = matrixData[index];
for (let j = 0; j < element.length; j++) {
const data = element[j];
this.matrix.setData(index, j, data);
}
}
return true;
}
public dwgOutFields(filter: IMcDbDwgFiler): boolean {
filter.writePoint("pt1", this.pt1);
filter.writePoint("pt2", this.pt2);
const matrix: number[][] = [];
for (let i = 0; i < 4; i++) {
matrix[i] = [];
for (let j = 0; j < 4; j++) {
const data = this.matrix.getData(i, j);
matrix[i][j] = data;
}
}
filter.writeString("matrix", JSON.stringify(matrix));
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);
tmpline.transformBy(this.matrix);
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;
}
// 编辑变换
public transformBy(_mat: McGeMatrix3d): boolean {
this.matrix = this.matrix.preMultBy(_mat);
this.assertWrite();
return true;
}
public getBoundingBox(): {
minPt: McGePoint3d;
maxPt: McGePoint3d;
ret: boolean;
} {
return {
minPt: new McGePoint3d(
Math.min(this.pt1.x, this.pt2.x),
Math.min(this.pt1.y, this.pt2.y),
),
maxPt: new McGePoint3d(
Math.max(this.pt1.x, this.pt2.x),
Math.max(this.pt1.y, this.pt2.y),
),
ret: true,
};
}
}- 注册实体
ts
import { MxCpp } from "mxcad";
const mxcad = MxCpp.getCurrentMxCAD();
mxcad.on("init", () => {
new McDbTestLineCustomEntity().rxInit();
});- 设置方法绘制目标实体
ts
import { MxCADUiPrPoint, MxCpp } from "mxcad";
async function MxTest_DrawCustomEntity() {
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);
const mxcad = MxCpp.getCurrentMxCAD();
mxcad.drawEntity(myline);
}