entity-events
本文回答:实体事件 9 个:
EntityAdding/EntityAdded/EntitiesAdding/EntitiesAdded/EntityModified/EntityErasing/EntityErased/EntitiesErasing/EntitiesErased。-ing可取消。Adding/Erasing由args.entity携带;-Modified表示已修改;批量版传entities[]。注意:交互式命令(如 PLINE)只在第 2 个点时触发EntityAdded,后续点都走EntityModified,详见 pline-timing。
一、事件清单
| 事件 | 说明 | 可取消 |
|---|---|---|
EntityAdding |
单个添加前 | 是 |
EntityAdded |
单个添加后 | 否 |
EntitiesAdding |
批量添加前 | 是 |
EntitiesAdded |
批量添加后 | 否 |
EntityModified |
实体已修改 | 否 |
EntityErasing |
— | |
EntityErased |
— | |
EntitiesErasing |
批量删除前 | 是 |
EntitiesErased |
批量删除后 | 否 |
⚠️
EntityErasing/EntityErased单数版在发布包里没有任何派发点(emit调用为 0)——Engine.eraseEntities只发EntitiesErasing/EntitiesErased复数版。订阅单数版永远不会触发;"阻止删除"要监听EntitiesErasing并检查args.entities。
MCP 参考:vjcad/doc/event/event-types.md。
二、监听添加 / 修改 / 删除
import { Engine, CadEvents } from 'vjcad';
Engine.eventManager.on(CadEvents.EntityAdded, (args) => {
console.log('已添加:', args.entity.type, 'id=', args.entity.id);
});
Engine.eventManager.on(CadEvents.EntityModified, (args) => {
console.log('已修改:', args.entity.type, 'id=', args.entity.id);
});
Engine.eventManager.on(CadEvents.EntitiesErased, (args) => {
// ⚠️ 勿用 EntityErased(单数从不派发);复数版 args.entities / args.entityIds
args.entities.forEach(e => console.log('已删除:', e.type, 'id=', e.id));
});
MCP 参考:vjcad/example/event/01entityadded.js、02entitymodified.js。
三、阻止删除(可取消)
// ⚠️ 只有复数 EntitiesErasing 派发且可取消;单数 EntityErasing 是死事件
Engine.eventManager.on(CadEvents.EntitiesErasing, (args) => {
if (args.entities.some(e => e.layer === '保护图层')) {
args.cancel = true;
Engine.writeMessage?.('该图层禁止删除');
}
});
// 部分排除:只保护其中一部分,其余照删
Engine.eventManager.on(CadEvents.EntitiesErasing, (args) => {
args.excludeEntities = args.entities.filter(e => e.layer === '保护图层');
});
⚠️ 取消/排除后撤销栈仍会记录原数组:
Engine.eraseEntities(ents)与内置ERASE命令都是先currentSpace.erase(ents)再无条件erased_undoMark(ents),不看取消/排除结果。于是撤销栈多出一条"删除"记录:Ctrl+Z 无可见变化,随后 Ctrl+Y(pushRedo把记录里所有实体isAlive=false)会把受保护实体也删掉。自己写删除逻辑时,用const erased = Engine.currentSpace.erase(ents)拿实际删除的数组,再if (erased.length) Engine.undoManager.erased_undoMark(erased)。
MCP 参考:vjcad/doc/event/event-usage.md;撤销行为见 Engine.eraseEntities。
四、批量版事件
Engine.eventManager.on(CadEvents.EntitiesAdded, (args) => {
console.log('批量添加:', args.entities.length);
});
Engine.eventManager.on(CadEvents.EntitiesErasing, (args) => {
if (args.entities.some(e => e.layer === '保护图层')) {
args.cancel = true;
}
});
注意:
Engine.addEntities无论传单个还是数组,都逐个走pcanvas.addEntity → pushEntity,对每个实体触发EntityAdding/EntityAdded,不会触发批量版。EntitiesAdding/EntitiesAdded只由Engine.currentSpace.addEntities(arr)(BlockDefinition.addEntities)派发——期间单实体事件被suspendEvents()暂停并丢弃;但这条底层路径不记撤销、不刷新可选实体列表(见 core/document-space)。删除则只有批量版:Engine.eraseEntities/currentSpace.erase都发EntitiesErasing/EntitiesErased。
五、EntityModified 触发时机
- 直接修改属性后(
entity.color = ...)调用entity.setModified()只置_modified旗标,不直接派发——EntityModified在下一次Engine.regen()的 bucket 脏扫描时派发(emit 点全部位于 bucket/regen 内部,且经suspendEvents队列延迟到resumeEvents才送达)。所以"改属性 +setModified()+Engine.regen()"才是完整链路。 move/rotate/scale/mirror/clone等方法内部(同样经 regen 通道派发)- 交互式命令(PLINE 等)的中间步骤
- ⚠️
Engine.updateEntities/Engine.pcanvas.updateEntities均不存在(d.ts 亦无)——不要按 IDE 的习惯去调。
MCP 参考:vjcad/example/event/02entitymodified.js、vjcad/example/event/06plinepoints.js。
六、典型场景
// 自动保存 hook
let dirty = false;
Engine.eventManager.on(CadEvents.EntityAdded, () => dirty = true);
Engine.eventManager.on(CadEvents.EntityModified, () => dirty = true);
Engine.eventManager.on(CadEvents.EntitiesErased, () => dirty = true); // ⚠️ 单数 EntityErased 是死事件
// 实体添加前验证
Engine.eventManager.on(CadEvents.EntityAdding, (args) => {
if (args.entity.type === 'CIRCLE' && args.entity.radius < 0) {
args.cancel = true;
}
});
MCP 参考:vjcad/doc/event/event-usage.md。
反例
- 在
EntityAdded里读 PLINE 全部点位 → 只有最初 2 个点(详见 pline-timing)。 - 在
EntityModified里再setModified()→ 触发新一轮事件,可能死循环。 - 订阅单数
EntityErasing想阻止删除 → 永不触发;用EntitiesErasing。即便用对了,取消后撤销栈仍留下一条删除记录(见第三节),建议在更早阶段(命令CommandStarting或 UI 层)拦截。
参考(官方文档 MCP)
- 文档:
vjcad/doc/event/event-types.md、vjcad/doc/event/event-usage.md - 示例:
vjcad/example/event/01entityadded.js、02entitymodified.js