关联标注(最常用反应器场景)
示例:
vjcad/example/reactor/02dimension.js文档:vjcad/doc/reactor/index.md、vjcad/doc/command/dimension-commands.md
vjcad 标注实体(LinearDimensionEnt、AlignedDimensionEnt、RadialDimensionEnt 等)已经内置了 IEntityReactor。直接调用 dim.setSourceEntities(...) / dim.setSourceEntity(...) 就能让标注随源实体自动更新,无需自己实现接口。方法名按标注类型不同:
| 标注类 | 关联方法 |
|---|---|
LinearDimensionEnt / AlignedDimensionEnt |
setSourceEntities(startId, startSegIdx, endId, endSegIdx, startPointType?, endPointType?, startT?, endT?) |
TwoLineAngularDimensionEnt / AngleDimensionEnt |
setSourceEntities(line1Id, line1SegIdx, line2Id, line2SegIdx) |
ThreePointAngularDimensionEnt |
setSourceEntities(e1Id, e1SegIdx, p1Type, e2Id, e2SegIdx, p2Type, centerId?, centerType?, t1?, t2?, tc?) |
RadialDimensionEnt / DiametricDimensionEnt |
setSourceEntity(circleId, pointAngle?)(单数) |
ArcDimensionEnt |
setSourceEntity(arcId)(单数) |
OrdinateDimensionEnt |
setSourceEntity(entityId, pointType?)(单数) |
1. setSourceEntities 签名
dim.setSourceEntities(
startLineId, startSegIdx, // 起点关联实体 ID + 线段下标(LINE 忽略下标,传 -1 即可;PLINE 必须 ≥0)
endLineId, endSegIdx,
startPointType, endPointType, // 'start' | 'end' | 'midpoint' | undefined(类型里有 'center' 但未实现,回退为起点)
startParameterT, endParameterT // 可选:0~1 曲线参数(更精细的位置)
);
注意:必须 先
Engine.addEntities(dim),再调用setSourceEntities。否则标注还没拿到 docId,注册不到反应器表。只支持
LINE与PLINE的直线段作为 Linear/Aligned 的源:PLINE按segIdx取getSubEnts()[segIdx],该段须是直线(segIdx=-1或弧段 → 取不到点,标注不更新);ARC/CIRCLE 等其他类型同样取不到点。
2. 标准用法:水平线性标注关联到一段直线
const line1 = new LineEnt([0, 0], [100, 0]);
line1.setDefaults();
Engine.addEntities(line1);
const dim = new LinearDimensionEnt(
new Point2D(0, 0), // 测量起点
new Point2D(100, 0), // 测量终点
new Point2D(50, 25), // 标注线位置点
0 // 标注角度(弧度,0=水平)
);
dim.setDefaults();
Engine.addEntities(dim);
dim.setSourceEntities(
line1.id, -1, // 起点关联整条 line1
line1.id, -1,
'start', // 起点取 line1 的起点
'end' // 终点取 line1 的终点
);
之后 line1.move(...) / 修改端点,dim 会在下一次 Engine.regen() 时重新测量(见下方说明)。
⚠️ 联动链路是
owner.setModified()(只置_modified旗标)→Engine.regen()(bucket 脏扫描时派发EntityModified)→ 反应器桥notifyOwnerChanged→dim.setReactorDirty()→updateDirtyReactors()/buildNestEnts时updateFromOwners()重算测量值。只改属性不regen不会联动;Engine.updateEntities/Engine.pcanvas.updateEntities均不存在,勿用。也可手动ReactorNotifier.notifyModified(entity)直接驱动。
3. 对齐标注关联斜线
const line2 = new LineEnt([0, -60], [80, -100]);
line2.setDefaults(); Engine.addEntities(line2);
const aligned = new AlignedDimensionEnt(
new Point2D(0, -60),
new Point2D(80, -100),
new Point2D(50, -65)
);
aligned.setDefaults(); Engine.addEntities(aligned);
aligned.setSourceEntities(line2.id, -1, line2.id, -1, 'start', 'end');
AlignedDimensionEnt 自动按两端点连线方向出标注。
4. 标注跨越两条线段(端点 → 端点)
const line3 = new LineEnt([150, 0], [200, 40]);
const line4 = new LineEnt([200, 40], [260, 0]);
[line3, line4].forEach(l => { l.setDefaults(); Engine.addEntities(l); });
const span = new LinearDimensionEnt(
new Point2D(150, 0),
new Point2D(260, 0),
new Point2D(205, -25),
0
);
span.setDefaults(); Engine.addEntities(span);
span.setSourceEntities(
line3.id, -1, // 起点 → line3 的 start
line4.id, -1, // 终点 → line4 的 end
'start',
'end'
);
5. 用 parameterT 精确定位(线段中段)
pointType 拿不到的位置(例如线段 1/4 处),用 parameterT(0~1):
const line5 = new LineEnt([0, -150], [120, -150]);
line5.setDefaults(); Engine.addEntities(line5);
const partial = new LinearDimensionEnt(
new Point2D(30, -150), // 1/4 处
new Point2D(90, -150), // 3/4 处
new Point2D(60, -130),
0
);
partial.setDefaults(); Engine.addEntities(partial);
partial.setSourceEntities(
line5.id, -1,
line5.id, -1,
undefined, undefined, // 不使用 pointType
0.25, // startParameterT
0.75 // endParameterT
);
完整 4 个示例都在
vjcad/example/reactor/02dimension.js。
6. 检查关联状态
console.log(dim.isAssociative); // 仅表示 _ownerRefs 非空(调过 setSourceEntities 且 id≥0),不校验源实体存在/有效
dim.getSourceEntityRefs(); // 查看关联引用
// 读取联动后的测量值:惰性更新,先通知再 regen,然后读公开方法 getMeasurement()
line1.endPoint = new Point2D(150, 0);
ReactorNotifier.notifyModified(line1); // 让 dim 立即标脏
Engine.regen(); // regen 开头 updateDirtyReactors() → updateFromOwners()
console.log(dim.getMeasurement()); // 150
红旗
- 顺序很重要:先
Engine.addEntities(dim),再dim.setSourceEntities(...) - ⚠️ 纠正:
Engine.eraseEntities(owner)首次删除并不通知反应器——EntityErased单数事件从不派发(源码 0 个 emit 点),ReactorNotifier.notifyErased只挂在 ERASE 撤销记录的pushRedo里,即只有 undo 后再 redo 才触发unlinkAllOwners。正常 erase 后dim.isAssociative仍为true、_ownerRefs残留死引用——需要清理时自己dim.unlinkAllOwners()或在EntitiesErased(复数,正常派发)事件里手动通知 - 修改源实体后标注是惰性同步更新(不是异步):
Engine.regen()开头的updateDirtyReactors()才调updateFromOwners();而EntityModified在 regen 的 bucket 扫描里才派发,晚于这一步——所以要么先ReactorNotifier.notifyModified(owner)再Engine.regen(),要么 regen 两次,之后读公开的dim.getMeasurement() setSourceEntities接收的是 number 类型的 entity.id(d.ts:line1Id: number),不要传entity对象本身pointType: 'midpoint'在多段线上是segIdx那一段的中点,单线段上等价于parameterT: 0.5;parameterT优先于pointType;'center'未实现(落到 default 分支取起点)
参考(官方文档 MCP)
- 示例:
vjcad/example/reactor/02dimension.js - 文档:
vjcad/doc/reactor/index.md、vjcad/doc/command/dimension-commands.md