# 文档 / 示例 / 源码命名不一致的坑

> 这一页专门收录 vjcad 的"两种 / 三种说法都对"的地方。开发时被绕进去 → 来这里翻一下。

vjcad 是高频迭代的产品，**官方文档 / 官方在线示例 / 实际类型** 偶有命名 / 签名差异。本文按"碰到的命名歧义"归档。

> ⚠️ 原则：以**示例代码（`vjcad/example/...`）能跑**为准；文档作为"理解性参考"。

## 1. `viewMode` vs `viewerMode`

| 来源 | 写法 |
|---|---|
| 文档（部分页面） | `viewerMode: true` |
| 示例 / 类型 | `viewMode: 'viewer'` |

✅ **正解**：

```js
new MainView({ viewMode: 'viewer' });   // 'full' | 'viewer'（完整编辑器 | 只读查看器）
```

`viewerMode` 在源码、`types.d.ts` 与 vjcad@1.0.16 打包产物中都不存在，传了会被静默忽略（不报错也不生效）；只能用 `viewMode`。

## 2. `entity.type` 值：`'PLINE'` vs `'LWPOLYLINE'`

实体 `type` 字段为**大写类型名**，但 `PolylineEnt` 是最容易踩的坑：

| 类 | type 字段（当前版本） |
|---|---|
| `LineEnt` | `'LINE'` |
| `CircleEnt` | `'CIRCLE'` |
| `ArcEnt` | `'ARC'` |
| `PolylineEnt` | `'PLINE'` ⚠️ **不是 `'LWPOLYLINE'`** |
| `TextEnt` | `'TEXT'` |
| `MTextEnt` | `'MTEXT'` |
| `HatchEnt` | `'HATCH'` |
| `InsertEnt` | `'INSERT'` |

`'LWPOLYLINE'` 的合法位置：**`EntityTypeFilter` 过滤器词表**（QuickSelect 相关 API 入参，与 `POLYLINE` 并列，types.d.ts 可查）与**兼容判断代码**——源码 `PatternMatchService`/`BoundaryDetector`/`TableExtractUtils` 等 6 处都有 `type === 'PLINE' || 'LWPOLYLINE'`（甚至 `|| 'POLYLINE'`）的双匹配，用于兼容 DWG 导入/旧版本数据。**运行时新建的 `PolylineEnt` 恒为 `'PLINE'`**（构造函数写死），你自己代码里的类型判断只写 `'PLINE'` 即可。

```js
Engine.getEntitiesByType('LINE');      // ✅
Engine.getEntitiesByType('PLINE');     // ✅ 多段线用这个
Engine.getEntitiesByType('LWPOLYLINE');// ❌ 当前实体永远不匹配（除非读老存档）
Engine.getEntitiesByType('Line');      // ❌ 找不到（内部 === 精确匹配，只做了 toUpperCase）
Engine.getEntitiesByType('LineEnt');   // ❌
```

## 3. `entity.id` 是 `string` 还是 `number`？

不同子类 / 不同文档说法：

- `architecture.md`：`entity.id: string`
- `api-reference.md`：`entity.id: number`

实际：**vjcad 内部是数字递增 ID**，但 **跨网络传输 / 序列化时通常字符串化**（与 `handle` 一致）。

✅ 安全做法：

```js
String(entity.id)         // 永远用字符串比较
String(otherEntity.id) === String(entity.id)
```

不要直接 `===` 比较，避免类型不一致。

## 4. `entity.move(dx, dy)` 还是 `entity.move(from, to)`

| 来源 | 用法 |
|---|---|
| 文档 `edit/edit-commands.md` | `entity.move(dx, dy)` 相对位移 |
| 示例 `edit/01move.js` | `entity.move(fromPoint, toPoint)` 绝对两点 |

✅ **以示例为准**：

```js
entity.move([0, 0], [100, 50]);     // 从原点移到 (100, 50)
```

如果你看到文档说 `move(dx, dy)`，那是历史接口，未必在最新版生效。

## 5. 坐标转换的多种入口

| 写法 | 说明 |
|---|---|
| `Engine.canvasToWcs(p)` | 顶层静态快捷（**小写**开头） |
| `Engine.trans.CanvasToWcs(p)` | `CoordinateTransform` 实例方法（**大写**开头） |
| `Engine.pcanvas.trans.CanvasToWcs(p)` | 同一条 `CoordinateTransform` 链路 |

⚠️ 注意大小写不对称：`Engine` 静态方法是 `canvasToWcs`（小写），`CoordinateTransform` 实例方法是 `CanvasToWcs`（大写，配套 `WcsToCanvas`/`DcsToWcs`/`WcsToDcs`…）。写错大小写就是 `undefined is not a function`。

```js
// 推荐：顶层
const wcs = Engine.canvasToWcs(canvasPoint);
```

不要混用，否则代码看起来"工程师不理解"。

## 6. Reactor 注册的真实入口

`EntityReactorManager` 只提供 `registerReactor(reactor: IEntityReactor, docId?)` / `unregisterReactor(reactorId, docId?)`——**没有** `entity.setReactor(r)` 也没有 `register({owner,target,onChange})` 配置对象形态（老资料里的两种写法都不存在）。

✅ 正确姿势：自定义实体实现 `IEntityReactor`（`getOwnerIds`/`updateFromOwners`/...），`Engine.reactorManager.registerReactor(myReactorEnt, docId)`。详见 `reactor/ireactor-interface.md`。

## 7. `Engine.lineTypeManager` vs `Engine.linetypeManager`

驼峰拼写不一致：

- 文档：`Engine.linetypeManager`
- 类型定义（types.d.ts）：`Engine.linetypeManager`

1.0.16 的 `types.d.ts` 里只有 `linetypeManager`（小写 t）。**统一用 `linetypeManager`**，不要写 `lineTypeManager`。

## 8. `Engine.zoomExtents()` vs `Engine.pcanvas.zoomExtents()`

二者等价 —— `Engine.zoomExtents` 是顶层静态快捷，`Engine.pcanvas`（`CanvasController`）上的同名方法也直接可调。注意是 `pcanvas` 不是 `view`：`MainView` 上没有 `zoomExtents`。

```js
Engine.zoomExtents();                // 推荐
Engine.pcanvas.zoomExtents();        // 等价
```

## 9. `LineEnt(p1, p2)` —— 位置参数是**唯一**构造形式

vjcad 实体构造函数全部是**位置参数**（首参几何、末参可选 color），**没有** `new LineEnt({ start, end })` 这类对象式签名：

```js
new LineEnt([0, 0], [100, 0]);        // ✅ 点参数支持 [x,y] 数组或 Point2D
new LineEnt({ start: [0,0] });        // ❌ 不存在——会得到 startPoint=NaN 的实体
```

**示例代码中怎么写就怎么用**，不要自创签名。

## 10. `mapbounds` 字符串 vs 数组

```js
// ❌ 数组
mapbounds: [minX, minY, maxX, maxY]

// ✅ 字符串
mapbounds: 'minX,minY,maxX,maxY'
```

文档少数早期页面写成数组形式，**实际 API 是字符串**。

## 11. `appendItems` / `prependItems` 顺序

`ContextMenuOpening` 的两个数组：

```js
events.on(CadEvents.ContextMenuOpening, args => {
    args.prependItems.push(item);     // 加到默认菜单的最前
    args.appendItems.push(item);      // 加到默认菜单的最后
});
```

注意 `prependItems` 是 **数组顺序就是显示顺序**（先 push 的更靠前）。不是 `unshift` 语义。

## 12. `setLoops`（HatchEnt 唯一正确 API）

```js
hatch.setLoops(edges);     // ✅ 当前包中唯一存在的边界 API
hatch.setBoundary(edges);  // ❌ 不存在（d.ts/运行时均无）——老资料里的写法会直接 TypeError
```

## 13. `lineType`（唯一正确写法，`linetypeName` 不存在）

| 写法 | 用途 |
|---|---|
| `entity.lineType` | **官方 get/set 属性**——读返回线型名，写接受 `CONTINUOUS`/别名/自定义名（内部转大写匹配） |
| `entity.linetypeName` | ❌ 不存在（d.ts/运行时均无），赋值只是挂了个无效字段 |

```js
line.lineType = 'HIDDEN';   // ✅
```

## 14. `EntityErasing` 与 `EntityErased`

vjcad 的事件命名：

- `EntityErasing` / `EntityErased`：枚举里有，但 vjcad@1.0.16 **从不派发**（订阅永不触发）
- 实际派发的是批量版 `EntitiesErasing`（可 `cancel`，或往 `args.excludeEntities` 里放部分实体）/ `EntitiesErased`

被某些社区文章误写成 `EntityRemoving` / `EntityDeleted`，请以 vjcad 类型 / 示例为准。

## 15. 总体策略

| 来源 | 可信度 | 用法 |
|---|---|---|
| `vjcad/example/*.js` | ⭐⭐⭐⭐⭐ | 第一优先级 |
| TypeScript 类型 | ⭐⭐⭐⭐ | 接口 / 字段 |
| 文档 (`vjcad/doc/`) | ⭐⭐⭐ | 概念解释 |
| 网上零散文章 | ⭐ | 仅参考思路 |

## 红旗

- 当文档与示例冲突时，**信示例**
- 看到驼峰大小写不一致时，**任选一种贯彻整个项目**
- 看到"参数顺序" / "签名"歧义时，先 `Object.getOwnPropertyNames(EntityClass.prototype)` 看真实方法
- 不要为"接口看起来更好看"而自创不存在的签名 —— 类型擦除后只剩字符串，编译器不会救你
- 老文档可能有效但不是"最佳实践"，新功能（如 viewMode、reactorManager）始终用新 API

## 参考（官方文档 MCP）

- 本页内容来自实际写本 skill 时遇到的歧义对照，相关文档与示例在每条小节内已标注：`vjcad/doc/guide/architecture.md`、`vjcad/doc/command/api-reference.md`、`vjcad/doc/edit/edit-commands.md`、`vjcad/doc/ui/dialogs.md`、`vjcad/doc/event/event-types.md` 等
- 示例对照：`vjcad/example/basic/04viewerremote.js`、`vjcad/example/edit/01move.js`、`vjcad/example/entity/dimension/09associative.js` 等
