惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
有赞技术团队
有赞技术团队
S
Schneier on Security
aimingoo的专栏
aimingoo的专栏
T
Troy Hunt's Blog
U
Unit 42
Hacker News - Newest:
Hacker News - Newest: "LLM"
V2EX - 技术
V2EX - 技术
T
The Blog of Author Tim Ferriss
V
Visual Studio Blog
H
Heimdal Security Blog
H
Hacker News: Front Page
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
Cloudbric
Cloudbric
Google DeepMind News
Google DeepMind News
C
Cisco Blogs
The Cloudflare Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
MyScale Blog
MyScale Blog
F
Fortinet All Blogs
N
News | PayPal Newsroom
Attack and Defense Labs
Attack and Defense Labs
D
DataBreaches.Net
N
News and Events Feed by Topic
Security Archives - TechRepublic
Security Archives - TechRepublic
Forbes - Security
Forbes - Security
Simon Willison's Weblog
Simon Willison's Weblog
F
Full Disclosure
The Register - Security
The Register - Security
L
LINUX DO - 热门话题
Webroot Blog
Webroot Blog
Google Online Security Blog
Google Online Security Blog
AI
AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
I
Intezer
S
Security Affairs
阮一峰的网络日志
阮一峰的网络日志
K
Kaspersky official blog
云风的 BLOG
云风的 BLOG
博客园 - 叶小钗
T
Threatpost
Spread Privacy
Spread Privacy
小众软件
小众软件
AWS News Blog
AWS News Blog
S
Secure Thoughts
S
Security @ Cisco Blogs
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
J
Java Code Geeks

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
命令与操作系统
我才是银古 · 2026-06-20 · via 博客园 - 我才是银古

第十六章:命令与操作系统

16.1 命令系统概述

16.1.1 命令模式

LightCAD的操作系统基于经典的命令模式(Command Pattern)设计,将用户的每一个操作封装为独立的命令对象。这种设计带来了以下优势:

  • 可撤销/重做:每个命令记录操作前后的状态
  • 命令队列:支持命令的排队执行
  • 宏录制:支持录制和回放操作序列
  • 命令行驱动:支持通过命令行输入执行操作

16.1.2 命令系统架构

命令注册表(CommandRegistry)
    ├── 绘图命令(Draw Commands)
    │   ├── LINE    - 绘制直线
    │   ├── CIRCLE  - 绘制圆
    │   ├── ARC     - 绘制圆弧
    │   ├── PLINE   - 绘制多段线
    │   ├── RECT    - 绘制矩形
    │   └── SPLINE  - 绘制样条曲线
    ├── 编辑命令(Edit Commands)
    │   ├── MOVE    - 移动
    │   ├── COPY    - 复制
    │   ├── ROTATE  - 旋转
    │   ├── SCALE   - 缩放
    │   ├── MIRROR  - 镜像
    │   ├── TRIM    - 修剪
    │   ├── EXTEND  - 延伸
    │   ├── OFFSET  - 偏移
    │   └── FILLET  - 圆角
    ├── 视图命令(View Commands)
    │   ├── ZOOM    - 缩放视图
    │   ├── PAN     - 平移视图
    │   └── VIEW    - 切换视图
    └── 3D命令(3D Commands)
        ├── EXTRUDE - 拉伸
        ├── REVOLVE - 旋转体
        ├── LOFT    - 放样
        └── BOOLEAN - 布尔运算

16.2 命令注册与执行

16.2.1 命令注册表

public class CommandRegistry
{
    private Dictionary<string, CommandInfo> commands = new();
    private Dictionary<string, string> aliases = new();

    /// <summary>
    /// 注册命令
    /// </summary>
    public void Register(CommandInfo command)
    {
        commands[command.Name.ToUpper()] = command;

        // 注册别名
        if (command.Aliases != null)
        {
            foreach (var alias in command.Aliases)
            {
                aliases[alias.ToUpper()] = command.Name.ToUpper();
            }
        }
    }

    /// <summary>
    /// 执行命令
    /// </summary>
    public bool Execute(string commandName)
    {
        var name = commandName.ToUpper();

        // 检查别名
        if (aliases.TryGetValue(name, out var realName))
        {
            name = realName;
        }

        if (commands.TryGetValue(name, out var command))
        {
            command.Action?.Invoke();
            return true;
        }

        return false;
    }

    /// <summary>
    /// 获取所有命令
    /// </summary>
    public IEnumerable<CommandInfo> GetAllCommands()
    {
        return commands.Values;
    }

    /// <summary>
    /// 搜索命令(支持模糊匹配)
    /// </summary>
    public IEnumerable<CommandInfo> Search(string keyword)
    {
        var upper = keyword.ToUpper();
        return commands.Values.Where(c =>
            c.Name.Contains(upper) ||
            c.DisplayName.Contains(keyword, StringComparison.OrdinalIgnoreCase));
    }
}

public class CommandInfo
{
    public string Name { get; set; }
    public string DisplayName { get; set; }
    public string Description { get; set; }
    public string Category { get; set; }
    public string[] Aliases { get; set; }
    public Action Action { get; set; }
    public string IconPath { get; set; }
    public Keys ShortcutKey { get; set; }
}

16.2.2 内置命令注册

public class BuiltInCommands
{
    public static void RegisterAll(CommandRegistry registry)
    {
        // 绘图命令
        registry.Register(new CommandInfo
        {
            Name = "LINE",
            DisplayName = "直线",
            Category = "绘图",
            Aliases = new[] { "L" },
            Action = () => ActionManager.StartAction(new DrawLineAction())
        });

        registry.Register(new CommandInfo
        {
            Name = "CIRCLE",
            DisplayName = "圆",
            Category = "绘图",
            Aliases = new[] { "C" },
            Action = () => ActionManager.StartAction(new DrawCircleAction())
        });

        registry.Register(new CommandInfo
        {
            Name = "ARC",
            DisplayName = "圆弧",
            Category = "绘图",
            Aliases = new[] { "A" },
            Action = () => ActionManager.StartAction(new DrawArcAction())
        });

        // 编辑命令
        registry.Register(new CommandInfo
        {
            Name = "MOVE",
            DisplayName = "移动",
            Category = "编辑",
            Aliases = new[] { "M" },
            Action = () => ActionManager.StartAction(new MoveAction())
        });

        registry.Register(new CommandInfo
        {
            Name = "COPY",
            DisplayName = "复制",
            Category = "编辑",
            Aliases = new[] { "CO", "CP" },
            Action = () => ActionManager.StartAction(new CopyAction())
        });

        registry.Register(new CommandInfo
        {
            Name = "ROTATE",
            DisplayName = "旋转",
            Category = "编辑",
            Aliases = new[] { "RO" },
            Action = () => ActionManager.StartAction(new RotateAction())
        });

        registry.Register(new CommandInfo
        {
            Name = "SCALE",
            DisplayName = "缩放",
            Category = "编辑",
            Aliases = new[] { "SC" },
            Action = () => ActionManager.StartAction(new ScaleAction())
        });

        registry.Register(new CommandInfo
        {
            Name = "MIRROR",
            DisplayName = "镜像",
            Category = "编辑",
            Aliases = new[] { "MI" },
            Action = () => ActionManager.StartAction(new MirrorAction())
        });

        registry.Register(new CommandInfo
        {
            Name = "OFFSET",
            DisplayName = "偏移",
            Category = "编辑",
            Aliases = new[] { "O" },
            Action = () => ActionManager.StartAction(new OffsetAction())
        });

        registry.Register(new CommandInfo
        {
            Name = "TRIM",
            DisplayName = "修剪",
            Category = "编辑",
            Aliases = new[] { "TR" },
            Action = () => ActionManager.StartAction(new TrimAction())
        });
    }
}

16.3 撤销/重做系统

16.3.1 操作记录

public class UndoRedoManager
{
    private Stack<IUndoableAction> undoStack = new();
    private Stack<IUndoableAction> redoStack = new();
    private const int MaxUndoLevels = 100;

    /// <summary>
    /// 记录可撤销的操作
    /// </summary>
    public void Record(IUndoableAction action)
    {
        undoStack.Push(action);
        redoStack.Clear(); // 新操作清除重做栈

        // 限制撤销深度
        while (undoStack.Count > MaxUndoLevels)
        {
            // 移除最底部的操作
            var temp = new Stack<IUndoableAction>(undoStack.Reverse());
            temp.Pop();
            undoStack = new Stack<IUndoableAction>(temp.Reverse());
        }
    }

    /// <summary>
    /// 撤销
    /// </summary>
    public bool Undo()
    {
        if (undoStack.Count == 0) return false;

        var action = undoStack.Pop();
        action.Undo();
        redoStack.Push(action);
        return true;
    }

    /// <summary>
    /// 重做
    /// </summary>
    public bool Redo()
    {
        if (redoStack.Count == 0) return false;

        var action = redoStack.Pop();
        action.Redo();
        undoStack.Push(action);
        return true;
    }

    public bool CanUndo => undoStack.Count > 0;
    public bool CanRedo => redoStack.Count > 0;
    public string UndoDescription => undoStack.Count > 0
        ? undoStack.Peek().Description : null;
    public string RedoDescription => redoStack.Count > 0
        ? redoStack.Peek().Description : null;
}

public interface IUndoableAction
{
    string Description { get; }
    void Undo();
    void Redo();
}

16.3.2 具体操作实现

/// <summary>
/// 添加实体操作
/// </summary>
public class AddEntityAction : IUndoableAction
{
    private LcEntity entity;
    private LcDocument document;

    public string Description => $"添加 {entity.TypeName}";

    public AddEntityAction(LcEntity entity, LcDocument document)
    {
        this.entity = entity;
        this.document = document;
    }

    public void Undo()
    {
        document.Entities.Remove(entity);
    }

    public void Redo()
    {
        document.Entities.Add(entity);
    }
}

/// <summary>
/// 删除实体操作
/// </summary>
public class DeleteEntityAction : IUndoableAction
{
    private List<LcEntity> entities;
    private LcDocument document;

    public string Description =>
        $"删除 {entities.Count} 个实体";

    public void Undo()
    {
        foreach (var entity in entities)
        {
            document.Entities.Add(entity);
        }
    }

    public void Redo()
    {
        foreach (var entity in entities)
        {
            document.Entities.Remove(entity);
        }
    }
}

/// <summary>
/// 移动实体操作
/// </summary>
public class MoveEntityAction : IUndoableAction
{
    private List<LcEntity> entities;
    private Vector2d displacement;

    public string Description =>
        $"移动 {entities.Count} 个实体";

    public void Undo()
    {
        var reverseTranslation = Matrix4d.CreateTranslation(
            -displacement.X, -displacement.Y, 0);

        foreach (var entity in entities)
        {
            entity.TransformBy(reverseTranslation);
        }
    }

    public void Redo()
    {
        var translation = Matrix4d.CreateTranslation(
            displacement.X, displacement.Y, 0);

        foreach (var entity in entities)
        {
            entity.TransformBy(translation);
        }
    }
}

16.4 元素操作(ElementAction)

16.4.1 ElementAction概述

ElementAction是LightCAD.Runtime中负责处理高层实体操作的核心类,文件大小达43KB,包含了大量的实体编辑逻辑:

public class ElementAction
{
    private LcDocument document;
    private SelectionManager selectionManager;
    private UndoRedoManager undoManager;

    /// <summary>
    /// 移动选中的实体
    /// </summary>
    public void Move(Vector2d displacement)
    {
        var entities = selectionManager.SelectedEntities.ToList();
        if (entities.Count == 0) return;

        var translation = Matrix4d.CreateTranslation(
            displacement.X, displacement.Y, 0);

        foreach (var entity in entities)
        {
            entity.TransformBy(translation);
        }

        undoManager.Record(new MoveEntityAction
        {
            entities = entities,
            displacement = displacement
        });
    }

    /// <summary>
    /// 复制选中的实体
    /// </summary>
    public void Copy(Vector2d displacement)
    {
        var entities = selectionManager.SelectedEntities.ToList();
        if (entities.Count == 0) return;

        var copies = new List<LcEntity>();
        var translation = Matrix4d.CreateTranslation(
            displacement.X, displacement.Y, 0);

        foreach (var entity in entities)
        {
            var copy = entity.Clone();
            copy.TransformBy(translation);
            document.Entities.Add(copy);
            copies.Add(copy);
        }

        undoManager.Record(new AddEntitiesAction(copies, document));
    }

    /// <summary>
    /// 旋转选中的实体
    /// </summary>
    public void Rotate(Point2d center, double angle)
    {
        var entities = selectionManager.SelectedEntities.ToList();
        if (entities.Count == 0) return;

        var toOrigin = Matrix4d.CreateTranslation(-center.X, -center.Y, 0);
        var rotation = Matrix4d.CreateRotationZ(angle);
        var backToPos = Matrix4d.CreateTranslation(center.X, center.Y, 0);
        var transform = backToPos * rotation * toOrigin;

        foreach (var entity in entities)
        {
            entity.TransformBy(transform);
        }

        undoManager.Record(new TransformEntityAction(
            entities, transform, $"旋转 {entities.Count} 个实体"));
    }

    /// <summary>
    /// 缩放选中的实体
    /// </summary>
    public void Scale(Point2d center, double factor)
    {
        var entities = selectionManager.SelectedEntities.ToList();
        if (entities.Count == 0) return;

        var toOrigin = Matrix4d.CreateTranslation(-center.X, -center.Y, 0);
        var scale = Matrix4d.CreateScale(factor, factor, factor);
        var backToPos = Matrix4d.CreateTranslation(center.X, center.Y, 0);
        var transform = backToPos * scale * toOrigin;

        foreach (var entity in entities)
        {
            entity.TransformBy(transform);
        }

        undoManager.Record(new TransformEntityAction(
            entities, transform, $"缩放 {entities.Count} 个实体"));
    }

    /// <summary>
    /// 镜像选中的实体
    /// </summary>
    public void Mirror(Point2d linePoint1, Point2d linePoint2,
                       bool deleteSource = false)
    {
        var entities = selectionManager.SelectedEntities.ToList();
        if (entities.Count == 0) return;

        var lineDir = (linePoint2 - linePoint1).Normalize();
        var mirrorMatrix = CreateMirrorMatrix(linePoint1, lineDir);

        var mirroredEntities = new List<LcEntity>();
        foreach (var entity in entities)
        {
            var mirrored = entity.Clone();
            mirrored.TransformBy(mirrorMatrix);
            document.Entities.Add(mirrored);
            mirroredEntities.Add(mirrored);
        }

        if (deleteSource)
        {
            foreach (var entity in entities)
            {
                document.Entities.Remove(entity);
            }
        }
    }

    /// <summary>
    /// 偏移
    /// </summary>
    public void Offset(LcEntity entity, double distance, Point2d side)
    {
        LcEntity offsetResult = null;

        switch (entity)
        {
            case LcLine line:
                offsetResult = OffsetLine(line, distance, side);
                break;
            case LcCircle circle:
                offsetResult = OffsetCircle(circle, distance, side);
                break;
            case LcArc arc:
                offsetResult = OffsetArc(arc, distance, side);
                break;
            case LcPolyline polyline:
                offsetResult = polyline.Offset(
                    DetermineOffsetSide(polyline, side) * distance);
                break;
        }

        if (offsetResult != null)
        {
            offsetResult.LayerName = entity.LayerName;
            offsetResult.Color = entity.Color;
            document.Entities.Add(offsetResult);

            undoManager.Record(new AddEntityAction(
                offsetResult, document));
        }
    }
}

16.5 快捷键系统

16.5.1 快捷键映射

public class ShortcutManager
{
    private Dictionary<Keys, string> shortcuts = new();

    public ShortcutManager()
    {
        // 注册默认快捷键
        RegisterDefaults();
    }

    private void RegisterDefaults()
    {
        // 文件操作
        shortcuts[Keys.Control | Keys.N] = "NEW";
        shortcuts[Keys.Control | Keys.O] = "OPEN";
        shortcuts[Keys.Control | Keys.S] = "SAVE";

        // 编辑操作
        shortcuts[Keys.Control | Keys.Z] = "UNDO";
        shortcuts[Keys.Control | Keys.Y] = "REDO";
        shortcuts[Keys.Control | Keys.C] = "COPYCLIP";
        shortcuts[Keys.Control | Keys.V] = "PASTECLIP";
        shortcuts[Keys.Delete] = "ERASE";

        // 绘图操作
        shortcuts[Keys.L] = "LINE";
        shortcuts[Keys.C] = "CIRCLE";
        shortcuts[Keys.A] = "ARC";

        // 视图操作
        shortcuts[Keys.Control | Keys.Shift | Keys.E] = "ZOOMEXTENTS";
    }

    /// <summary>
    /// 处理快捷键
    /// </summary>
    public bool HandleKey(Keys keys)
    {
        if (shortcuts.TryGetValue(keys, out var command))
        {
            CommandRegistry.Instance.Execute(command);
            return true;
        }
        return false;
    }
}

16.6 本章小结

本章详细介绍了LightCAD的命令与操作系统。基于命令模式的设计实现了操作的可撤销/重做,命令注册表提供了统一的命令管理和执行机制。元素操作(ElementAction)封装了移动、复制、旋转、缩放、镜像、偏移等核心编辑功能。快捷键系统为高效操作提供了支持。


上一章第十五章:插件系统与扩展开发

下一章第十七章:数据库与项目管理