znlgis 博客

GIS开发与技术分享

第三章:核心架构与分层设计

3.1 架构设计理念

3.1.1 分层架构原则

LightCAD采用严格的分层架构(Layered Architecture),这是企业级软件开发中最经典也最可靠的架构模式之一。其核心原则包括:

  1. 单向依赖:上层模块可以依赖下层模块,但下层模块不能依赖上层模块
  2. 职责分离:每一层只负责特定的功能域
  3. 接口隔离:层与层之间通过明确定义的接口通信
  4. 可替换性:每一层的实现可以独立替换,只要接口保持不变

3.1.2 设计模式应用

LightCAD在架构设计中大量使用了经典设计模式:

设计模式 应用场景 具体实现
分层模式 整体架构 Math→Core→RenderUtils→Drawing→Runtime
命令模式 操作系统 ActionCmds、ElementAction
策略模式 渲染引擎 Three.js/OpenTK不同渲染器
工厂模式 组件创建 ComponentCreator
观察者模式 变更通知 IUpdateObject
组合模式 层次实体 LcGroup3、LcArray3
插件模式 扩展系统 Weikio PluginFramework
MVVM模式 UI绑定 Avalonia XAML

3.2 分层架构详解

3.2.1 层次总览

LightCAD的分层架构从底层到顶层分为六个主要层次:

┌─────────────────────────────────────────────────────┐
│  第6层  用户界面层(Presentation Layer)              │
│  LightCAD.WinForm + LightCAD.Model                  │
├─────────────────────────────────────────────────────┤
│  第5层  应用运行时层(Application Runtime Layer)     │
│  LightCAD.Runtime                                    │
├─────────────────────────────────────────────────────┤
│  第4层  绘图交互层(Drawing Interaction Layer)       │
│  LightCAD.Drawing                                    │
├─────────────────────────────────────────────────────┤
│  第3层  渲染工具层(Render Utilities Layer)          │
│  LightCAD.RenderUtils                                │
├─────────────────────────────────────────────────────┤
│  第2层  核心数据层(Core Data Layer)                 │
│  LightCAD.Core                                       │
├─────────────────────────────────────────────────────┤
│  第1层  数学基础层(Math Foundation Layer)           │
│  LightCAD.MathLib                                    │
└─────────────────────────────────────────────────────┘

3.2.2 第1层:数学基础层(LightCAD.MathLib)

职责:提供所有数学运算的基础支持

模块结构

LightCAD.MathLib/
├── Basic/                # 基本数学类型
│   ├── Point2d.cs        # 二维点
│   ├── Point3d.cs        # 三维点
│   ├── Vector2d.cs       # 二维向量
│   ├── Vector3d.cs       # 三维向量
│   ├── Matrix3d.cs       # 3×3矩阵
│   ├── Matrix4d.cs       # 4×4变换矩阵
│   └── BoundingBox.cs    # 包围盒
├── Curve/                # 曲线数学
│   ├── BezierCurve.cs    # 贝塞尔曲线
│   ├── NurbsCurve.cs     # NURBS曲线
│   └── ArcMath.cs        # 圆弧数学
├── Intersection/         # 交集计算
│   ├── LineIntersect.cs  # 直线求交
│   ├── CurveIntersect.cs # 曲线求交
│   └── SurfaceIntersect.cs # 曲面求交
└── Utilities/            # 工具函数
    ├── Tolerance.cs      # 精度容差
    ├── AngleUtils.cs     # 角度工具
    └── GeometryUtils.cs  # 几何工具

关键特点

  • 无外部依赖(除MathNet.Numerics外)
  • 所有计算使用双精度浮点数
  • 内置容差比较机制
  • 性能优化的矩阵运算

示例代码

// 基本的向量和矩阵运算
var point = new Point3d(1.0, 2.0, 3.0);
var vector = new Vector3d(0.0, 0.0, 1.0);

// 创建变换矩阵(绕Z轴旋转45度)
var rotation = Matrix4d.CreateRotationZ(Math.PI / 4);

// 应用变换
var transformed = rotation.Transform(point);

// 计算两条直线的交点
var p1 = new Point2d(0, 0);
var d1 = new Vector2d(1, 1);
var p2 = new Point2d(1, 0);
var d2 = new Vector2d(0, 1);
var intersection = LineIntersect.Compute(p1, d1, p2, d2);

3.2.3 第2层:核心数据层(LightCAD.Core)

职责:定义CAD系统的核心数据模型

模块结构

LightCAD.Core/
├── Elements/             # 图元实体
│   ├── Basic/            # 2D基本图元
│   ├── Element3d/        # 3D图元
│   ├── Solid/            # 实体建模
│   ├── Dim/              # 2D标注
│   ├── Dim3/             # 3D标注
│   ├── Table/            # 表格
│   ├── Mass/             # 质量属性
│   ├── Misc/             # 杂项
│   ├── Ref/              # 引用
│   └── VoidElement/      # 空元素
├── LcDocument.cs         # 文档容器
├── LcEntity.cs           # 实体基类
├── LcText.cs             # 文本元素
├── ElementType.cs        # 元素类型定义
└── IUpdateObject.cs      # 更新接口

核心类层次

LcEntity (实体基类)
├── LcLine          # 直线
├── LcArc           # 圆弧
├── LcCircle        # 圆
├── LcEllipse       # 椭圆
├── LcPolyline      # 多段线
├── LcPolygon       # 多边形
├── LcSpline        # 样条曲线
├── LcText          # 文本
├── LcLine3d        # 3D直线
├── LcArc3d         # 3D圆弧
├── LcCircle3d      # 3D圆
├── LcSolid3d       # 3D实体
├── LcExtrude3d     # 拉伸体
├── LcRevolve3d     # 旋转体
├── LcLoft3d        # 放样体
├── LcBlend3d       # 融合体
├── LcMesh3d        # 网格体
└── ...

关键特点

  • 依赖MathLib层和ThreeJs4Net(字体管理)
  • 实体采用组合模式组织层次关系
  • 实现IUpdateObject接口支持变更通知
  • ElementType系统支持自定义元素类型注册

3.2.4 第3层:渲染工具层(LightCAD.RenderUtils)

职责:在Core和Runtime之间架起渲染桥梁

模块结构

LightCAD.RenderUtils/
├── 3dcontrols/           # 3D操控器
│   ├── Gizmo.cs          # 变换手柄
│   ├── Manipulator.cs    # 操控器
│   └── Handle3d.cs       # 3D句柄
├── AssetManagers/        # 资源管理
│   ├── MaterialManager.cs    # 材质管理
│   ├── TextureManager.cs     # 纹理管理
│   └── FontManager.cs        # 字体管理
├── ThreeUtils/           # Three.js工具
│   ├── SceneHelper.cs    # 场景辅助
│   ├── CameraHelper.cs   # 相机辅助
│   └── LightHelper.cs    # 光照辅助
└── Events/               # 事件系统
    ├── RenderEvent.cs    # 渲染事件
    └── SelectionEvent.cs # 选择事件

关键特点

  • 核心桥接层,连接数据和显示
  • 支持无编辑环境的纯渲染场景
  • 管理3D操控器和交互句柄
  • 提供统一的资源管理接口

架构备忘(摘自框架结构备忘.txt):

编辑环境:    Core → RenderUtils → Runtime
无头环境:    Core → Base → RenderUtils → 客户端程序

这意味着RenderUtils被设计为可以在没有完整编辑环境的情况下工作,支持服务端渲染或批处理场景。

3.2.5 第4层:绘图交互层(LightCAD.Drawing)

职责:管理二维绘图视口和用户交互

模块结构

LightCAD.Drawing/
├── InputSys/             # 输入系统
│   ├── Inputer.cs        # 输入处理器
│   ├── MouseHandler.cs   # 鼠标处理
│   └── KeyHandler.cs     # 键盘处理
├── Snap/                 # 捕捉系统
│   ├── SnapManager.cs    # 捕捉管理器
│   ├── EndPointSnap.cs   # 端点捕捉
│   ├── MidPointSnap.cs   # 中点捕捉
│   ├── CenterSnap.cs     # 圆心捕捉
│   └── IntersectSnap.cs  # 交点捕捉
├── PViewPort/            # 视口组件
│   ├── ViewportRenderer.cs   # 视口渲染器
│   ├── ViewportManager.cs    # 视口管理器
│   └── ZoomController.cs     # 缩放控制器
└── ViewPortRtAction/     # 视口运行时动作
    ├── PanAction.cs      # 平移动作
    ├── ZoomAction.cs     # 缩放动作
    └── RotateAction.cs   # 旋转动作

关键特点

  • 完整的鼠标/键盘输入处理
  • 精确的对象捕捉系统
  • 视口的缩放、平移、旋转控制
  • 夹点(Grip)编辑机制

3.2.6 第5层:应用运行时层(LightCAD.Runtime)

职责:提供高层应用逻辑和视图构建

模块结构

LightCAD.Runtime/
├── ViewBuilder/              # 视图构建系统
│   ├── ViewBuilder.cs        # 主构建器(71KB)
│   ├── ProjectioinProcessor.cs # 投影处理器(39KB)
│   ├── PolygonExt.cs         # 多边形扩展(22KB)
│   ├── PrjResultCollector.cs # 投影结果收集
│   ├── BrepViewBuilder.cs    # B-Rep视图构建
│   ├── GpuVisibleFilter.cs   # GPU可见性过滤
│   ├── GlobalTopView/        # 全局俯视图
│   ├── ElevationView/        # 立面视图
│   ├── PlaneDetailView/      # 平面详图
│   ├── SectionView/          # 剖面视图
│   └── ProjectionApp/        # 投影应用框架
├── ViewportRuntime.cs        # 视口运行时(119KB)
├── ElementAction.cs          # 元素操作(43KB)
├── ComponentEditingObject.cs # 组件编辑对象
├── RefEditingObject.cs       # 引用编辑对象
├── SectionEditingObject.cs   # 截面编辑对象
└── ViewportEditingObject.cs  # 视口编辑对象

关键特点

  • 包含最复杂的业务逻辑
  • 视图构建器是核心组件(仅ViewBuilder.cs就有71KB)
  • 支持多种视图类型生成
  • 实现跨层级编辑能力

3.2.7 第6层:用户界面层

职责:提供最终用户交互界面

LightCAD.Model(Avalonia XAML控件)

LightCAD.Model/
├── ModelControl.axaml         # 模型控件
├── ModelControl.axaml.cs      # 模型控件后台代码
├── ModelEditor.axaml          # 模型编辑器
├── ModelEditor.axaml.cs       # 编辑器后台代码
├── ModelViewControl.axaml     # 模型视图控件
├── ModelViewControl.axaml.cs  # 视图控件后台代码
├── ModelStatusBar.axaml       # 状态栏
└── ModelStatusBar.axaml.cs    # 状态栏后台代码

LightCAD.WinForm(主应用程序)

LightCAD.WinForm/
├── Program.cs                 # 程序入口点
├── MainForm.cs                # 主窗体
├── Font/                      # 字体资源
│   ├── TTF/                   # TrueType字体
│   │   ├── Roboto-Regular.ttf
│   │   ├── SimFang.ttf
│   │   ├── SimHei.ttf
│   │   └── SimSun.ttf
│   └── Shx/                   # SHX CAD字体(50+种)
│       ├── simplex.shx
│       ├── txt.shx
│       ├── gbcbig.shx
│       └── ...
└── Resources/                 # 其他资源文件

3.3 模块间通信机制

3.3.1 直接方法调用

最基本的通信方式——上层模块直接调用下层模块的公共API:

// Runtime层调用Core层的方法
var document = new LcDocument();
var line = new LcLine(new Point2d(0, 0), new Point2d(100, 100));
document.AddEntity(line);

3.3.2 事件驱动通信

通过事件系统实现松耦合的模块间通信:

// RenderUtils中的事件定义
public class RenderEvent
{
    public event EventHandler<EntityChangedArgs> EntityChanged;
    public event EventHandler<SelectionChangedArgs> SelectionChanged;
    public event EventHandler ViewInvalidated;
}

// 上层订阅事件
renderEvent.EntityChanged += (sender, args) =>
{
    // 处理实体变更
    RefreshViewport();
};

3.3.3 接口契约

通过接口定义层间的交互契约:

// 在Core层定义接口
public interface IUpdateObject
{
    void OnUpdate(UpdateContext context);
    bool NeedsUpdate { get; }
}

// 在具体实体中实现
public class LcLine : LcEntity, IUpdateObject
{
    public void OnUpdate(UpdateContext context)
    {
        // 更新逻辑
        RecalculateBounds();
    }

    public bool NeedsUpdate { get; private set; }
}

3.3.4 依赖注入

LightCAD在某些模块中使用依赖注入来管理组件的创建和生命周期:

// 插件系统中的依赖注入
public class ComponentActionLoader
{
    public void RegisterAction<T>(string name) where T : IComponentAction
    {
        // 注册组件操作
    }

    public IComponentAction ResolveAction(string name)
    {
        // 解析并返回操作实例
    }
}

3.4 两种运行环境

3.4.1 完整编辑环境

在完整的编辑环境中,所有层次模块都参与工作:

用户操作 → WinForm → Model → Runtime → Drawing → RenderUtils → Core → MathLib

这是桌面应用程序的标准运行模式,提供完整的编辑和交互功能。

3.4.2 无头渲染环境

LightCAD还支持无编辑界面的渲染环境:

客户端程序 → RenderUtils → Core → MathLib

这种模式适用于:

  • 服务端批量渲染
  • 格式转换工具
  • 自动化测试
  • 嵌入式预览

在无头环境中,跳过了Drawing和Runtime层,直接使用RenderUtils的渲染能力。这是LightCAD架构的一个重要设计亮点。

3.5 扩展架构

3.5.1 插件体系

LightCAD的插件体系构建在分层架构之上,插件可以在不同层次上进行扩展:

┌─────────────────────────────────────────────────────────┐
│                  行业插件层                               │
│  QdArch / QdElectric / QdHvac / QdStruct / QdWater     │
├─────────────────────────────────────────────────────────┤
│                  组件操作层                               │
│  LightCAD.Component.Actions                              │
├─────────────────────────────────────────────────────────┤
│                  核心框架层                               │
│  MathLib → Core → RenderUtils → Drawing → Runtime       │
└─────────────────────────────────────────────────────────┘

3.5.2 组件操作模块

LightCAD.Component.Actions模块实现了具体的3D建模操作:

// 操作加载器
public class ComponentActionLoader
{
    // 动态加载操作
    public void LoadActions(string pluginPath);
}

// 具体操作实现
public class CubeAction : IComponentAction
{
    // 创建立方体的操作逻辑
}

public class ExtrudeAction : IComponentAction
{
    // 拉伸操作逻辑(15KB)
}

public class LoftAction : IComponentAction
{
    // 放样操作逻辑(40KB)
}

3.5.3 导入导出模块

LightCAD.ImportAndExport模块独立于核心层次结构,作为横切关注点存在:

LightCAD.ImportAndExport
├── DwgImporter.cs        # DWG导入
├── DwgExporter.cs        # DWG导出
├── DxfImporter.cs        # DXF导入
├── DxfExporter.cs        # DXF导出
├── SketchUpImporter.cs   # SketchUp导入
└── NativeFormat.cs       # 原生格式

3.6 核心设计决策分析

3.6.1 为什么选择分层架构

优势

  1. 可维护性:每层代码量可控,修改影响范围有限
  2. 可测试性:每层可以独立进行单元测试
  3. 可复用性:底层模块(如MathLib)可以在其他项目中复用
  4. 团队协作:不同开发者可以并行开发不同层次的功能

设计权衡

  • 分层带来一定的性能开销(跨层调用)
  • 需要严格遵守依赖规则
  • 某些跨层功能的实现可能不够直观

3.6.2 RenderUtils独立成层的原因

将渲染工具独立为一层是LightCAD架构中的关键决策。根据框架设计备忘录的说明:

  1. Core层不适合直接用于无编辑环境的插件开发:因为Core依赖ThreeJs4Net(字体管理需要)
  2. WorkPlane3d的层次化问题:工作平面创建会引入ThreeJs4Net依赖
  3. 需要在Core和Runtime之间建立中间层:使得无编辑环境也能使用渲染功能

这个设计使得LightCAD可以灵活地支持多种运行模式。

3.6.3 混合UI框架的选择

WinForms + Avalonia的组合也是经过深思熟虑的选择:

  • WinForms提供稳定的窗口管理和Windows平台原生集成
  • Avalonia提供现代化的XAML声明式UI和数据绑定
  • 这种混合方案在保持与Windows原生功能兼容的同时,享受了现代UI框架的开发效率

3.7 数据流分析

3.7.1 创建实体的数据流

用户操作(UI层)
    ↓
命令解析(Runtime层)
    ↓
创建实体对象(Core层)
    ↓
几何计算(MathLib层)
    ↓
注册到文档(Core层)
    ↓
触发更新事件(RenderUtils层)
    ↓
重新渲染视口(Drawing层)
    ↓
更新UI显示(UI层)

3.7.2 渲染管线数据流

实体数据(Core层)
    ↓
构建Three.js场景对象(RenderUtils层)
    ↓
设置材质和光照(RenderUtils层)
    ↓
投影计算(Runtime层 ViewBuilder)
    ↓
视口剪裁和可见性判断(Drawing层)
    ↓
GPU渲染输出(Three.js/OpenTK)
    ↓
屏幕显示(UI层)

3.7.3 文件导入数据流

读取DWG/DXF文件(ImportAndExport模块)
    ↓
解析文件结构(netDxf/ODA SDK)
    ↓
转换为LightCAD实体(Core层)
    ↓
几何数据转换(MathLib层)
    ↓
添加到文档(Core层)
    ↓
触发重新渲染(RenderUtils层)
    ↓
显示在视口中(Drawing层 → UI层)

3.8 与FY_Layout的关系

3.8.1 框架与应用的关系

LightCAD是底层CAD框架,FY_Layout是基于LightCAD开发的场地布置应用:

┌───────────────────────────────┐
│      FY_Layout(应用层)       │
│  场布设计/板房系统/道路设计    │
├───────────────────────────────┤
│      LightCAD(框架层)        │
│  CAD核心/渲染/绘图/运行时     │
└───────────────────────────────┘

FY_Layout展示了如何基于LightCAD框架进行行业应用的二次开发,是学习LightCAD扩展开发的最佳参考实例。

3.8.2 扩展点

FY_Layout主要在以下层次上扩展LightCAD:

  • Core层:定义新的场布元素类型
  • Runtime层:实现场布特有的操作逻辑
  • UI层:提供场布设计的专用界面
  • Plugin层:通过插件系统注册场布功能

3.9 本章小结

本章深入分析了LightCAD的核心架构和分层设计。LightCAD采用6层分层架构,从底层的数学库到顶层的用户界面,每一层职责明确、依赖清晰。通过事件驱动、接口契约和依赖注入等机制实现模块间的松耦合通信。独特的RenderUtils中间层设计使得LightCAD可以灵活支持完整编辑环境和无头渲染两种运行模式。这种架构设计为插件开发和行业定制提供了坚实的基础。


上一章第二章:开发环境搭建与项目配置

下一章第四章:数学库详解