实体基类
实体基类
EntityBase 是所有 CAD 实体的基类,定义了实体的通用属性和基本行为。所有具体的图形实体(如直线、圆、圆弧等)都继承自这个基类。
概述
实体生命周期
基本属性
标识属性
| 属性 | 类型 | 说明 |
|---|---|---|
type | string | 实体类型标识(如 "LINE"、"CIRCLE") |
id | number | 实体索引 ID(WebCAD 内部使用) |
objectId | string | DWG 对象句柄(从 DWG 导入时保留) |
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
console.log('实体类型:', line.type); // "LINE"
console.log('实体ID:', line.id); // 0(添加到文档后会分配)
console.log('DWG句柄:', line.objectId); // ""(新建实体为空)状态属性
| 属性 | 类型 | 说明 |
|---|---|---|
isAlive | boolean | 是否存活(false 表示已删除) |
isDirty | boolean | 是否需要重绘(脏标记) |
block | BlockDefinition | 所属块(模型空间或块定义) |
doc | CadDocument | 所属文档 |
import { Engine, EntityBase } from 'vjcad';
const entity: EntityBase = /* ... */;
// 检查实体状态
console.log('是否存活:', entity.isAlive);
console.log('是否需要重绘:', entity.isDirty);
console.log('所属文档:', entity.doc?.name);
console.log('所属块:', entity.block?.name);外观属性
颜色
WebCAD 支持两种颜色表示方式:索引颜色(ACI) 和 RGB 真彩色。
颜色存储格式
| 值范围 | 含义 |
|---|---|
| 0 | 随块(ByBlock) |
| 1-255 | CAD 标准颜色索引(ACI) |
| 256 | 随层(ByLayer) |
| >= 16777216 (0x1000000) | RGB 真彩色 |
RGB 颜色使用标记位 0x1000000 与索引颜色区分:
- RGB 颜色值 =
0x1000000 + 0xRRGGBB - 例如:红色 =
0x1000000 + 0xFF0000 = 0x1FF0000 = 33488896
使用索引颜色
注意调用顺序
必须先调用 setDefaults(),再设置颜色。否则 setDefaults() 会用系统默认颜色覆盖你设置的颜色值。
// 正确顺序
line.setDefaults(); // 先应用默认属性
line.color = 1; // 再设置颜色
// 错误顺序(颜色会被覆盖)
line.color = 1; // 设置颜色
line.setDefaults(); // 默认属性覆盖了颜色!import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
line.setDefaults(); // 先应用默认属性
// 再设置颜色索引
line.color = 1; // 红色
line.color = 2; // 黄色
line.color = 3; // 绿色
line.color = 256; // 随层(ByLayer)
line.color = 0; // 随块(ByBlock)颜色索引表
| 索引 | 颜色 | 说明 |
|---|---|---|
| 0 | 随块 | ByBlock,继承块的颜色 |
| 1 | 红色 | Red |
| 2 | 黄色 | Yellow |
| 3 | 绿色 | Green |
| 4 | 青色 | Cyan |
| 5 | 蓝色 | Blue |
| 6 | 洋红 | Magenta |
| 7 | 白色/黑色 | White(深色背景显示为白色) |
| 8-255 | 扩展颜色 | AutoCAD 扩展颜色 |
| 256 | 随层 | ByLayer,继承图层的颜色 |
使用 RGB 真彩色
WebCAD 提供 ColorConverter 工具类简化 RGB 颜色的操作:
import { LineEnt, Point2D, ColorConverter } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
line.setDefaults(); // 先应用默认属性
// 方式1:直接计算(RGB标记位 + RGB值)
line.color = 0x1000000 + 0xFF8000; // 橙色
// 方式2:使用 createRgbColorIndex (传入 RGB 整数值)
line.color = ColorConverter.createRgbColorIndex(0xFF8000); // 橙色
// 方式3:使用 createRgbColorIndexFromComponents (传入 R, G, B 分量)
line.color = ColorConverter.createRgbColorIndexFromComponents(255, 128, 0); // 橙色
// 方式4:使用 createRgbColorIndexFromHexStr (传入十六进制字符串)
line.color = ColorConverter.createRgbColorIndexFromHexStr("#FF8000"); // 橙色
line.color = ColorConverter.createRgbColorIndexFromHexStr("FF8000"); // 不带 # 也可以ColorConverter 工具类
| 方法 | 说明 | 示例 |
|---|---|---|
isRgbColor(color) | 判断是否为 RGB 颜色 | ColorConverter.isRgbColor(0x1FF0000) → true |
extractRgbValue(color) | 从颜色值提取 RGB | ColorConverter.extractRgbValue(0x1FF0000) → 0xFF0000 |
createRgbColorIndex(rgb) | 从 RGB 整数创建颜色 | ColorConverter.createRgbColorIndex(0xFF0000) → 0x1FF0000 |
createRgbColorIndexFromComponents(r, g, b) | 从 RGB 分量创建颜色 | ColorConverter.createRgbColorIndexFromComponents(255, 0, 0) |
createRgbColorIndexFromHexStr(hex) | 从十六进制字符串创建颜色 | ColorConverter.createRgbColorIndexFromHexStr("#FF0000") |
getRgbComponents(color) | 获取 RGB 分量 | ColorConverter.getRgbComponents(color) → {r, g, b} |
GetColorHexStr(color) | 获取十六进制字符串 | ColorConverter.GetColorHexStr(color) → "FF0000" |
GetColorCssStr(color) | 获取 CSS 颜色格式 | ColorConverter.GetColorCssStr(color) → "#FF0000" |
颜色名称转换
使用 colorNameToIndex 和 colorIndexToName 进行颜色名称与索引的转换:
import { colorNameToIndex, colorIndexToName } from 'vjcad';
// 颜色名称 → 索引
colorNameToIndex("red"); // 1
colorNameToIndex("ByLayer"); // 256
colorNameToIndex("#FF8000"); // 0x1FF8000 (RGB 橙色)
// 索引 → 颜色名称
colorIndexToName(1); // "Red"
colorIndexToName(256); // "ByLayer"
colorIndexToName(0x1FF8000); // "#FF8000"图层
import { Engine, LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
// 通过图层名设置
line.layer = '标注层';
// 获取图层名
console.log('图层:', line.layer);
// 通过图层 ID 设置(内部使用)
line.layerId = '1'; // 图层 ID
// 获取图层 ID
console.log('图层ID:', line.layerId);线型
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
// 设置线型
line.lineType = 'CONTINUOUS'; // 实线
line.lineType = 'HIDDEN'; // 虚线
line.lineType = 'CENTER'; // 中心线
line.lineType = 'PHANTOM'; // 幻影线
line.lineType = 'ByLayer'; // 随层
// 设置线型比例
line.lineTypeScale = 2.0; // 放大虚线间隔内置线型
| 线型名 | 别名 | 说明 |
|---|---|---|
CONTINUOUS | - | 连续实线 |
HIDDEN | DASHED, DASHED1, DASHED2 | 虚线 |
CENTER | CENTER1, CENTER2 | 中心线 |
PHANTOM | PHANTOM1, PHANTOM2 | 幻影线 |
ByLayer | - | 随图层线型 |
ByBlock | - | 随块线型 |
线宽
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
// 设置线宽(单位:0.01mm)
line.lineWeight = 25; // 0.25mm
line.lineWeight = 50; // 0.50mm
line.lineWeight = -1; // 随层(ByLayer)
line.lineWeight = -2; // 随块(ByBlock)透明度
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
// 设置透明度(0-100)
line.transpMgr.setTp100(50); // 50% 透明
// 随层透明度
line.transpMgr.setValue(-1); // 随图层
// 获取透明度
const alpha = line.transpMgr.alpha; // 0-1 范围扩展数据 (XData)
扩展数据(Extended Data,简称 XData)允许为实体附加自定义数据,这些数据会随 DWG 文件一起保存和加载。适用于存储业务属性、传感器数据、设备信息等。
数据格式
扩展数据以键值对形式存储,内部使用 JSON 序列化:
// 内部存储格式(遵循 DXF 标准)
{
"1001": "APP_NAME", // 应用程序名(标识数据来源)
"1000": "{\"key\":\"value\"}" // JSON 序列化的数据字符串
}DWG 文件限制
- 单个字符串:数据序列化后不能超过 255 字节
- 每个实体总 xdata:不能超过 16KB
建议只存储关键数据,避免存储大量文本或复杂结构。
复制实体会保留扩展数据
使用 clone() 方法或复制命令复制实体时,扩展数据会被完整复制到新实体,无需手动处理。
设置扩展数据
使用 setExtData(appName, data) 方法:
import { LineEnt, Engine } from 'vjcad';
// 创建实体
const line = new LineEnt([0, 0], [100, 0]);
line.setDefaults();
// 设置扩展数据
line.setExtData("SENSOR_DATA", {
name: "温度传感器",
value: 25.5,
unit: "℃",
isActive: true,
lastUpdate: "2026-01-29"
});
Engine.addEntities(line);| 参数 | 类型 | 说明 |
|---|---|---|
appName | string | 应用程序名,用于在 DWG 中标识数据来源 |
data | object | 要存储的数据对象(将被序列化为 JSON) |
读取扩展数据
// 检查是否有扩展数据
if (line.hasExtData()) {
// 获取应用程序名
console.log('AppName:', line.extDataAppName); // "SENSOR_DATA"
// 获取扩展数据(已自动解析 JSON)
const data = line.extData;
console.log('传感器名称:', data.name); // "温度传感器"
console.log('传感器值:', data.value); // 25.5
console.log('是否激活:', data.isActive); // true
}修改扩展数据
// 方式1:完整替换
line.setExtData("SENSOR_DATA", {
name: "温度传感器",
value: 30.2, // 更新温度值
unit: "℃",
isActive: true,
alarm: true // 新增报警字段
});
// 方式2:基于现有数据修改
const currentData = line.extData;
if (currentData) {
currentData.value = 30.2; // 更新温度值
currentData.lastCheck = "2026-01-28"; // 新增检查日期
line.setExtData("SENSOR_DATA", currentData);
}清除扩展数据
line.clearExtData();
console.log(line.hasExtData()); // false查询带扩展数据的实体
// 获取所有实体
const allEntities = Engine.getEntities();
// 筛选有扩展数据的实体
const entitiesWithXData = allEntities.filter(ent => ent.hasExtData?.());
// 按 AppName 分类
const byAppName = {};
entitiesWithXData.forEach(ent => {
const appName = ent.extDataAppName;
if (!byAppName[appName]) byAppName[appName] = [];
byAppName[appName].push(ent);
});获取原始 xdata(高级用法)
// 获取原始格式的 xdata
const rawXData = line.xdataRaw;
// { "1001": "SENSOR_DATA", "1000": "{\"name\":\"温度传感器\",...}" }
// 直接设置原始 xdata(通常不需要)
line.xdataRaw = {
"1001": "MY_APP",
"1000": JSON.stringify({ custom: "data" })
};扩展数据 API 汇总
| 方法/属性 | 类型 | 说明 |
|---|---|---|
setExtData(appName, data) | method | 设置扩展数据 |
extData | getter | 获取解析后的扩展数据对象 |
extDataAppName | getter | 获取应用程序名 |
hasExtData() | method | 检查是否有扩展数据 |
clearExtData() | method | 清除扩展数据 |
xdataRaw | getter/setter | 获取/设置原始 xdata 对象 |
属性面板编辑
选中实体后,可以在右侧属性面板中查看和编辑扩展数据:
- 属性面板显示"扩展数据"行,显示当前 AppName 和字段数
- 点击右侧按钮打开详细编辑对话框
- 支持添加、编辑、删除键值对
- 自动识别数据类型(字符串、数字、布尔值)
完整示例
import { LineEnt, CircleEnt, Engine, message } from 'vjcad';
// 创建带传感器数据的线段
const sensorLine = new LineEnt([0, 0], [100, 0]);
sensorLine.setDefaults();
sensorLine.color = 1;
sensorLine.setExtData("SENSOR_DATA", {
name: "温度传感器",
value: 25.5,
unit: "℃",
isActive: true
});
// 创建带设备信息的圆
const deviceCircle = new CircleEnt([150, 0], 20);
deviceCircle.setDefaults();
deviceCircle.color = 3;
deviceCircle.setExtData("DEVICE_INFO", {
deviceId: "DEV-001",
type: "阀门",
status: "open",
pressure: 1.2
});
// 添加到画布
Engine.addEntities([sensorLine, deviceCircle]);
// 读取和显示扩展数据
const sensorData = sensorLine.extData;
message.info(`传感器: ${sensorData.name}, 值: ${sensorData.value}${sensorData.unit}`);
const deviceData = deviceCircle.extData;
message.info(`设备: ${deviceData.deviceId}, 状态: ${deviceData.status}`);
// 更新数据
sensorData.value = 30.2;
sensorData.alarm = true;
sensorLine.setExtData("SENSOR_DATA", sensorData);
Engine.zoomExtents();
// 提示:导出为 DWG 后重新打开,扩展数据会保留属性汇总表
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
type | string | "" | 实体类型标识符 |
id | number | 0 | 实体索引 ID |
objectId | string | "" | DWG 对象句柄 |
color | number | 256 | 颜色索引(256=随层) |
layer | string | "0" | 图层名称 |
layerId | string | "0" | 图层 ID |
lineType | string | "ByLayer" | 线型名称 |
lineTypeScale | number | 1 | 线型比例 |
lineWeight | number | -1 | 线宽(-1=随层) |
isAlive | boolean | true | 是否存活 |
isDirty | boolean | false | 是否需要重绘 |
extData | object | undefined | 扩展数据(已解析的 JSON 对象) |
extDataAppName | string | undefined | 扩展数据的应用程序名 |
xdataRaw | object | undefined | 原始扩展数据格式 |
核心方法
setDefaults()
应用系统默认属性(当前图层、颜色、线型等)。创建新实体后应调用此方法。
import { Engine, LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
// 应用系统默认值
line.setDefaults();
// 等同于:
// line.layer = Engine.currentDoc.CLAYER;
// line.color = Engine.CECOLOR;
// line.lineType = Engine.CELTYPE;
// line.lineTypeScale = Engine.CELTSCALE;setModified()
标记实体已被修改(设置 isDirty = true)。
注意:setModified() 不会触发重绘
setModified() 只是标记实体为"脏"状态,不会自动触发画面重绘。
要看到修改效果,必须在适当时机调用 Engine.regen()。
// setModified() 内部实现原理:
setModified(): void {
this.isDirty = true; // 标记脏状态
this.cachedBoundingBox = undefined; // 清除边界框缓存
// 注意:没有调用任何渲染方法!
}import { LineEnt, Point2D, Engine } from 'vjcad';
const line: LineEnt = /* ... */;
// 通过 setter 修改属性时,会自动调用 setModified()
line.color = 1; // 自动调用 setModified()
line.startPoint.x = 50; // ObservablePoint2D 自动调用 setModified()
// 但修改后必须手动调用 regen() 才能看到效果
Engine.regen();自动调用场景
以下操作会自动调用 setModified(),无需手动调用:
- 属性 setter:
entity.color、entity.radius、entity.layer等 - ObservablePoint2D:
line.startPoint.x、circle.center.y等 - 变换方法:
move()、rotate()、scale()、mirror()等
但都需要手动调用 Engine.regen() 才能看到修改效果!
需要手动调用的场景
直接操作集合或内部数据时需要手动调用:
// 直接操作 bulgePoints 集合
pline.bulgePoints.items[0].bulge = 0.5;
pline.bulgePoints.removeLast();
pline.setModified(); // 需要手动调用clone()
创建实体的深拷贝。
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
line.setDefaults();
line.color = 1;
// 克隆实体
const lineCopy = line.clone();
// 克隆的实体是独立的
lineCopy.color = 3; // 不影响原实体
console.log(line.color); // 1
console.log(lineCopy.color); // 3dispose()
释放实体占用的资源。通常在删除实体时由系统调用。
// 手动释放(通常不需要)
entity.dispose();
// 实体被删除时自动释放
Engine.eraseEntities(entity); // 内部会调用 dispose()几何变换
所有实体都支持以下几何变换方法。这些方法的点参数支持 PointInput 类型,可以使用 Point2D 对象或 [x, y] 数组形式。
重要:延迟渲染机制
WebCAD 采用延迟渲染设计,几何变换方法(move、rotate、scale、mirror)只会:
- 修改实体的几何数据
- 调用
setModified()标记isDirty = true
不会自动触发重绘! 必须手动调用 Engine.regen() 才能看到变化。
// 错误:移动后看不到效果
circle.move([0, 0], [50, 30]);
// 画面没有更新...
// 正确:移动后调用 regen()
circle.move([0, 0], [50, 30]);
Engine.regen(); // 触发重绘,看到移动效果设计原因:允许批量修改多个实体后一次性重绘,避免每次小修改都触发 GPU 渲染,提高性能。
// 批量修改后统一重绘(推荐)
entities.forEach(ent => ent.move([0, 0], [100, 0]));
Engine.regen(); // 一次重绘,性能最优move() - 移动
import { EntityBase, Point2D } from 'vjcad';
const entity: EntityBase = /* ... */;
// 简化写法(推荐)
entity.move([0, 0], [50, 50]);
// Point2D 写法
entity.move(new Point2D(0, 0), new Point2D(50, 50));
// 偏移量为 to - from = (50, 50)
// 实际上是将实体整体平移 (50, 50)rotate() - 旋转
import { EntityBase, Point2D } from 'vjcad';
const entity: EntityBase = /* ... */;
// 简化写法(推荐)
entity.rotate([50, 50], Math.PI / 4); // 绕点 (50,50) 旋转 45 度
// Point2D 写法
entity.rotate(new Point2D(50, 50), Math.PI / 4);scale() - 缩放
import { EntityBase, Point2D } from 'vjcad';
const entity: EntityBase = /* ... */;
// 简化写法(推荐)
entity.scale([50, 50], 2); // 以点 (50,50) 为基点,放大 2 倍
entity.scale([50, 50], 0.5); // 缩小到 50%
// Point2D 写法
entity.scale(new Point2D(50, 50), 2);mirror() - 镜像
import { EntityBase, Point2D } from 'vjcad';
const entity: EntityBase = /* ... */;
// 简化写法(推荐)
entity.mirror([0, 0], [100, 0]); // 沿水平线镜像(Y=0)
entity.mirror([50, 0], [50, 100]); // 沿垂直线镜像(X=50)
entity.mirror([0, 0], [100, 100]); // 沿任意直线镜像
// Point2D 写法
entity.mirror(new Point2D(0, 0), new Point2D(100, 0));PointInput 类型
所有几何变换方法都支持 PointInput 类型参数,可以使用:
Point2D对象:new Point2D(x, y)- 二维数组:
[x, y](推荐,更简洁) - 三维数组:
[x, y, z]
详见 点和向量 章节。
边界框
获取实体的边界框(Bounding Box)。
import { EntityBase, BoundingBox } from 'vjcad';
const entity: EntityBase = /* ... */;
// 获取边界框
const bbox: BoundingBox = entity.boundingBox();
// 访问边界框属性
console.log('最小点:', bbox.pt1);
console.log('最大点:', bbox.pt2);
console.log('宽度:', bbox.width);
console.log('高度:', bbox.height);
console.log('中心点:', bbox.center);序列化
实体支持序列化为 JSON 格式以及从 JSON 恢复。
导出 (toDb)
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
line.setDefaults();
// 序列化为数据库格式
const dbData = line.toDb();
// dbData 结构示例:
// {
// type: "LINE",
// layerId: "0",
// color: 256,
// lineType: "ByLayer",
// lineTypeScale: 1,
// startPoint: [0, 0], // 2D 坐标格式
// endPoint: [100, 100]
// }
// 带 Z 坐标时: startPoint: [0, 0, 10], endPoint: [100, 100, 20]导入 (fromDb)
import { LineEnt } from 'vjcad';
// 从 JSON 数据恢复实体(支持新的数组格式和旧的对象格式)
const dbData = {
type: "LINE",
layerId: "0",
color: 1,
startPoint: [0, 0], // 新格式: [x, y] 或 [x, y, z]
endPoint: [100, 100, 50] // 带 Z 坐标
};
const line = new LineEnt();
line.fromDb(dbData);
console.log('起点:', line.startPoint); // Point2D(0, 0)
console.log('终点:', line.endPoint); // Point2D(100, 100)完整示例
import { Engine, LineEnt, CircleEnt, Point2D } from 'vjcad';
// 创建实体(使用简化写法)
const line = new LineEnt([0, 0], [100, 100]);
const circle = new CircleEnt([50, 50], 30);
// 应用系统默认属性
line.setDefaults();
circle.setDefaults();
// 设置自定义属性
line.color = 1; // 红色
line.layer = '图层1';
line.lineType = 'HIDDEN'; // 虚线
line.lineTypeScale = 2.0;
circle.color = 3; // 绿色
circle.transpMgr.setTp100(30); // 30% 透明
// 几何变换(使用简化写法)
line.move([0, 0], [50, 0]);
circle.scale([50, 50], 1.5);
// 添加到画布
Engine.addEntities([line, circle]);
// 【重要】修改后必须调用 regen() 才能看到效果
Engine.regen();
// 获取边界框
const lineBBox = line.boundingBox();
const circleBBox = circle.boundingBox();
console.log('直线边界:', lineBBox.pt1, lineBBox.pt2);
console.log('圆边界:', circleBBox.pt1, circleBBox.pt2);
// 克隆实体
const lineCopy = line.clone();
lineCopy.color = 5; // 蓝色
lineCopy.move([0, 0], [0, 50]);
Engine.addEntities(lineCopy);下一步
- 直线实体 (LineEnt) - 直线实体详解
- 圆实体 (CircleEnt) - 圆实体详解
- 圆弧实体 (ArcEnt) - 圆弧实体详解
- 多段线实体 (PolylineEnt) - 多段线实体详解
- 自定义实体 - 创建自定义实体
相关示例
- 扩展数据示例:
webcad-playground/public/static/demo/map/example/property/07xdata.js