唯杰地图 logo唯杰地图
VJMAP
EN

entity-events

vjcad/references/event/entity-events.md 6.2 KB117 行
原文

本文回答:实体事件 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