> **本文回答**（**重要陷阱**）：PLINE / SPLINE 这类交互式命令"边输入边构建"，事件触发时机分阶段：用户点第 2 个点 → 触发 `EntityAdded`（**只 2 个点**）；后续点 3, 4, 5… → 触发 `EntityModified`；按回车结束 → 触发 `CommandEnded`（**完整点数**）。在 `EntityAdded` 里读 `bulgePoints.items` 拿不到全部数据。

## 一、事件时序图

```
用户输入                  触发的事件
──────────────────────────────────────────────
点击第 1 个点               (无事件，只在内存)
点击第 2 个点               EntityAdded（实体 ID 诞生，bulgePoints = 2 个点）
点击第 3 个点               EntityModified（bulgePoints = 3 个点）
点击第 4 个点               EntityModified（bulgePoints = 4 个点）
...
回车结束                    CommandEnded（最终状态）
ESC 取消                    CommandEnded（同样只发 Ended；CommandCancelled 不派发）
                            （已过第 2 点则保留已绘制部分，否则完全无实体）
```

MCP 参考：`vjcad/example/event/06plinepoints.js`、`vjcad/doc/event/event-usage.md`。

## 二、错与对

```js
// 错：在 EntityAdded 拿不到全部点
Engine.eventManager.on(CadEvents.EntityAdded, (args) => {
  if (args.entity.type === 'PLINE') {
    console.log(args.entity.bulgePoints.items.length);  // 输出: 2（不完整）
  }
});

// 对（方案 A）：在 CommandEnded 拿全部
Engine.eventManager.on(CadEvents.CommandEnded, (args) => {
  if (args.commandName === 'PLINE') {
    const plines = Engine.getEntities(e => e.type === 'PLINE');
    const last   = plines[plines.length - 1];
    console.log(last.bulgePoints.items.length);          // 输出: 完整点数
  }
});

// 对（方案 B）：实时跟踪
Engine.eventManager.on(CadEvents.EntityModified, (args) => {
  if (args.entity.type === 'PLINE') {
    console.log('当前点数:', args.entity.bulgePoints.items.length);
  }
});
```

MCP 参考：`vjcad/doc/event/event-usage.md`、`vjcad/example/event/06plinepoints.js`。

## 三、读点位的两种数据形态对照

| 属性 | 实体对象 | `toDb()` 输出 |
|---|---|---|
| 点位集合 | `entity.bulgePoints.items` | `dbPline.dbBulgePoints.pts` |
| 单个点坐标 | `bp.point2d.x / .y` | `pts[i][0] / pts[i][1]` |
| 凸度 | `bp.bulge` | `bulges?[i]`（全 0 时省略） |

```js
// 实体对象
const pl = Engine.getEntities(e => e.type === 'PLINE').slice(-1)[0];
pl.bulgePoints.items.forEach((bp, i) => {
  console.log(`P${i}:`, bp.point2d.x, bp.point2d.y, 'bulge=', bp.bulge);
});

// toDb 输出
const dbDoc = Engine.currentDoc.toDb();
// dbBlocks.items 是数组（DbBlock[]），按 name 找模型空间块
const model = dbDoc.dbBlocks.items.find(b => b.name === '*Model');
const dbPl  = model.items.find(it => it.type === 'PLINE');

const pts    = dbPl.dbBulgePoints.pts;          // [[x,y], ...]
const bulges = dbPl.dbBulgePoints.bulges;       // [b1, b2, ...] | undefined

pts.forEach((pt, i) => {
  const b = bulges ? bulges[i] : 0;
  console.log(`pt${i}: [${pt[0]}, ${pt[1]}] bulge=${b}`);
});

// 从 DB 数据还原
const np = new PolylineEnt(new BulgePoints());
np.fromDb(dbPl);
```

MCP 参考：`vjcad/doc/event/event-usage.md`、`vjcad/example/event/06plinepoints.js`。

## 四、追踪当前 active 实体

```js
let activePline = null;

Engine.eventManager.on(CadEvents.CommandStarted, (args) => {
  if (args.commandName === 'PLINE') activePline = null;
});

Engine.eventManager.on(CadEvents.EntityAdded, (args) => {
  if (args.entity.type === 'PLINE') activePline = args.entity;
});

Engine.eventManager.on(CadEvents.EntityModified, (args) => {
  if (args.entity === activePline) {
    console.log('当前点数:', activePline.bulgePoints.items.length);
  }
});

// 回车完成与 ESC 取消都只触发 CommandEnded（CommandCancelled 不派发），在这里统一分支
Engine.eventManager.on(CadEvents.CommandEnded, (args) => {
  if (args.commandName === 'PLINE') {
    if (activePline) {
      console.log('结束，保留实体点数:', activePline.bulgePoints.items.length);
    } else {
      console.log('结束，未创建实体（第 2 点前就退出）');
    }
  }
  activePline = null;
});
```

MCP 参考：`vjcad/example/event/06plinepoints.js`。

## 五、其他可能受同种时序影响的命令

`PLINE` 这种"边交互边构建实体"的命令同类型有：

- `SPLINE`（样条曲线）
- `MTEXT`（多行文字）
- 部分 `DIMENSION*` 标注命令
- 自定义命令中通过 `getPoint(callback)` 实时预览的实体

凡是这类命令，都按 `CommandEnded` 兜底比较可靠。

## 反例

- 在 `EntityAdded` 弹 toast "已添加 PLINE，点数 N"：永远显示 2。
- `EntityModified` 没过滤 active 实体，把页面上所有 PLINE 的修改都误认为是"当前命令的"。
- 用 `entity.id` 做去重关键：可以，但 `EntityAdded` 时拿到的 `id` 与 `CommandEnded` 时是同一个 id，请确保不要在 `EntityAdded` 里做"我的版本号 = 0"这样的状态依赖。

## 参考（官方文档 MCP）

- 文档：`vjcad/doc/event/event-usage.md`
- 示例：`vjcad/example/event/06plinepoints.js`
