> **本文回答**：撤销标记 = 把"做了一次某种类型的操作"记到栈里。常用方法：`added_undoMark / erased_undoMark / modEntity_undoMark / moved_undoMark / rotate_undoMark / scaled_undoMark / mirrored_undoMark / entLayer_undoMark / entColorIndex_undoMark / entLineType_undoMark / entLTScale_undoMark / entTransp_undoMark / clayer_undoMark / layerModified_undoMark / ucs_undoMark / plan_undoMark`。**`Engine.addEntities / eraseEntities` 等封装会自动记录，无需手动 mark**。

## 一、最常用：添加 / 删除 / 修改

```js
const u = Engine.undoManager;

// 添加 — 推荐 Engine.addEntities，自动记录
Engine.addEntities(line1);

// 或手动两步
Engine.pcanvas.addEntity(circle1);
u.added_undoMark([circle1]);

// 删除 — 推荐 Engine.eraseEntities，自动记录
Engine.eraseEntities(line1);

// 或手动
Engine.currentDoc.currentSpace.erase([line1]);
u.erased_undoMark([line1]);

// 修改属性前先记
u.modEntity_undoMark(entity);   // 记录修改前状态
entity.color = 1;
entity.setModified();
```

MCP 参考：`vjcad/doc/undo/undo-redo.md`、`vjcad/example/undo/02mark.js`。

## 二、几何变换

```js
const fromPt = new Point2D(0, 0);
const toPt   = new Point2D(40, 0);

// 移动
for (const e of entities) e.move(fromPt, toPt);
u.moved_undoMark(entities, fromPt, toPt);

// 旋转
const basePt = new Point2D(50, 50);
const ang    = Math.PI / 4;
for (const e of entities) e.rotate(basePt, ang);
u.rotate_undoMark(entities, basePt, ang);

// 缩放
for (const e of entities) e.scale(basePt, 2.0);
u.scaled_undoMark(entities, basePt, 2.0);

// 镜像
const p1 = new Point2D(0, 0), p2 = new Point2D(100, 0);
for (const e of entities) e.mirror(p1, p2);
u.mirrored_undoMark(entities, p1, p2);
```

MCP 参考：`vjcad/doc/undo/undo-redo.md`、`vjcad/example/undo/02mark.js`。

## 三、属性 / 图层 / 颜色 / 线型

```js
u.entLayer_undoMark(entities);        // 改图层前
u.entColorIndex_undoMark(entities);   // 改颜色前
u.entLineType_undoMark(entities);     // 改线型前
u.entLTScale_undoMark(entities);      // 改线型比例前
u.entTransp_undoMark(entities);       // 改透明度前

// 当前图层切换：Engine.setCurrentLayer 内部已 clayer_undoMark，不要再手动 mark（否则记两次）
Engine.setCurrentLayer('新图层');
// 只有直接赋 Engine.currentDoc.CLAYER 时才需要先手动 u.clayer_undoMark(oldLayer)

// 图层属性修改
u.layerModified_undoMark(layer);
layer.color = 1;
```

MCP 参考：`vjcad/doc/undo/undo-redo.md`。

## 四、UCS / PLAN（视图）

UCS / PLAN 是**先改值、再 mark**：`UcsUndoCommand` / `PlanViewUndoCommand` 构造时就把空间当前值记为 redo 目标，先 mark 会把旧值当成 redo 值（redo 失效）。

```js
// UCS 设置时
const oldOrg = Engine.currentSpace.UCSORG.clone();
const oldAng = Engine.currentSpace.UCSXANG;
Engine.currentSpace.UCSORG  = newOrg;
Engine.currentSpace.UCSXANG = newAng;
u.ucs_undoMark(oldOrg, oldAng);         // 改完再 mark，传旧值

// 平面视图旋转
const oldTwistAngle = Engine.currentSpace.twistAngle;
Engine.currentSpace.twistAngle = newTwistAngle;
u.plan_undoMark(oldTwistAngle);         // 改完再 mark，传旧值
```

MCP 参考：`vjcad/example/view/10ucs.js`、`11planview.js`。

## 五、撤销标记总表

| 方法 | 撤销时行为 |
|---|---|
| `added_undoMark(ents)` | 删除这些实体 |
| `erased_undoMark(ents)` | 恢复这些实体 |
| `modEntity_undoMark(ent)` | 恢复修改前状态 |
| `moved_undoMark(ents, from, to)` | 反向移动 |
| `rotate_undoMark(ents, base, angle)` | 反向旋转 |
| `scaled_undoMark(ents, base, scale)` | 反向缩放（除以 scale） |
| `mirrored_undoMark(ents, p1, p2)` | 再次镜像（自反） |
| `entLayer_undoMark(ents)` | 恢复原图层 |
| `entColorIndex_undoMark(ents)` | 恢复原颜色 |
| `entLineType_undoMark(ents)` | 恢复原线型 |
| `entLTScale_undoMark(ents)` | 恢复原比例 |
| `entTransp_undoMark(ents)` | 恢复原透明度 |
| `clayer_undoMark(oldLayer)` | 恢复原当前图层 |
| `layerModified_undoMark(layer)` | 恢复图层属性 |
| `ucs_undoMark(oldOrg, oldAng)` | 恢复 UCS |
| `plan_undoMark(oldTwistAngle)` | 恢复视图旋转 |

MCP 参考：`vjcad/doc/undo/undo-redo.md`。

## 六、`Engine.markModified` 封装

```js
Engine.markModified(entity);            // 单个，等价 modEntity_undoMark
entity.color = 1;
entity.setModified();

Engine.markModifiedBatch([e1, e2, e3]); // 批量
```

MCP 参考：`vjcad/doc/undo/undo-redo.md`。

## 反例

- 修改属性后才 mark：记录的是新值，撤销不了。快照类 mark（`modEntity_undoMark`、`entColorIndex_undoMark` 等）**必须先 mark，再改**；但 `ucs_undoMark` / `plan_undoMark` 相反——先改值再 mark（传旧值），见 §四。
- 用 `Engine.addEntities` + 手动 `added_undoMark`：双重记录（addEntities 已自动记录）。撤销按记录条数从块末尾 `pop`，会**多删掉一个原本就存在的实体**，重做又会把新实体加两次。
- 改了 `Engine.currentSpace.UCSORG` 但不 `ucs_undoMark`：`Ctrl+Z` 不会回滚 UCS。

## 参考（官方文档 MCP）

- 文档：`vjcad/doc/undo/undo-redo.md`、`vjcad/doc/undo/index.md`
- 示例：`vjcad/example/undo/02mark.js`
