# 命令系统

自定义命令的创建、注册和执行。

## 基础命令定义

```javascript
const { 
    CommandDefinition, 
    CommandRegistry, 
    CommandOptions,
    writeMessage,
    Engine 
} = vjcad;

// 定义命令类
class HelloCommand {
    async main() {
        writeMessage("<br/>Hello WebCAD!");
        Engine.zoomExtents();
    }
}

// 创建命令定义
const options = new CommandOptions();
options.useAutoComplete = true;  // 启用自动补全

const cmdDef = new CommandDefinition(
    'HELLO',              // 命令名（大写）
    '简单示例命令',        // 描述
    HelloCommand,         // 命令类
    options               // 选项（可选）
);

// 注册命令
CommandRegistry.regist(cmdDef);

// 执行命令
await Engine.editor.executerWithOp('HELLO');
```

---

## 带用户输入的命令

```javascript
const { 
    CommandDefinition, CommandRegistry,
    PointInputOptions, InputStatusEnum,
    getPoint, writeMessage,
    LineEnt, Engine 
} = vjcad;

class DrawLineCommand {
    async main() {
        // 开始撤销组
        Engine.undoManager.start_undoMark();
        
        try {
            // 获取起点
            const opt1 = new PointInputOptions("指定起点:");
            const result1 = await getPoint(opt1);
            if (result1.status !== InputStatusEnum.OK) return;
            
            // 获取终点（带橡皮筋线）
            const opt2 = new PointInputOptions("指定终点:");
            opt2.useBasePoint = true;
            opt2.basePoint = result1.value;
            const result2 = await getPoint(opt2);
            if (result2.status !== InputStatusEnum.OK) return;
            
            // 创建直线
            const line = new LineEnt(result1.value, result2.value);
            line.setDefaults();
            Engine.addEntities(line);
            
            writeMessage("<br/>直线已创建");
        } finally {
            Engine.undoManager.end_undoMark();
        }
    }
}

const cmdDef = new CommandDefinition("DRAWLINE", "绘制直线", DrawLineCommand);
CommandRegistry.regist(cmdDef);
```

---

## 预览绘制

使用 `callback` 实现鼠标移动时的实时预览。

```javascript
const { 
    PointInputOptions, InputStatusEnum,
    getPoint, CircleEnt, Engine 
} = vjcad;

class DrawCircleWithPreviewCommand {
    constructor() {
        this.center = null;
    }
    
    async main() {
        Engine.undoManager.start_undoMark();
        
        try {
            // 获取圆心
            const opt1 = new PointInputOptions("指定圆心:");
            const result1 = await getPoint(opt1);
            if (result1.status !== InputStatusEnum.OK) return;
            this.center = result1.value;
            
            // 获取半径（带预览）
            await this.getRadiusWithPreview();
        } finally {
            Engine.undoManager.end_undoMark();
            Engine.clearPreview();  // 清除预览
        }
    }
    
    async getRadiusWithPreview() {
        const options = new PointInputOptions("指定半径:");
        options.useBasePoint = true;
        options.basePoint = this.center;
        
        // 预览回调
        options.callback = (canvasPoint) => {
            const worldPoint = Engine.canvasToWcs(canvasPoint);
            const radius = Math.sqrt(
                Math.pow(worldPoint.x - this.center.x, 2) +
                Math.pow(worldPoint.y - this.center.y, 2)
            );
            
            if (radius > 0) {
                const previewCircle = new CircleEnt(
                    [this.center.x, this.center.y], 
                    radius
                );
                previewCircle.setDefaults();
                Engine.clearPreview();
                Engine.drawPreviewEntity(previewCircle);
            }
        };
        
        const result = await getPoint(options);
        Engine.clearPreview();
        
        if (result.status === InputStatusEnum.OK) {
            const endPoint = result.value;
            const radius = Math.sqrt(
                Math.pow(endPoint.x - this.center.x, 2) +
                Math.pow(endPoint.y - this.center.y, 2)
            );
            
            const circle = new CircleEnt([this.center.x, this.center.y], radius);
            circle.setDefaults();
            Engine.addEntities(circle);
        }
    }
}
```

**预览相关 API**：
- `Engine.canvasToWcs(canvasPoint)` - 画布坐标转世界坐标
- `Engine.clearPreview()` - 清除预览
- `Engine.drawPreviewEntity(entity)` - 绘制单个预览实体
- `Engine.drawPreviewEntities(entities)` - 绘制多个预览实体

---

## 状态机命令模式

处理复杂的多步骤命令。

```javascript
const { 
    PointInputOptions, InputStatusEnum,
    getPoint, PolylineEnt, Engine, writeMessage 
} = vjcad;

class DrawPolylineCommand {
    constructor() {
        this.step = 1;      // 当前状态
        this.points = [];   // 顶点集合
        this.isClosed = false;
    }
    
    async main() {
        Engine.undoManager.start_undoMark();
        
        try {
            // 状态机循环
            while (this.step > 0) {
                switch (this.step) {
                    case 1:
                        await this.step1_getFirstPoint();
                        break;
                    case 2:
                        await this.step2_getNextPoints();
                        break;
                    case 3:
                        this.step3_finish();
                        break;
                }
            }
        } finally {
            Engine.undoManager.end_undoMark();
            Engine.clearPreview();
        }
    }
    
    async step1_getFirstPoint() {
        const options = new PointInputOptions("指定起点:");
        const result = await getPoint(options);
        
        if (result.status === InputStatusEnum.OK) {
            this.points.push(result.value);
            this.step = 2;
        } else {
            this.step = 0;  // 退出
        }
    }
    
    async step2_getNextPoints() {
        const options = new PointInputOptions(
            "指定下一点 [闭合(C)/撤销(U)] <完成>:"
        );
        options.keywords = ["C", "U"];  // 支持关键字
        
        if (this.points.length > 0) {
            options.useBasePoint = true;
            options.basePoint = this.points[this.points.length - 1];
        }
        
        const result = await getPoint(options);
        
        if (result.status === InputStatusEnum.OK) {
            // 用户点击了一个点
            this.points.push(result.value);
        } else if (result.status === InputStatusEnum.Keyword) {
            // 用户输入了关键字
            const keyword = result.stringResult.toUpperCase();
            if (keyword === "C") {
                this.isClosed = true;
                this.step = 3;
            } else if (keyword === "U") {
                if (this.points.length > 1) {
                    this.points.pop();
                    writeMessage("<br/>已撤销上一点");
                }
            }
        } else if (result.status === InputStatusEnum.None) {
            // 用户直接按回车/空格（getPoint 空回车返回 None，不是 EnterOrSpace），完成
            this.step = 3;
        } else {
            // 取消
            this.step = 0;
        }
    }
    
    step3_finish() {
        if (this.points.length >= 2) {
            const pline = new PolylineEnt();
            pline.setPoints(this.points.map(p => [p.x, p.y]));
            pline.isClosed = this.isClosed;
            pline.setDefaults();
            Engine.addEntities(pline);
            
            writeMessage(`<br/>多段线已创建，${this.points.length} 个顶点`);
        }
        this.step = 0;  // 结束
    }
}
```

**状态机要点**：
- `step > 0` 继续循环，`step = 0` 退出
- `InputStatusEnum.Keyword` 处理关键字输入
- `result.stringResult` 获取关键字字符串
- `InputStatusEnum.None` 处理直接回车/空格（getPoint/getInteger/getString 空回车均返回 `None`；`EnterOrSpace` 仅 getEntity 返回）

---

## 命令注册与管理

```javascript
// 批量注册命令
const commands = [
    { name: 'CMD1', desc: '命令1', cls: Cmd1Class },
    { name: 'CMD2', desc: '命令2', cls: Cmd2Class },
];

commands.forEach(cmd => {
    const options = new CommandOptions();
    options.useAutoComplete = true;
    CommandRegistry.regist(
        new CommandDefinition(cmd.name, cmd.desc, cmd.cls, options)
    );
});

// 查询已注册的命令
const cmdInfo = CommandRegistry.item('CMD1');
if (cmdInfo) {
    console.log("命令名:", cmdInfo.name);
    console.log("描述:", cmdInfo.description);
}

// 注销命令
CommandRegistry.unregist('CMD1');
```

---

## 命令类标准模板

```javascript
class MyCommand {
    constructor() {
        // 初始化状态
    }
    
    async main() {
        // 开始撤销组
        Engine.undoManager.start_undoMark();
        
        try {
            // 命令逻辑
            await this.doWork();
        } catch (error) {
            writeMessage(`<br/>错误: ${error.message}`);
        } finally {
            // 结束撤销组
            Engine.undoManager.end_undoMark();
            // 清除预览
            Engine.clearPreview();
        }
    }
    
    async doWork() {
        // 实际命令逻辑
    }
}
```
