# 编辑、选择与撤销

实体编辑、选择集操作、撤销系统。

## 实体变换

### 移动

```javascript
const { LineEnt, Point2D, Engine } = vjcad;

const line = new LineEnt([0, 0], [100, 0]);
line.setDefaults();
Engine.addEntities(line);

// 移动实体：从 from 点移动到 to 点
line.move([0, 0], [50, 30]);  // 支持 [x, y] 数组形式

// 必须重绘才能看到变化
Engine.regen();
```

### 复制

```javascript
// 使用 clone() 创建独立副本
const original = new LineEnt([0, 0], [100, 0]);
original.setDefaults();
Engine.addEntities(original);

const copy = original.clone();
copy.move([0, 0], [0, 50]);  // 移动副本
copy.color = 2;               // 修改副本属性
Engine.addEntities(copy);     // 添加副本

Engine.regen();
```

### 旋转

```javascript
const { Point2D, Engine } = vjcad;

// rotate(basePoint, angle) - 绕基点旋转（角度为弧度）
const basePoint = new Point2D(50, 50);  // PointInput：Point2D 或 [x,y] 数组均可
entity.rotate(basePoint, Math.PI / 4);  // 旋转 45°

Engine.regen();
```

### 缩放

```javascript
const { Point2D, Engine } = vjcad;

// scale(basePoint, scaleFactor) - 以基点为中心缩放
const basePoint = new Point2D(0, 0);
entity.scale(basePoint, 0.5);  // 缩小到 0.5 倍
entity.scale(basePoint, 2.0);  // 放大到 2 倍

Engine.regen();
```

### 镜像

```javascript
const { Point2D, Engine } = vjcad;

// mirror(p1, p2) - 沿 p1-p2 定义的直线镜像
const p1 = new Point2D(100, 0);
const p2 = new Point2D(100, 100);
entity.mirror(p1, p2);  // 沿 x=100 垂直线镜像

Engine.regen();
```

**注意**：
- `move()`/`rotate()`/`scale()`/`mirror()` 的点参数均为 `PointInput`——`Point2D` 对象或 `[x, y]` 数组均可
- 变换后必须调用 `Engine.regen()` 才能看到变化

---

## 删除实体

```javascript
const { Engine } = vjcad;

// 删除单个实体
Engine.eraseEntities(entity);

// 删除多个实体
Engine.eraseEntities([entity1, entity2, entity3]);

// 删除后 entity.isAlive 变为 false
console.log(entity.isAlive);  // false
```

---

## 绘图顺序

```javascript
const { Engine } = vjcad;

// 方式1：使用命令
await Engine.editor.executerWithOp('ENTDRAWFRONT');  // 选中图形置于最前
await Engine.editor.executerWithOp('ENTDRAWBACK');   // 选中图形置于最后
await Engine.editor.executerWithOp('LAYDRAWFRONT');  // 选中图形的图层置于最前
await Engine.editor.executerWithOp('LAYDRAWBACK');   // 选中图形的图层置于最后

// 方式2：直接操作 items 数组
const items = Engine.currentSpace.items;
const idx = items.indexOf(entity);
if (idx > -1) {
    items.splice(idx, 1);  // 移除
    items.push(entity);    // 添加到末尾（最前面）
    Engine.pcanvas.regen(true);
}
```

---

## 分组

```javascript
const { GroupEnt, LineEnt, CircleEnt, Engine } = vjcad;

// 创建实体
const line = new LineEnt([0, 0], [100, 0]);
const circle = new CircleEnt([50, 50], 20);
line.setDefaults();
circle.setDefaults();

// 创建组
const group = new GroupEnt("MyGroup", [line, circle]);
group.setDefaults();
Engine.addEntities(group);

// 访问组内实体（items 是数组属性，另有 count 属性可取数量）
group.items.forEach((item, index) => {
    console.log(`${index}: ${item.type}, 颜色: ${item.color}`);
});

// 炸开组
const explodedEntities = group.explode();
explodedEntities.forEach(ent => {
    Engine.addEntities(ent);
});
Engine.eraseEntities(group);
Engine.regen(true);

// 使用命令
await Engine.editor.executerWithOp('GROUP');    // 创建组
await Engine.editor.executerWithOp('UNGROUP');  // 取消组
```

---

## 高亮

```javascript
const { Engine } = vjcad;

// 高亮指定实体
Engine.highLightEntities([entity1, entity2]);

// 清除高亮
Engine.clearHighLight();

// 高亮是临时视觉效果，不影响实体属性
```

---

## 选择集操作

### 设置选择集

```javascript
const { Engine } = vjcad;

// 选择单个实体
Engine.ssSetFirst([entity]);

// 选择多个实体
Engine.ssSetFirst([entity1, entity2, entity3]);

// 清空选择集
Engine.ssSetFirst([]);

// 选择所有实体（getEntities 含已删除实体，须过滤 isAlive）
const allEntities = Engine.getEntities(ent => ent.isAlive);
Engine.ssSetFirst(allEntities);
```

### 获取选择集

```javascript
const { Engine } = vjcad;

// 获取当前选择集
const selected = Engine.ssGetFirst();

console.log(`选中 ${selected.length} 个实体`);

// 遍历选择集
selected.forEach((entity, index) => {
    console.log(`${index + 1}. 类型: ${entity.type}, 颜色: ${entity.color}`);
});

// 对选择集进行操作
selected.forEach(entity => {
    entity.color = 1;
});
Engine.regen();
```

---

## 获取实体

### 获取所有实体

```javascript
const { Engine } = vjcad;

// getEntities() 返回 currentSpace.items 原样——含已删除实体（isAlive=false）
const allEntities = Engine.getEntities(ent => ent.isAlive);   // 或 Engine.currentSpace.aliveItems
```

**注意**：下面的 `getEntitiesByType` / 条件过滤同样含已删除实体，实际使用时条件里带上 `ent.isAlive`（或对结果 `.filter(e => e.isAlive)`）。

### 按类型获取

```javascript
const { Engine } = vjcad;

const lines = Engine.getEntitiesByType('LINE');
const circles = Engine.getEntitiesByType('CIRCLE');
const arcs = Engine.getEntitiesByType('ARC');
const plines = Engine.getEntitiesByType('PLINE');
const texts = Engine.getEntitiesByType('TEXT');
const mtexts = Engine.getEntitiesByType('MTEXT');
const inserts = Engine.getEntitiesByType('INSERT');
const hatches = Engine.getEntitiesByType('HATCH');
```

### 按条件过滤

```javascript
const { Engine } = vjcad;

// 按颜色过滤
const redEntities = Engine.getEntities(ent => ent.color === 1);

// 按图层过滤
const layerAEntities = Engine.getEntities(ent => ent.layer === "图层A");

// 组合条件
const redLines = Engine.getEntities(ent => 
    ent.type === "LINE" && ent.color === 1
);

// 自定义复杂条件
const largeCircles = Engine.getEntities(ent => 
    ent.type === "CIRCLE" && ent.radius > 50
);

// 排除某些实体
const notHidden = Engine.getEntities(ent => 
    ent.layer !== "隐藏图层"
);
```

---

## 撤销系统

### 基本撤销/重做

```javascript
const { Engine } = vjcad;

const undoMgr = Engine.undoManager;

// 检查是否可撤销/重做
if (undoMgr.canUndo()) {
    undoMgr.undo();
}

if (undoMgr.canRedo()) {
    undoMgr.redo();
}
```

### 撤销标记

```javascript
const { Engine, Point2D } = vjcad;

const undoMgr = Engine.undoManager;

// 添加/删除实体的撤销标记——仅在用底层 Engine.pcanvas.addEntity / currentSpace.erase 时手动补；
// Engine.addEntities / eraseEntities 已自动记录；重复 added_undoMark 会在撤销时多删一个无关实体
undoMgr.added_undoMark([entity1, entity2]);
undoMgr.erased_undoMark([entity1, entity2]);

// 移动操作的撤销标记
const fromPt = new Point2D(0, 0);
const toPt = new Point2D(40, 0);
entity.move(fromPt, toPt);
undoMgr.moved_undoMark([entity], fromPt, toPt);

// 旋转操作的撤销标记
undoMgr.rotate_undoMark(entities, basePoint, angle);

// 缩放操作的撤销标记
undoMgr.scaled_undoMark(entities, basePoint, scale);
```

**注意**：`Engine.addEntities()` 和 `Engine.eraseEntities()` 会自动记录撤销。

### 撤销分组

将多个操作合并为一次撤销。

```javascript
const { Engine, LineEnt, CircleEnt, Point2D } = vjcad;

const undoMgr = Engine.undoManager;

// 开始撤销组
undoMgr.start_undoMark();

try {
    // 执行多个操作
    const line = new LineEnt([0, 0], [100, 0]);
    line.setDefaults();
    Engine.addEntities(line);
    
    const circle = new CircleEnt([50, 50], 30);
    circle.setDefaults();
    Engine.addEntities(circle);
    
    // 移动操作
    line.move([0, 0], [50, 0]);
    undoMgr.moved_undoMark([line], new Point2D(0, 0), new Point2D(50, 0));
    
} finally {
    // 结束撤销组（必须确保调用）
    undoMgr.end_undoMark();
}

// 撤销时会一次性撤销整个组内的所有操作
```

---

## 视图操作

### 缩放

```javascript
const { Engine } = vjcad;

// 缩放到全图
Engine.zoomExtents();

// 缩放到指定实体
Engine.zoomToEntities([entity1, entity2]);

// 带边距的缩放
Engine.zoomToEntities(entities, {
    padding: { top: 50, bottom: 50, left: 50, right: 50 }
});
```

### 刷新显示

```javascript
const { Engine } = vjcad;

// render() - 最底层渲染，只执行 GPU 绘制
Engine.render();

// redraw() - 更新视图变换后渲染
Engine.redraw();

// regen() - 局部更新，只重绘已修改的实体（快）
Engine.regen();

// regen(true) - 完全重绘所有图形（慢）
Engine.regen(true);
```

### 预览

```javascript
const { Engine } = vjcad;

// 绘制预览实体
Engine.drawPreviewEntity(entity);
Engine.drawPreviewEntities([entity1, entity2]);

// 清除预览
Engine.clearPreview();
```

### 设置视图中心

```javascript
const { Engine, Point2D } = vjcad;

// 设置视图中心点
Engine.setCenter(new Point2D(100, 100));

// 带边距的设置
Engine.setCenter(new Point2D(100, 100), true, {
    top: 50, bottom: 50, left: 50, right: 50
});
```

### 背景色

```javascript
const { Engine } = vjcad;

// 设置背景灰度值 0-255：0 与 255 是特例，其余值为 rgb(v,v,v)
Engine.setBgc(0);     // 默认深色背景 #212830（不是纯黑）
Engine.setBgc(33);    // rgb(33,33,33) 深灰（不是默认深色背景）
Engine.setBgc(1);     // 近似纯黑 rgb(1,1,1)
Engine.setBgc(255);   // 纯白 #FFFFFF

Engine.redraw();
```

### 主题

```javascript
const { Engine } = vjcad;

// 切换主题（THEME_MODE 的 setter 已自动调用 setBgc(0/255)，无需再手动 setBgc；
// 若再 setBgc(33) 反而会把默认深色背景 #212830 覆盖成灰色）
Engine.THEME_MODE = 0;   // 深色主题
Engine.redraw();

Engine.THEME_MODE = 1;   // 浅色主题
Engine.redraw();

// 检查当前主题
const isDark = Engine.isDarkTheme();
```

### 屏幕范围

```javascript
const { Engine } = vjcad;

// 获取屏幕边界（世界坐标系）
const bounds = Engine.getScreenBoundsWcs();
// 返回: BoundingBox 类实例——{ pt1, pt2, minX, minY, maxX, maxY, width, height, center, ... }

// 获取屏幕内的实体
const screenEntities = Engine.getScreenEntities();
const expandedEntities = Engine.getScreenEntities(1.5);  // 扩展 1.5 倍范围
```

---

## 缩放因子

```javascript
const { Engine } = vjcad;

// 获取当前缩放比例
const zoom = Engine.currentSpace.zoom;

// 设置滚轮缩放因子（百分比，默认 75）：每格滚轮 zoom 除/乘以 ZOOMFACTOR/100
// 值越小每格缩放越大（越快）；100 = 滚轮不缩放；ZOOMFACTOR 命令只接受 ≥60
Engine.ZOOMFACTOR = 60;   // 缩放更快（每格约 1.67 倍）
Engine.ZOOMFACTOR = 90;   // 缩放更慢（每格约 1.11 倍）
```
