# 撤销/重做系统

撤销标记、撤销组、嵌套撤销、各类操作的撤销模式。

---

## 核心规则

**`start_undoMark()` / `end_undoMark()` 只负责把多条撤销记录合并为一步**；未包裹时每条记录单独成一步，仍可撤销。

**`Engine.addEntities()` / `Engine.eraseEntities()` 已自带撤销记录，切勿再手动 `added_undoMark` / `erased_undoMark`**（撤销实现为从块末尾 `pop`，重复记录会多删一个原有实体）。只有用 `Engine.pcanvas.addEntity` / `currentSpace.erase` 等底层 API 才手动补记。不需要撤销时传 `{ recordUndo: false }`：`Engine.addEntities(ents, { recordUndo: false })`、`Engine.eraseEntities(ents, { recordUndo: false })`。

---

## 基本用法

```javascript
const { Engine } = vjcad;
const undoMgr = Engine.undoManager;

// 字符串简写（最常用）
undoMgr.start_undoMark('绘制图形');
try {
    // ... 创建/修改/删除实体 ...
    Engine.addEntities(entity);   // 自带撤销记录，无需再 added_undoMark
} finally {
    undoMgr.end_undoMark();
}
```

---

## 嵌套撤销（推荐在命令中使用）

```javascript
const { Engine } = vjcad;

Engine.startUndoRecord('我的操作');   // 支持嵌套，只有最外层生效
// ... 操作 ...
Engine.endUndoRecord();
```

---

## 新增实体的撤销

```javascript
undoMgr.start_undoMark('添加实体');
try {
    const line = new LineEnt([0, 0], [100, 0]);
    line.setDefaults();
    Engine.addEntities(line);   // 已自动记录撤销
} finally {
    undoMgr.end_undoMark();
}
```

**注意**：`Engine.addEntities()` 会自动记录撤销，**不要**再手动调用 `added_undoMark`（双重记录会让 Ctrl+Z 多删一个原有实体、重做时新实体被压入两次）。只有用 `Engine.pcanvas.addEntity(ent)` 底层入库时才需要手动补记：

```javascript
Engine.pcanvas.addEntity(line);          // 底层 API，不记撤销
undoMgr.added_undoMark([line]);          // 手动补记
```

---

## 修改已有实体的撤销

```javascript
undoMgr.start_undoMark('修改颜色');
try {
    Engine.markModified(entity);    // 先标记（记录修改前状态）
    entity.color = 1;               // 再修改属性
    entity.layer = "新图层";
    entity.setModified();           // 通知修改完成
} finally {
    undoMgr.end_undoMark();
}
```

---

## 删除实体的撤销

```javascript
undoMgr.start_undoMark('删除实体');
try {
    Engine.eraseEntities(entities);      // 自带 erased_undoMark，勿重复记录
} finally {
    undoMgr.end_undoMark();
}
```

---

## 变换操作的撤销

```javascript
const { Point2D } = vjcad;

undoMgr.start_undoMark('移动');
try {
    const from = new Point2D(0, 0);
    const to = new Point2D(50, 0);
    entities.forEach(ent => ent.move(from, to));
    undoMgr.moved_undoMark(entities, from, to);
    Engine.regen();
} finally {
    undoMgr.end_undoMark();
}
```

其他变换撤销标记：

```javascript
undoMgr.rotate_undoMark(entities, basePoint, angle);
undoMgr.scaled_undoMark(entities, basePoint, scale);
undoMgr.mirrored_undoMark(entities, p1, p2);
```

---

## 撤销/重做操作

```javascript
undoMgr.undo();                             // 撤销一步
undoMgr.redo();                             // 重做一步
undoMgr.undoSteps(3);                       // 撤销 3 步
undoMgr.undoSteps('绘制图形');              // 按名称撤销到最近匹配项
undoMgr.redoSteps('绘制图形');              // 按名称重做
undoMgr.undoSteps(3, { silent: true });     // 静默撤销（不触发消息）
```

---

## 检查状态

```javascript
if (undoMgr.canUndo()) { undoMgr.undo(); }
if (undoMgr.canRedo()) { undoMgr.redo(); }
Engine.isInUndoRecord();   // 是否在撤销记录中
```
