第五章:核心数据模型详解
5.1 Core模块概述
5.1.1 LightCAD.Core的定位
LightCAD.Core是整个CAD系统的数据基础层,定义了所有图元实体、文档结构和元素类型系统。它位于MathLib之上、RenderUtils之下,是连接数学计算和图形显示的关键桥梁。
5.1.2 核心职责
- 实体定义:定义所有2D和3D图元的数据结构
- 文档管理:提供CAD文档的容器和组织结构
- 元素类型系统:支持自定义元素类型的注册和管理
- 属性系统:为实体提供可扩展的属性机制
- 变更通知:通过IUpdateObject接口实现变更追踪
5.1.3 依赖关系
LightCAD.Core
├── 依赖: LightCAD.MathLib(数学计算)
└── 依赖: ThreeJs4Net.dll(字体管理 FontManager)
架构说明:Core对ThreeJs4Net的依赖主要来自字体管理功能(FontManager),这也是设计RenderUtils中间层的原因之一——使得在某些场景下可以绕过这个依赖。
5.2 文档模型(LcDocument)
5.2.1 文档结构
LcDocument是LightCAD中最顶层的数据容器,代表一个完整的CAD文档:
public class LcDocument
{
/// <summary>
/// 文档唯一标识
/// </summary>
public LcGuid Guid { get; set; }
/// <summary>
/// 文档名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 文档中的所有实体
/// </summary>
public EntityCollection Entities { get; }
/// <summary>
/// 图层管理器
/// </summary>
public LayerManager Layers { get; }
/// <summary>
/// 块定义集合
/// </summary>
public BlockCollection Blocks { get; }
/// <summary>
/// 线型管理
/// </summary>
public LineTypeManager LineTypes { get; }
/// <summary>
/// 文字样式管理
/// </summary>
public TextStyleManager TextStyles { get; }
/// <summary>
/// 标注样式管理
/// </summary>
public DimStyleManager DimStyles { get; }
/// <summary>
/// 文档单位设置
/// </summary>
public UnitSettings Units { get; set; }
/// <summary>
/// 文档变量
/// </summary>
public DocumentVariables Variables { get; }
}
5.2.2 实体集合管理
public class EntityCollection : IEnumerable<LcEntity>
{
private readonly List<LcEntity> entities = new();
/// <summary>
/// 添加实体
/// </summary>
public void Add(LcEntity entity)
{
entity.Document = ownerDocument;
entity.Handle = GenerateHandle();
entities.Add(entity);
OnEntityAdded(entity);
}
/// <summary>
/// 删除实体
/// </summary>
public bool Remove(LcEntity entity)
{
if (entities.Remove(entity))
{
entity.Document = null;
OnEntityRemoved(entity);
return true;
}
return false;
}
/// <summary>
/// 按类型查询
/// </summary>
public IEnumerable<T> OfType<T>() where T : LcEntity
{
return entities.OfType<T>();
}
/// <summary>
/// 空间查询(包围盒过滤)
/// </summary>
public IEnumerable<LcEntity> FindInBounds(BoundingBox2d bounds)
{
return entities.Where(e => e.Bounds.Intersects(bounds));
}
}
5.2.3 图层系统
public class LayerManager
{
private readonly Dictionary<string, Layer> layers = new();
/// <summary>
/// 默认图层 "0"
/// </summary>
public Layer DefaultLayer { get; }
/// <summary>
/// 当前活动图层
/// </summary>
public Layer CurrentLayer { get; set; }
/// <summary>
/// 创建新图层
/// </summary>
public Layer Create(string name)
{
var layer = new Layer(name);
layers[name] = layer;
return layer;
}
/// <summary>
/// 获取图层
/// </summary>
public Layer Get(string name)
{
return layers.TryGetValue(name, out var layer) ? layer : null;
}
}
public class Layer
{
public string Name { get; set; }
public Color Color { get; set; }
public string LineType { get; set; }
public double LineWeight { get; set; }
public bool IsVisible { get; set; } = true;
public bool IsLocked { get; set; } = false;
public bool IsFrozen { get; set; } = false;
}
5.3 实体基类(LcEntity)
5.3.1 实体基类定义
LcEntity是所有CAD实体的基类,定义了实体的通用属性和行为:
public abstract class LcEntity
{
/// <summary>
/// 实体的唯一句柄
/// </summary>
public long Handle { get; internal set; }
/// <summary>
/// 全局唯一标识符
/// </summary>
public LcGuid Guid { get; set; }
/// <summary>
/// 所属文档
/// </summary>
public LcDocument Document { get; internal set; }
/// <summary>
/// 所在图层
/// </summary>
public string LayerName { get; set; } = "0";
/// <summary>
/// 颜色
/// </summary>
public LcColor Color { get; set; }
/// <summary>
/// 线型
/// </summary>
public string LineType { get; set; }
/// <summary>
/// 线宽
/// </summary>
public double LineWeight { get; set; }
/// <summary>
/// 是否可见
/// </summary>
public bool IsVisible { get; set; } = true;
/// <summary>
/// 包围盒
/// </summary>
public abstract BoundingBox2d Bounds { get; }
/// <summary>
/// 实体类型标识
/// </summary>
public abstract string TypeName { get; }
/// <summary>
/// 几何变换
/// </summary>
public abstract void TransformBy(Matrix4d matrix);
/// <summary>
/// 克隆实体
/// </summary>
public abstract LcEntity Clone();
/// <summary>
/// 计算与点的最近距离
/// </summary>
public abstract double DistanceTo(Point2d point);
}
5.3.2 实体类层次结构
LcEntity(抽象基类)
│
├── 二维基本图元
│ ├── LcLine # 直线
│ ├── LcArc # 圆弧
│ ├── LcCircle # 圆
│ ├── LcEllipse # 椭圆
│ ├── LcPolyline # 多段线
│ ├── LcPolygon # 多边形
│ └── LcSpline # 样条曲线
│
├── 三维图元
│ ├── LcLine3d # 三维直线
│ ├── LcArc3d # 三维圆弧
│ ├── LcCircle3d # 三维圆
│ ├── LcCurve3d # 三维曲线
│ ├── LcProfile3d # 三维轮廓
│ ├── LcWorkPlane3d # 工作平面
│ ├── LcGroup3 # 三维组
│ ├── LcArray3 # 三维阵列
│ └── LcContianerRef3 # 容器引用
│
├── 实体建模
│ ├── LcSolid3d # 实体基类(26KB)
│ ├── LcExtrude3d # 拉伸体
│ ├── LcRevolve3d # 旋转体
│ ├── LcLoft3d # 放样体
│ ├── LcBlend3d # 融合体
│ ├── LcSweptBlend3d # 扫掠融合体
│ ├── LcPushPull3d # 推拉体
│ ├── LcMesh3d # 网格体
│ └── LcCombination # 布尔运算组合体
│
├── 标注元素
│ ├── LcDimension # 标注基类
│ ├── LcDimLinear # 线性标注
│ ├── LcDimAngular # 角度标注
│ ├── LcDimRadial # 半径标注
│ ├── LcDimDiameter # 直径标注
│ └── LcDim3d # 三维标注
│
├── 文本和表格
│ ├── LcText # 文本(15KB)
│ └── LcTable # 表格
│
└── 引用元素
├── LcBlockRef # 块引用
└── LcComponentRef # 组件引用
5.4 元素类型系统(ElementType)
5.4.1 ElementType类
ElementType是LightCAD中定义自定义元素类型的核心机制。每个自定义元素都需要对应一个ElementType实例:
public class ElementType
{
/// <summary>
/// 唯一标识符
/// </summary>
public LcGuid Guid { get; set; }
/// <summary>
/// 内部名称(用于代码引用)
/// </summary>
public string Name { get; set; }
/// <summary>
/// 显示名称(用于UI显示)
/// </summary>
public string DisplayName { get; set; }
/// <summary>
/// 元素类别
/// </summary>
public string Category { get; set; }
/// <summary>
/// 创建器(工厂方法)
/// </summary>
public Func<LcEntity> Creator { get; set; }
/// <summary>
/// 属性定义集合
/// </summary>
public List<PropertyDefinition> Properties { get; set; }
/// <summary>
/// 图标资源路径
/// </summary>
public string IconPath { get; set; }
/// <summary>
/// 是否为三维元素
/// </summary>
public bool Is3D { get; set; }
}
5.4.2 元素类型注册
public class ElementTypeRegistry
{
private static readonly Dictionary<LcGuid, ElementType> types = new();
/// <summary>
/// 注册元素类型
/// </summary>
public static void Register(ElementType type)
{
if (types.ContainsKey(type.Guid))
throw new InvalidOperationException(
$"元素类型 {type.Name} 已注册");
types[type.Guid] = type;
}
/// <summary>
/// 按GUID获取元素类型
/// </summary>
public static ElementType GetByGuid(LcGuid guid)
{
return types.TryGetValue(guid, out var type) ? type : null;
}
/// <summary>
/// 按名称获取元素类型
/// </summary>
public static ElementType GetByName(string name)
{
return types.Values.FirstOrDefault(t => t.Name == name);
}
/// <summary>
/// 获取某个类别下的所有类型
/// </summary>
public static IEnumerable<ElementType> GetByCategory(string category)
{
return types.Values.Where(t => t.Category == category);
}
}
5.4.3 自定义元素类型示例
// 注册一个自定义的"门"元素类型
var doorType = new ElementType
{
Guid = new LcGuid("door-standard-001"),
Name = "Door",
DisplayName = "标准门",
Category = "建筑",
Is3D = true,
Creator = () => new DoorEntity(),
Properties = new List<PropertyDefinition>
{
new PropertyDefinition("Width", typeof(double), 900.0),
new PropertyDefinition("Height", typeof(double), 2100.0),
new PropertyDefinition("OpenDirection", typeof(string), "左开"),
new PropertyDefinition("Material", typeof(string), "木质")
}
};
ElementTypeRegistry.Register(doorType);
5.5 属性系统
5.5.1 属性定义
public class PropertyDefinition
{
/// <summary>
/// 属性名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 属性类型
/// </summary>
public Type ValueType { get; set; }
/// <summary>
/// 默认值
/// </summary>
public object DefaultValue { get; set; }
/// <summary>
/// 显示名称
/// </summary>
public string DisplayName { get; set; }
/// <summary>
/// 属性分组
/// </summary>
public string Group { get; set; }
/// <summary>
/// 是否只读
/// </summary>
public bool IsReadOnly { get; set; }
/// <summary>
/// 值验证器
/// </summary>
public Func<object, bool> Validator { get; set; }
public PropertyDefinition(string name, Type valueType, object defaultValue)
{
Name = name;
ValueType = valueType;
DefaultValue = defaultValue;
DisplayName = name;
}
}
5.5.2 属性值存储
public class PropertyBag
{
private readonly Dictionary<string, object> values = new();
private readonly List<PropertyDefinition> definitions;
public PropertyBag(List<PropertyDefinition> definitions)
{
this.definitions = definitions;
// 初始化默认值
foreach (var def in definitions)
{
values[def.Name] = def.DefaultValue;
}
}
/// <summary>
/// 获取属性值
/// </summary>
public T Get<T>(string name)
{
if (values.TryGetValue(name, out var value))
return (T)value;
throw new KeyNotFoundException($"属性 {name} 不存在");
}
/// <summary>
/// 设置属性值
/// </summary>
public void Set(string name, object value)
{
var def = definitions.Find(d => d.Name == name);
if (def == null)
throw new KeyNotFoundException($"属性 {name} 未定义");
if (def.IsReadOnly)
throw new InvalidOperationException($"属性 {name} 为只读");
if (def.Validator != null && !def.Validator(value))
throw new ArgumentException($"属性 {name} 的值无效");
values[name] = value;
}
}
5.6 变更通知系统
5.6.1 IUpdateObject接口
/// <summary>
/// 实体变更通知接口
/// </summary>
public interface IUpdateObject
{
/// <summary>
/// 当实体属性或几何发生变化时调用
/// </summary>
void OnUpdate(UpdateContext context);
/// <summary>
/// 是否需要更新
/// </summary>
bool NeedsUpdate { get; }
}
public class UpdateContext
{
/// <summary>
/// 更新类型
/// </summary>
public UpdateType Type { get; set; }
/// <summary>
/// 更新来源
/// </summary>
public object Source { get; set; }
/// <summary>
/// 更新数据
/// </summary>
public Dictionary<string, object> Data { get; set; }
}
public enum UpdateType
{
GeometryChanged, // 几何数据改变
PropertyChanged, // 属性改变
StyleChanged, // 样式改变
TransformChanged, // 变换改变
VisibilityChanged, // 可见性改变
ParentChanged // 父级改变
}
5.6.2 实体变更通知流程
public class LcLine : LcEntity, IUpdateObject
{
private Point2d startPoint;
private Point2d endPoint;
public Point2d StartPoint
{
get => startPoint;
set
{
if (!startPoint.IsEqualTo(value))
{
startPoint = value;
NeedsUpdate = true;
OnUpdate(new UpdateContext
{
Type = UpdateType.GeometryChanged,
Source = this,
Data = new Dictionary<string, object>
{
["Property"] = nameof(StartPoint)
}
});
}
}
}
public bool NeedsUpdate { get; private set; }
public void OnUpdate(UpdateContext context)
{
// 重新计算包围盒
RecalculateBounds();
// 通知文档
Document?.OnEntityChanged(this, context);
NeedsUpdate = false;
}
}
5.7 LcGuid标识系统
5.7.1 LcGuid设计
LightCAD使用自定义的LcGuid类型来标识实体和元素类型,而不是直接使用System.Guid:
public struct LcGuid : IEquatable<LcGuid>
{
private readonly string value;
public LcGuid(string value)
{
this.value = value ?? throw new ArgumentNullException(nameof(value));
}
public static LcGuid NewGuid()
{
return new LcGuid(Guid.NewGuid().ToString());
}
public bool Equals(LcGuid other)
{
return string.Equals(value, other.value,
StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode()
{
return value?.GetHashCode(StringComparison.OrdinalIgnoreCase) ?? 0;
}
public override string ToString() => value;
}
5.8 颜色系统
5.8.1 LcColor
public struct LcColor
{
public byte R { get; set; }
public byte G { get; set; }
public byte B { get; set; }
public byte A { get; set; }
/// <summary>
/// 颜色来源
/// </summary>
public ColorSource Source { get; set; }
/// <summary>
/// AutoCAD颜色索引
/// </summary>
public int AciIndex { get; set; }
// 预定义颜色
public static readonly LcColor Red = new LcColor(255, 0, 0);
public static readonly LcColor Green = new LcColor(0, 255, 0);
public static readonly LcColor Blue = new LcColor(0, 0, 255);
public static readonly LcColor White = new LcColor(255, 255, 255);
public static readonly LcColor Black = new LcColor(0, 0, 0);
public static readonly LcColor ByLayer = new LcColor { Source = ColorSource.ByLayer };
public static readonly LcColor ByBlock = new LcColor { Source = ColorSource.ByBlock };
}
public enum ColorSource
{
Explicit, // 直接指定颜色
ByLayer, // 随层颜色
ByBlock // 随块颜色
}
5.9 坐标系与单位
5.9.1 坐标系统
LightCAD使用标准的右手坐标系:
Y
↑
|
|
+------→ X
/
/
Z(指向屏幕外)
5.9.2 单位系统
public class UnitSettings
{
/// <summary>
/// 长度单位
/// </summary>
public LengthUnit LengthUnit { get; set; } = LengthUnit.Millimeter;
/// <summary>
/// 角度单位
/// </summary>
public AngleUnit AngleUnit { get; set; } = AngleUnit.Degree;
/// <summary>
/// 显示精度(小数位数)
/// </summary>
public int Precision { get; set; } = 4;
/// <summary>
/// 单位转换
/// </summary>
public double ConvertTo(double value, LengthUnit targetUnit)
{
// 先转换为毫米,再转换为目标单位
var mmValue = value * GetMillimeterFactor(LengthUnit);
return mmValue / GetMillimeterFactor(targetUnit);
}
}
public enum LengthUnit
{
Millimeter, // 毫米
Centimeter, // 厘米
Meter, // 米
Inch, // 英寸
Foot // 英尺
}
public enum AngleUnit
{
Degree, // 度
Radian, // 弧度
Gradian // 百分度
}
5.10 文档序列化
5.10.1 序列化框架
LightCAD的文档序列化支持将完整的文档模型保存到文件和从文件加载:
public class DocumentSerializer
{
/// <summary>
/// 保存文档到文件
/// </summary>
public void Save(LcDocument document, string filePath)
{
using var stream = File.Create(filePath);
using var writer = new BinaryWriter(stream);
// 写入文件头
WriteHeader(writer, document);
// 写入图层表
WriteLayers(writer, document.Layers);
// 写入实体
WriteEntities(writer, document.Entities);
// 写入块定义
WriteBlocks(writer, document.Blocks);
}
/// <summary>
/// 从文件加载文档
/// </summary>
public LcDocument Load(string filePath)
{
using var stream = File.OpenRead(filePath);
using var reader = new BinaryReader(stream);
var document = new LcDocument();
// 读取文件头
ReadHeader(reader, document);
// 读取图层表
ReadLayers(reader, document);
// 读取实体
ReadEntities(reader, document);
// 读取块定义
ReadBlocks(reader, document);
return document;
}
}
5.11 Core模块的扩展方式
5.11.1 创建自定义实体
要创建新的自定义实体,需要继承LcEntity并实现所有抽象方法:
public class LcCustomElement : LcEntity, IUpdateObject
{
// 自定义几何数据
public Point2d Position { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public double Rotation { get; set; }
// 自定义属性
public PropertyBag Properties { get; }
public override string TypeName => "CustomElement";
public override BoundingBox2d Bounds
{
get
{
// 计算旋转后的包围盒
var corners = GetCorners();
return BoundingBox2d.FromPoints(corners);
}
}
public override void TransformBy(Matrix4d matrix)
{
var p3d = new Point3d(Position.X, Position.Y, 0);
var transformed = matrix.Transform(p3d);
Position = new Point2d(transformed.X, transformed.Y);
}
public override LcEntity Clone()
{
return new LcCustomElement
{
Position = this.Position,
Width = this.Width,
Height = this.Height,
Rotation = this.Rotation,
Color = this.Color,
LayerName = this.LayerName
};
}
public override double DistanceTo(Point2d point)
{
// 计算点到元素的最近距离
return GeometryUtils.PointToRectDistance(
point, Position, Width, Height, Rotation);
}
// IUpdateObject实现
public bool NeedsUpdate { get; private set; }
public void OnUpdate(UpdateContext context)
{
NeedsUpdate = false;
}
}
5.11.2 注册自定义元素类型
// 在插件初始化时注册
public class MyPlugin
{
public void Initialize()
{
var customType = new ElementType
{
Guid = new LcGuid("my-custom-element-type"),
Name = "CustomElement",
DisplayName = "自定义元素",
Category = "自定义",
Is3D = false,
Creator = () => new LcCustomElement
{
Width = 100,
Height = 50
}
};
ElementTypeRegistry.Register(customType);
}
}
5.12 本章小结
本章详细介绍了LightCAD.Core模块的核心数据模型,包括文档结构(LcDocument)、实体基类(LcEntity)、元素类型系统(ElementType)、属性系统、变更通知机制、颜色系统和坐标单位等。理解这些数据模型是后续学习二维图元、三维建模和渲染系统的基础。Core模块的设计体现了良好的面向对象原则,通过接口和抽象类提供了强大的扩展能力。
上一章:第四章:数学库详解
下一章:第六章:二维图元系统