> **本文回答**：自定义实体继承 `CustomEntityBase`，必须实现 `customType / customDisplayName / getSnapPoints / getGripPoints / buildNestEnts / clone / fromDb / getEntityData / setEntityData`；构造函数必须可**无参**调用，并用 `CustomEntityRegistry.getInstance().register(type, Class)` 注册，否则保存后重开无法还原。完整示例见 `vjcad/example/entity/network/01create.js`。

## 一、`CustomEntityBase`（来自架构图）

> 引自 `vjcad/doc/guide/architecture.md`：

```ts
abstract class CustomEntityBase extends EntityBase {
  abstract readonly customType: string;         // 子类必须声明（readonly）
  abstract readonly customDisplayName: string;  // 属性栏显示名（readonly）

  abstract getSnapPoints(): SnapPointResult[];   // [{point, type:'endpoint'|...}]，不是 Point2D[]
  abstract getGripPoints(): GripPointResult[];   // [{point, gripId, type:'move'|'stretch'|...}]，不是 Point2D[]
  protected abstract buildNestEnts(): EntityBase[];  // 子类实现，返回值被基类缓存（getNestEnts）
  abstract clone(): CustomEntityBase;            // 复制 / 粘贴 / 撤销快照用
  abstract fromDb(dbData: CustomEntityDbData): void;  // 打开图纸时反序列化（EntityBase 上没有默认实现）
  abstract getEntityData(): any;
  abstract setEntityData(data): void;
}
```

注：`clone`/`fromDb` 同为 abstract（官方架构图里漏列）。

继承图：`EntityBase <|-- CustomEntityBase <|-- UserCustomEntity`。

## 二、最小自定义实体（伪代码骨架）

```js
class MyArrowEntity extends CustomEntityBase {
  get customType()        { return 'MyArrow'; }   // 官方示例用 getter 声明
  get customDisplayName() { return '箭头'; }

  // 必须可无参构造：打开图纸时注册表先无参实例化子类，再调 fromDb()
  constructor() {
    super();
    this.start = new Point2D(0, 0);
    this.end   = new Point2D(0, 0);
  }

  // 内嵌真实实体（vjcad 用这些去渲染、抛事件）
  buildNestEnts() {
    const line = new LineEnt(this.start, this.end);
    line.setDefaults();

    // 箭头三角形（用 SolidEnt 或 PolylineEnt 都可）
    const head = new SolidEnt(/* p1, p2, p3 箭头三点，计算略 */);

    return [line, head];
  }

  // 返回 {point, type} / {point, gripId, type} 对象，不是裸 Point2D
  getSnapPoints() {
    return [
      { point: this.start, type: 'endpoint' },
      { point: this.end,   type: 'endpoint' },
    ];
  }
  getGripPoints() {
    return [
      { point: this.start, gripId: 'start', type: 'stretch' },
      { point: this.end,   gripId: 'end',   type: 'stretch' },
    ];
  }

  clone() {
    const c = new MyArrowEntity();
    c.fromDefaultProps(this);            // 复制图层 / 颜色等通用属性
    c.start = this.start.clone();
    c.end   = this.end.clone();
    return c;
  }

  // 存纯 {x,y}，读回时还原成 Point2D
  getEntityData() { return { start: { x: this.start.x, y: this.start.y }, end: { x: this.end.x, y: this.end.y } }; }
  setEntityData(d) {
    if (d.start) this.start = new Point2D(d.start.x, d.start.y);
    if (d.end)   this.end   = new Point2D(d.end.x, d.end.y);
    this.setModified();
  }
  fromDb(db) { this.fromDbDefaultProps(db); if (db.data) this.setEntityData(db.data); }
}

// 注册（页面加载时、打开图纸前执行）；入口是 CustomEntityRegistry.getInstance()，Engine.customEntities 不存在
CustomEntityRegistry.getInstance().register('MyArrow', MyArrowEntity);   // 与 customType 一致

// 使用：无参构造后设字段
const arrow = new MyArrowEntity();
arrow.start = new Point2D(0, 0);
arrow.end   = new Point2D(100, 0);
arrow.setDefaults();
Engine.addEntities(arrow);
```

## 三、典型扩展点

| 想做的 | 重写 |
|---|---|
| 改变实体怎么画 | `buildNestEnts()`（返回内嵌真实实体集合） |
| 改变捕捉点 / 夹点 | `getSnapPoints / getGripPoints` |
| 自定义保存格式 | `getEntityData / setEntityData` |
| 自定义碰撞 / 包围盒 | `boundingBox()`（vjcad@1.0.16 d.ts 已在实体类上声明，返回 `BoundingBox`） |

## 四、注册

① `CustomEntityRegistry.getInstance().register(customType, Class)`；② `Engine.addEntities(myEnt)`；③ 打开图纸时 `CadDocument` 调 `CustomEntityRegistry.getInstance().createFromDb(dbData)`：按 `customType` 找到类 → **无参**实例化 → `entity.fromDb(dbData)`。未注册的类型会打印警告并跳过，所以注册必须在打开图纸之前完成。参考 `vjcad/example/entity/network/01create.js`。

## 反例

- 不重写 `buildNestEnts`：自定义实体在画布上看不见。
- 构造函数要求必传参数（如 `constructor(start, end)` 且直接用）：反序列化时注册表无参实例化，字段为 undefined，重开图纸就坏。
- 漏实现 `clone` / `fromDb` 或忘了 `register`：复制、撤销或保存后重开时无法还原。
- 把"业务字段"放在普通属性：保存 / 加载后会丢，应放在 `getEntityData / setEntityData`。
- `customType` 与已有内置类型重名：触发未定义行为。

## 参考（官方文档 MCP）

- 文档：`vjcad/doc/entity/custom-entities.md`、`vjcad/doc/guide/architecture.md`
