# 反应器系统

反应器用于实时响应实体变化并自动执行操作。

## 概念

反应器是一种事件驱动的机制，可以：
- 监听实体的添加、修改、删除
- 自动响应变化并执行关联操作
- 实现实体间的依赖关系（如关联标注）

## 自定义反应器

通过事件监听实现自定义反应逻辑。

```javascript
const { CadEventManager, CadEvents, Engine, CircleEnt, PolylineEnt } = vjcad;

class CircleBoundingBoxReactor {
    constructor() {
        this.enabled = true;
        this.boundingLines = new Map();  // 存储关联数据
    }
    
    install() {
        // 监听实体添加
        Engine.eventManager.on(CadEvents.EntityAdded, (args) => {
            if (this.enabled && args.entity.type === 'CIRCLE') {
                this.createBoundingBox(args.entity);
            }
        });
        
        // 监听实体修改
        Engine.eventManager.on(CadEvents.EntityModified, (args) => {
            if (this.enabled && args.entity.type === 'CIRCLE') {
                this.updateBoundingBox(args.entity);
            }
        });
        
        // 监听实体删除
        Engine.eventManager.on(CadEvents.EntitiesErased, (args) => {
            args.entities.forEach(entity => {
                if (entity.type === 'CIRCLE') {
                    this.removeBoundingBox(entity);
                }
            });
        });
    }
    
    createBoundingBox(circle) {
        const r = circle.radius;
        const cx = circle.center.x;
        const cy = circle.center.y;
        
        const rect = new PolylineEnt();
        rect.setPoints([
            [cx - r, cy - r],
            [cx + r, cy - r],
            [cx + r, cy + r],
            [cx - r, cy + r]
        ]);
        rect.isClosed = true;
        rect.setDefaults();
        rect.color = 3;
        
        Engine.addEntities(rect);
        this.boundingLines.set(circle.id, rect);
        Engine.regen();
    }
    
    updateBoundingBox(circle) {
        const rect = this.boundingLines.get(circle.id);
        if (!rect) return;
        
        const r = circle.radius;
        const cx = circle.center.x;
        const cy = circle.center.y;
        
        rect.setPoints([
            [cx - r, cy - r],
            [cx + r, cy - r],
            [cx + r, cy + r],
            [cx - r, cy + r]
        ]);
        Engine.regen();
    }
    
    removeBoundingBox(circle) {
        const rect = this.boundingLines.get(circle.id);
        if (rect) {
            Engine.eraseEntities(rect);
            this.boundingLines.delete(circle.id);
            Engine.regen();
        }
    }
    
    toggle() {
        this.enabled = !this.enabled;
        message.info(`反应器已${this.enabled ? '启用' : '禁用'}`);
    }
}

// 安装反应器
const reactor = new CircleBoundingBoxReactor();
reactor.install();
```

### 关键事件

| 事件 | 说明 |
|------|------|
| `CadEvents.EntityAdded` | 实体添加后触发 |
| `CadEvents.EntityModified` | 实体修改后触发 |
| `CadEvents.EntitiesErased` | 实体删除后触发（批量）|

---

## 关联标注反应器

标注实体内置反应器机制，通过 `setSourceEntities()` 建立关联，源实体移动时标注自动更新。

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

// 创建直线
const line = new LineEnt([0, 0], [100, 0]);
line.setDefaults();
Engine.addEntities(line);

// 创建标注
const dim = new LinearDimensionEnt(
    new Point2D(0, 0),
    new Point2D(100, 0),
    new Point2D(50, 20),
    0
);
dim.setDefaults();
Engine.addEntities(dim);

// 建立关联（必须在 addEntities 之后）
dim.setSourceEntities(
    line.id, -1,    // 起点关联实体 + 段索引：LINE 忽略段索引（填 -1 即可）；PLINE 须填 ≥0 的直线段索引（-1/弧段取不到点，标注不跟随）
    line.id, -1,    // 终点关联实体
    'start',        // 起点端点类型
    'end'           // 终点端点类型
);

// 检查关联状态
console.log("isAssociative:", dim.isAssociative);

// 移动直线时，标注会自动更新
line.move([0, 0], [50, 0]);
Engine.regen();
```

### 端点类型

| 类型 | 说明 |
|------|------|
| `'start'` | 起点 |
| `'end'` | 终点 |
| `'center'` | ⚠️ 类型里有但未实现（Linear/Aligned 关联回退为起点） |
| `'midpoint'` | 中点 |

### 精确定位

使用 `parameterT` 指定在线段上的比例位置（0~1）：

```javascript
dim.setSourceEntities(
    line.id, -1,
    line.id, -1,
    undefined,       // 不使用 pointType
    undefined,
    0.25,            // startParameterT: 1/4 处
    0.75             // endParameterT: 3/4 处
);
```

### 支持的标注类型

上面的 `setSourceEntities(...)` 签名用于：
- `LinearDimensionEnt` - 线性标注
- `AlignedDimensionEnt` - 对齐标注

其他可关联的实体（参数各不相同，见 types.d.ts）：
- `AngleDimensionEnt` / `TwoLineAngularDimensionEnt` - `setSourceEntities(line1Id, line1SegIdx, line2Id, line2SegIdx)`
- `ThreePointAngularDimensionEnt` - `setSourceEntities(entity1Id, seg1, point1Type, entity2Id, seg2, point2Type, centerEntityId?, ...)`
- `RadialDimensionEnt` / `DiametricDimensionEnt` - `setSourceEntity(entityId, pointAngle?)`
- `ArcDimensionEnt` - `setSourceEntity(entityId)`
- `OrdinateDimensionEnt` - `setSourceEntity(entityId, pointType?)`
- `MLeaderEnt` - `setArrowSourceEntity(leaderLineIndex, entityId, segmentIndex?, pointType?, ...)`（箭头跟随实体）

---

## 反应器生命周期管理

### 启用/禁用控制

```javascript
class MyReactor {
    constructor() {
        this.enabled = true;
    }
    
    toggle() {
        this.enabled = !this.enabled;
    }
}
```

### 数据管理

使用 `Map` 存储关联数据：

```javascript
class MyReactor {
    constructor() {
        this.relations = new Map();  // entityId -> relatedEntity
    }
    
    // 创建关联
    onCreate(entity) {
        const related = this.createRelated(entity);
        this.relations.set(entity.id, related);
    }
    
    // 更新关联
    onModify(entity) {
        const related = this.relations.get(entity.id);
        if (related) {
            this.updateRelated(entity, related);
        }
    }
    
    // 删除关联
    onDelete(entity) {
        const related = this.relations.get(entity.id);
        if (related) {
            Engine.eraseEntities(related);
            this.relations.delete(entity.id);
        }
    }
}
```

### 注意事项

1. **关联标注必须在 addEntities 之后设置**
2. **线段索引 `-1` 表示整条线**
3. **使用 `parameterT` 时，`pointType` 应设为 `undefined`**
4. **关联后源实体变化时标注自动更新，无需手动调用**
