进阶实体
标注、填充、块插入等高级实体类型。
HatchEnt - 填充
const { HatchEnt, PolylineEnt, Edge, Edges, EdgeType, Engine } = vjcad;
// 1. 创建边界(闭合多段线)
const boundary = new PolylineEnt();
boundary.setPoints([
[0, 0],
[100, 0],
[100, 60],
[0, 60]
]);
boundary.isClosed = true;
boundary.setDefaults();
// 2. 创建填充
const hatch = new HatchEnt();
hatch.patternName = "ANSI31"; // 图案名称
hatch.patternScale = 1.0; // 图案比例
hatch.patternAngle = 0; // 图案角度
// 3. 设置边界
const edge = new Edge();
edge.edgeType = EdgeType.Polyline;
edge.bulgePoints = boundary.bulgePoints.clone();
const edges = new Edges();
edges.add(edge);
hatch.setLoops(edges);
hatch.setDefaults();
hatch.color = 4;
Engine.addEntities([boundary, hatch]);
常用填充图案:
| 图案名 | 说明 |
|---|---|
SOLID |
实心填充 |
ANSI31 |
45° 单斜线(铁/砖石) |
ANSI32 |
45° 双线组(钢) |
ANSI33 |
45° 实线+虚线交替(青铜/黄铜) |
ANSI34 |
45° 四线组(塑料/橡胶) |
ANSI37 |
45°/135° 交叉斜线 |
属性:
patternName- 图案名称patternScale- 图案比例patternAngle- 图案角度color- 填充颜色
标注实体
LinearDimensionEnt - 线性标注
const { LinearDimensionEnt, Point2D, Engine } = vjcad;
const dim = new LinearDimensionEnt(
new Point2D(0, 0), // startPoint: 标注起点
new Point2D(100, 0), // endPoint: 标注终点
new Point2D(50, 20), // thirdPoint: 标注线位置
0 // dimAngle: 0=水平, PI/2=垂直
);
dim.setDefaults();
dim.textHeight = 5;
dim.arrowSize = 3;
dim.textOverride = "L=<>"; // <> 会被替换为测量值
dim.color = 3;
Engine.addEntities(dim);
AlignedDimensionEnt - 对齐标注
const { AlignedDimensionEnt, Point2D, Engine } = vjcad;
const dim = new AlignedDimensionEnt(
new Point2D(0, 0), // startPoint
new Point2D(100, 50), // endPoint
new Point2D(50, 40) // thirdPoint
);
dim.setDefaults();
Engine.addEntities(dim);
RadialDimensionEnt - 半径标注
const { RadialDimensionEnt, Point2D, CircleEnt, Engine } = vjcad;
// 先创建圆
const circle = new CircleEnt([50, 50], 30);
circle.setDefaults();
Engine.addEntities(circle);
// 创建半径标注
const dim = new RadialDimensionEnt(
new Point2D(50, 50), // center: 圆心
new Point2D(80, 50), // chordPoint: 圆周点
10 // leaderLength: 引线延伸长度
);
dim.setDefaults();
Engine.addEntities(dim);
DiametricDimensionEnt - 直径标注
const { DiametricDimensionEnt, Point2D, Engine } = vjcad;
const dim = new DiametricDimensionEnt(
new Point2D(20, 50), // chordPoint1: 直径端点1
new Point2D(80, 50), // chordPoint2: 直径端点2
10 // leaderLength
);
dim.setDefaults();
Engine.addEntities(dim);
AngleDimensionEnt - 角度标注
const { AngleDimensionEnt, Point2D, Engine } = vjcad;
const dim = new AngleDimensionEnt(
new Point2D(0, 0), // center: 角度顶点
new Point2D(100, 0), // xLine1Point: 第一条边上的点
new Point2D(50, 80), // xLine2Point: 第二条边上的点
new Point2D(60, 40) // arcPoint: 标注弧位置
);
dim.setDefaults();
Engine.addEntities(dim);
ArcDimensionEnt - 弧长标注
const { ArcDimensionEnt, Point2D, Engine } = vjcad;
const dim = new ArcDimensionEnt(
new Point2D(50, 50), // center
new Point2D(80, 50), // xLine1Point: 圆弧起点
new Point2D(50, 80), // xLine2Point: 圆弧终点
new Point2D(70, 70) // arcPoint: 标注位置
);
dim.setDefaults();
Engine.addEntities(dim);
OrdinateDimensionEnt - 坐标标注
const { OrdinateDimensionEnt, Point2D, Engine } = vjcad;
const dim = new OrdinateDimensionEnt(
new Point2D(0, 0), // origin: 坐标原点(首参,不能省)
new Point2D(100, 50), // definingPoint: 被标注点
new Point2D(120, 70), // leaderEndPoint: 引线终点
true // displayModeOrUseXAxis: true=X坐标, false=Y坐标
);
dim.setDefaults();
Engine.addEntities(dim);
标注通用属性:
| 属性 | 说明 |
|---|---|
textHeight |
文字高度 |
arrowSize |
箭头大小 |
textOverride |
文字覆盖(<> 被替换为测量值) |
scaleFactor |
缩放因子 |
decimalPlaces |
小数位数 |
标注方法:
getMeasurement()- 获取测量值getFormattedMeasurement()- 获取格式化后的文本
关联标注
标注可以关联到源实体,源实体移动时标注自动更新。
const { LinearDimensionEnt, LineEnt, Point2D, Engine } = vjcad;
// 创建直线
const line = new LineEnt([0, 0], [100, 0]);
line.setDefaults();
Engine.addEntities(line);
// 创建标注
const dim = new LinearDimensionEnt(
new Point2D(0, 0),
new Point2D(100, 0),
new Point2D(50, 20),
0
);
dim.setDefaults();
Engine.addEntities(dim);
// 建立关联(必须在 addEntities 之后)
dim.setSourceEntities(
line.id, -1, // 起点关联实体 + 段索引:LINE 忽略段索引(填 -1 即可);PLINE 须填 ≥0 的直线段索引(-1/弧段取不到点,标注不跟随)
line.id, -1, // 终点关联实体
'start', // 起点端点类型
'end' // 终点端点类型
);
// 检查关联状态
console.log("isAssociative:", dim.isAssociative);
端点类型:
'start'- 起点'end'- 终点'center'- 类型里有但未实现(Linear/Aligned 关联落到默认分支取起点)'midpoint'- 中点
精确定位:
// 使用 parameterT(0~1 表示在线段上的比例位置)
dim.setSourceEntities(
line.id, -1,
line.id, -1,
undefined, // 不使用 pointType
undefined,
0.25, // startParameterT: 1/4 处
0.75 // endParameterT: 3/4 处
);
InsertEnt - 块插入
创建块定义
const { LineEnt, CircleEnt, Engine } = vjcad;
const doc = Engine.currentDoc;
const blockName = "MySymbol";
// 检查是否已存在
let blockDef = doc.blocks.itemByName(blockName);
if (!blockDef) {
// 创建块定义
blockDef = doc.blocks.add(blockName);
blockDef.basePoint = [0, 0]; // 基点
// 添加块内实体
const line1 = new LineEnt([-10, 0], [10, 0]);
const line2 = new LineEnt([0, -10], [0, 10]);
const circle = new CircleEnt([0, 0], 5);
line1.setDefaults();
line2.setDefaults();
circle.setDefaults();
blockDef.addEntity(line1);
blockDef.addEntity(line2);
blockDef.addEntity(circle);
}
插入块引用
const { InsertEnt, Engine } = vjcad;
const insert = new InsertEnt();
insert.blockId = blockDef.blockId; // 块ID(必须)
insert.insertionPoint = [50, 50]; // 插入点
insert.scaleFactor = 1.5; // 缩放因子
insert.rotation = Math.PI / 4; // 旋转角度(弧度)
insert.setDefaults();
insert.color = 5;
Engine.addEntities(insert);
炸开块
// getNestEnts(forceUpdate) 的参数是"强制重算缓存",不是"炸开";返回的是 insert 内部缓存数组
// (已按插入点/比例/旋转变换到世界坐标)——加入图纸前先 clone,别把缓存对象本身加进去
const explodedEnts = insert.getNestEnts(true).map(ent => ent.clone());
Engine.addEntities(explodedEnts);
Engine.eraseEntities(insert);
// 与 EXPLODE 命令的差别:命令还会把 0 图层 / ByBlock(color 0) 的子实体解析为继承插入块的图层/颜色,手写时需自行处理
属性:
blockId- 块ID(必须,从 blockDef.blockId 获取)insertionPoint- 插入点scaleFactor- 缩放因子rotation- 旋转角度(弧度)
方法:
getNestEnts(forceUpdate = false)- 获取(变换到世界坐标的)嵌套实体缓存;true 强制重算,返回内部数组,炸开时需 clone
符号管理
从本地存储加载符号
const {
getLocalStorageService,
parseSymbolVcadData,
mergeSymbolToDocument,
Point2D, Engine
} = vjcad;
const localService = getLocalStorageService();
// 获取分类和符号列表
const categories = await localService.getLocalSymbolCategories();
const symbols = await localService.getLocalSymbolList(categories[0].id);
const symbol = await localService.getLocalSymbol(symbols[0].id);
// 解析符号数据
const symbolDoc = await parseSymbolVcadData(symbol.vcadData);
// 合并到文档(getLocalSymbol 返回 {record, vcadData},basePoint 在 record 上)
const addedEntities = mergeSymbolToDocument(
symbolDoc,
new Point2D(100, 100), // insertPoint
symbol.record.basePoint, // basePoint: [x, y]
1.5, // scale
45, // rotation (度)
false // clearSourceInfo
);
Engine.zoomExtents();
交互式插入符号
const { insertSymbolInteractive } = vjcad;
const entities = await insertSymbolInteractive(
symbolData,
basePoint,
{
allowScale: true, // 允许指定缩放
allowRotation: true, // 允许指定旋转
clearSourceInfo: false
}
);
创建本地符号
const {
getLocalStorageService,
buildSymbolVcadData,
generateThumbnailFromEntities
} = vjcad;
const localService = getLocalStorageService();
// 创建分类
const category = await localService.createLocalSymbolCategory("我的符号");
// 创建符号内容
const entities = [line1, circle1];
// 生成符号数据
const vcadData = await buildSymbolVcadData(entities, false);
const thumbnail = await generateThumbnailFromEntities(entities, 128, 128);
// 保存符号
const symbol = await localService.saveLocalSymbol({
categoryId: category.id,
name: "十字符号",
basePoint: [0, 0],
thumbnail: thumbnail,
vcadData: vcadData
});
SplineEnt 进阶 - 拟合点与参数化
拟合点 vs 控制点
const { SplineEnt, Engine } = vjcad;
const points = [[0, 0], [50, 80], [100, 20], [150, 90], [200, 40]];
// 拟合点:曲线精确经过每个点
const fit = new SplineEnt();
fit.setFitPoints(points);
fit.setDefaults();
fit.color = 1;
// 控制点:点"拉动"曲线但不经过
const ctrl = new SplineEnt();
ctrl.setControlPoints(points);
ctrl.setDefaults();
ctrl.color = 3;
Engine.addEntities([fit, ctrl]); // 第二参是 options 不是第二个实体,必须传数组
参数化方式
| 方式 | 说明 | 适用场景 |
|---|---|---|
'chord' |
弦长参数化(默认) | 通用场景 |
'sqrtChord' |
弦长平方根(向心) | 急转弯、密集点 |
'uniform' |
均匀参数化 | 等距分布点 |
// 向心参数化
spline.setFitPoints(points, { parameterization: 'sqrtChord' });
端点切线控制
spline.setFitPoints(points, {
startTangent: [1, 0], // 起点水平向右
endTangent: [0, 1] // 终点竖直向上
});
不指定切线时使用自然端条件(端点二阶导数为零)。
拟合点 API 速查
| 方法 | 说明 |
|---|---|
setFitPoints(pts, opts?) |
设置拟合点,自动计算控制点和节点向量 |
addFitPoint(pt, opts?) |
追加一个拟合点并重新计算 ⚠️ 内部是"读当前 fitPoints→push→setFitPoints",而 setFitPoints 的插值求解器在拟合点 <4 个时静默 return——从空样条逐点 addFitPoint 永远不积累(每次求解失败、读回仍为空)。要逐点加需先 setFitPoints/addFitPoints 铺够 ≥4 个点 |
addFitPoints(pts, opts?) |
批量追加拟合点(一次给 ≥4 个即可通过求解器) |
getFitPoint(i) |
获取第 i 个拟合点 [x, y] |
getFitPoints() |
获取所有拟合点 [x, y][] |
控制点 API 速查
| 方法 | 说明 |
|---|---|
setControlPoints(pts) |
设置控制点,支持 [x,y] 或 [[x,y], weight] |
addControlPoint(pt, w?) |
追加一个控制点(链式调用) |
addControlPoints(pts) |
批量追加控制点 |
getControlPoint(i) |
获取第 i 个控制点 [x, y] |
getControlPoints() |
获取所有控制点 [x, y][] |
getControlPointsWithWeight() |
获取含权重的控制点 |
底层插值算法
const { fitPointsToControlPoints } = vjcad;
const result = fitPointsToControlPoints(
[[0,0], [50,100], [100,50], [150,100], [200,0]],
{ parameterization: 'chord' }
);
// result.controlPoints — 计算出的控制点
// result.knots — 对应的节点向量
MLeaderEnt - 多重引线
const { MLeaderEnt, Point2D, Engine } = vjcad;
const mleader = new MLeaderEnt();
mleader.setDefaults();
mleader.textContent = "标注内容"; // 注意是 textContent 不是 text
// 文字位置用 textPosition 设置(默认 (0,0)),且要在 addLeaderLine 之前设——基线朝向据此计算
mleader.textPosition = new Point2D(100, 50);
mleader.textHeight = 5;
mleader.arrowSize = 3;
// 引线顶点用 addLeaderLine + addLastVertex 构建(无 leaderPoints 数组属性)
const lineIdx = mleader.addLeaderLine(new Point2D(0, 0)); // 箭头位置
mleader.addLastVertex(lineIdx, new Point2D(50, 50)); // 中间拐点(addLastVertex 只加顶点,不决定文字位置)
Engine.addEntities(mleader);
GroupEnt - 组
const { GroupEnt, LineEnt, CircleEnt, Engine } = vjcad;
// 创建实体
const line = new LineEnt([0, 0], [100, 0]);
const circle = new CircleEnt([50, 50], 20);
line.setDefaults();
circle.setDefaults();
// 创建组
const group = new GroupEnt("MyGroup", [line, circle]);
group.setDefaults();
Engine.addEntities(group);
// 访问组内实体(items 是数组属性)
group.items.forEach((item, index) => {
console.log(`${index}: ${item.type}`);
});
// 炸开组
const explodedEntities = group.explode();
explodedEntities.forEach(ent => {
Engine.addEntities(ent);
});
Engine.eraseEntities(group);