元素类型系统详解
第五章:元素类型系统详解
5.1 元素类型概述
5.1.1 ElementType类
ElementType是LightCAD中定义元素类型的核心类,每个自定义元素都需要对应一个ElementType实例:
public class ElementType
{
/// <summary>
/// 唯一标识符
/// </summary>
public LcGuid Guid { get; set; }
/// <summary>
/// 内部名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 显示名称
/// </summary>
public string DispalyName { get; set; }
/// <summary>
/// 元素类类型
/// </summary>
public Type ClassType { get; set; }
}
5.1.2 GUID的作用
GUID(全局唯一标识符)在元素类型系统中起到关键作用:
- 唯一性:确保每种元素类型在全球范围内唯一
- 持久化:文件保存和加载时识别元素类型
- 版本兼容:新版本软件能识别旧版本创建的元素
// GUID格式示例
Guid = Guid.ParseExact("{63A566EA-3702-A98C-7A6B-8DBEA6B3F41A}", "B").ToLcGuid()
// 生成新GUID
Guid = Guid.NewGuid().ToLcGuid()
5.1.3 FY_Layout元素类型定义
public static class LayoutElementType
{
// 草坪
public static ElementType Lawn = new ElementType
{
Guid = Guid.ParseExact("{63A566EA-3702-A98C-7A6B-8DBEA6B3F41A}", "B").ToLcGuid(),
Name = "Lawn",
DispalyName = "草坪",
ClassType = typeof(QdLawn)
};
// 板房
public static ElementType PlateBuilding = new ElementType
{
Guid = Guid.ParseExact("{63A523EA-3702-A98C-7A6B-8DBEA5C3F42B}", "B").ToLcGuid(),
Name = "PlateBuilding",
DispalyName = "板房",
ClassType = typeof(PlateBuilding)
};
// 板房楼栋
public static ElementType PlateBuildGroup = new ElementType
{
Guid = Guid.ParseExact("{C3A523EA-3702-A98C-7A6B-8DBEA2D2F4D4}", "B").ToLcGuid(),
Name = "PlateBuildGroup",
DispalyName = "板房楼栋",
ClassType = typeof(PlateBuildGroup)
};
// 基坑
public static ElementType FoundationPit = new ElementType
{
Guid = Guid.ParseExact("{C082E6EA-E099-94AB-50FA-50DF72D286D4}", "B").ToLcGuid(),
Name = "FoundationPit",
DispalyName = "基坑",
ClassType = typeof(QdFoundationPit)
};
// 围栏
public static ElementType Fence = new ElementType
{
Guid = Guid.ParseExact("{5A9EC5E6-09BF-4DB3-9AD2-DD0DA74F099A}", "B").ToLcGuid(),
Name = "Fence",
DispalyName = "围墙",
ClassType = typeof(QdFence)
};
// 拟建建筑
public static ElementType PlanBuild = new ElementType
{
Guid = Guid.ParseExact("{5A5EC5E6-06BF-4DB3-4AD2-DD0DA52F078A}", "B").ToLcGuid(),
Name = "PlanBuild",
DispalyName = "拟建建筑",
ClassType = typeof(QdPlanBuild)
};
// 硬化地面/道路
public static ElementType Road = new ElementType
{
Guid = Guid.ParseExact("{85AB36C8-FBB1-424B-C7C4-1F92576EC5BD}", "B").ToLcGuid(),
Name = "Road",
DispalyName = "硬化地面",
ClassType = typeof(QdRoad)
};
// 土方回填
public static ElementType Earthwork = new ElementType
{
Guid = Guid.ParseExact("{B20D7BB7-6209-B3DB-2761-E1197E59723E}", "B").ToLcGuid(),
Name = "Earthwork",
DispalyName = "土方回填",
ClassType = typeof(QdEarthwork)
};
// 出土道路
public static ElementType Berm = new ElementType
{
Guid = Guid.ParseExact("{982B310D-627F-9168-6A63-1257A26BDDF8}", "B").ToLcGuid(),
Name = "Berm",
DispalyName = "出土道路",
ClassType = typeof(QdBerm)
};
// 路面硬化
public static ElementType Harden = new ElementType
{
Guid = Guid.ParseExact("{897700F4-F50B-62EE-3900-59D967BEBC85}", "B").ToLcGuid(),
Name = "Harden",
DispalyName = "路面硬化",
ClassType = typeof(QdHarden)
};
// 防护栏杆
public static ElementType Barrier = new ElementType
{
Guid = Guid.ParseExact("{6A8E5173-8D53-4542-8272-5B1A6680AFD2}", "B").ToLcGuid(),
Name = "Barrier",
DispalyName = "防护栏杆",
ClassType = typeof(QdBarrier)
};
// 场地
public static ElementType Site = new ElementType
{
Guid = Guid.ParseExact("{15AC9FC1-9BC3-4D11-DFDB-3426FAE73336}", "B").ToLcGuid(),
Name = "Site",
DispalyName = "场地",
ClassType = typeof(QdSite)
};
// 硬化地面
public static ElementType Ground = new ElementType
{
Guid = Guid.ParseExact("{85AB36C8-FBB1-424B-C7C4-1F92576EC5BD}", "B").ToLcGuid(),
Name = "Ground",
DispalyName = "硬化地面",
ClassType = typeof(QdGround)
};
// 用地红线
public static ElementType PropertyLine = new ElementType
{
Guid = Guid.ParseExact("{6FF93D96-A3D3-DAC7-D720-8497DA8E3A9A}", "B").ToLcGuid(),
Name = "PropertyLine",
DispalyName = "用地红线",
ClassType = typeof(QdPropertyLine)
};
// 开门边线
public static ElementType OpenLine = new ElementType
{
Guid = Guid.ParseExact("{6FE950F0-9E13-FA8A-E216-AAE0D40BECBC}", "B").ToLcGuid(),
Name = "OpenLine",
DispalyName = "开门边线",
ClassType = typeof(QdOpenLine)
};
// 场布设备
public static ElementType LayoutEquipment = new ElementType
{
Guid = Guid.ParseExact("{552E316C-ADB2-43A7-AC77-9E0069ACEDC8}", "B").ToLcGuid(),
Name = "LayoutEquipment",
DispalyName = "场布设备",
ClassType = typeof(QdLayoutEquipment)
};
// 注册所有元素类型
public static ElementType[] All = new ElementType[]
{
Lawn, FoundationPit, Road, Earthwork, Berm,
Harden, Site, PropertyLine, Fence, PlateBuilding,
PlateBuildGroup, OpenLine
};
}
5.2 元素基类
5.2.1 DirectComponent基类
FY_Layout的场布元素都继承自DirectComponent类:
public abstract class DirectComponent : LcElement
{
// 组件定义
public LcComponentDefinition Definition { get; protected set; }
// 属性集
public LcParameterSet Properties { get; protected set; }
// 基础曲线(用于闭合区域元素)
public Curve2d BaseCurve { get; set; }
// 获取形状
public virtual Curve2dGroupCollection GetShapes() { ... }
// 初始化
public virtual void Initilize(LcDocument doc) { ... }
// 设置属性
protected void SetProps(params (int propId, object value)[] props) { ... }
}
5.2.2 LcElement基类
所有元素的基类,提供核心功能:
public abstract class LcElement
{
// 元素ID
public LcGuid Id { get; set; }
// 元素类型
public ElementType Type { get; set; }
// 所属文档
public LcDocument Document { get; set; }
// 图层名称
public string Layer { get; set; }
// 可见性
public bool Visible { get; set; }
// 包围盒
public Box2 BoundingBox { get; protected set; }
// 抽象方法
public abstract Box2 GetBoundingBox();
public abstract LcElement Clone();
public virtual void Copy(LcElement src) { ... }
// 平移
public virtual void Translate(Vector2 offset) { ... }
// 旋转
public virtual void Rotate(Vector2 center, double angle) { ... }
// 缩放
public virtual void Scale(Vector2 center, double scale) { ... }
}
5.3 草坪元素实现
5.3.1 QdLawn类
namespace QdLayout
{
public class QdLawn : DirectComponent
{
/// <summary>
/// 底部高度
/// </summary>
public double Bottom
{
get => Properties.GetValue<double>("Bottom");
set => SetProps((GetPropId(nameof(Bottom)), value));
}
/// <summary>
/// 材质
/// </summary>
public MaterialInfo Material
{
get => Properties.GetValue<MaterialInfo>("Material");
set => SetProps((GetPropId(nameof(Material)), value));
}
/// <summary>
/// 轮廓
/// </summary>
public Polyline2d Outline
{
get => this.BaseCurve as Polyline2d;
set => this.BaseCurve = value;
}
/// <summary>
/// 构造函数
/// </summary>
public QdLawn(QdLawnDef lawnDef) : base(lawnDef)
{
Type = LayoutElementType.Lawn;
Outline = new Polyline2d();
}
/// <summary>
/// 计算包围盒
/// </summary>
public override Box2 GetBoundingBox()
{
return new Box2().ExpandByPoints(
GetShapes()[0].Curve2ds.SelectMany(n => n.GetPoints()).ToArray()
);
}
/// <summary>
/// 批量插入后回调
/// </summary>
public override void OnBatchInsertAfter()
{
ResetBoundingBox();
}
/// <summary>
/// 克隆元素
/// </summary>
public override LcElement Clone()
{
var clone = new QdLawn(Definition as QdLawnDef);
clone.Copy(this);
return clone;
}
/// <summary>
/// 复制属性
/// </summary>
public override void Copy(LcElement src)
{
base.Copy(src);
var lawn = (QdLawn)src;
// 复制特有属性...
}
}
}
5.3.2 QdLawnDef定义类
namespace QdLayout
{
public class QdLawnDef : LcComponentDefinition
{
// 组件定义属性
// 通常在构件库中定义
}
}
5.4 基坑元素实现
5.4.1 QdFoundationPit类
基坑元素相比草坪有更多的专业属性:
namespace QdLayout
{
public class QdFoundationPit : DirectComponent
{
/// <summary>
/// 轮廓
/// </summary>
public Polyline2d Outline
{
get => this.BaseCurve as Polyline2d;
set => this.BaseCurve = value;
}
/// <summary>
/// 底部标高
/// </summary>
public double Bottom
{
get => Properties.GetValue<double>("Bottom");
set
{
OnPropertyChangedBefore("Bottom", Properties.GetValue<double>("Bottom"), value);
Properties.SetValue("Bottom", value);
ResetCache();
OnPropertyChangedAfter("Bottom", Properties.GetValue<double>("Bottom"), value);
}
}
/// <summary>
/// 顶部标高
/// </summary>
public double Elevation
{
get => Properties.GetValue<double>("Elevation");
set
{
OnPropertyChangedBefore("Elevation", Properties.GetValue<double>("Elevation"), value);
Properties.SetValue("Elevation", value);
ResetCache();
OnPropertyChangedAfter("Elevation", Properties.GetValue<double>("Elevation"), value);
}
}
/// <summary>
/// 放坡系数
/// </summary>
public double Factor
{
get => Properties.GetValue<double>("Factor");
set
{
OnPropertyChangedBefore("Factor", Properties.GetValue<double>("Factor"), value);
Properties.SetValue("Factor", value);
ResetCache();
OnPropertyChangedAfter("Factor", Properties.GetValue<double>("Factor"), value);
}
}
/// <summary>
/// 放坡模式:0-向外放坡,1-向内放坡
/// </summary>
public int Pattern
{
get => Properties.GetValue<int>("Pattern");
set
{
OnPropertyChangedBefore("Pattern", Properties.GetValue<int>("Pattern"), value);
Properties.SetValue("Pattern", value);
ResetCache();
OnPropertyChangedAfter("Pattern", Properties.GetValue<int>("Pattern"), value);
}
}
public QdFoundationPit(QdFoundationPitDef def) : base(def)
{
Type = LayoutElementType.FoundationPit;
Outline = new Polyline2d();
}
public override Box2 GetBoundingBox()
{
return new Box2().ExpandByPoints(
GetShapes()[0].Curve2ds.SelectMany(n => n.GetPoints()).ToArray()
);
}
public override LcElement Clone()
{
var clone = new QdFoundationPit(Definition as QdFoundationPitDef);
clone.Copy(this);
return clone;
}
}
}
5.4.2 放坡计算
基坑的关键特性是支持放坡计算:
放坡宽度 = (顶标高 - 底标高) × 放坡系数
向外放坡示意图:
┌───────────────────┐ ← 顶部轮廓(外扩)
/ \
/ ← 坡面 \
/ \
┼───────────────────────────┼ ← 底部轮廓(原轮廓)
向内放坡示意图:
┼───────────────────────────┼ ← 顶部轮廓(原轮廓)
\ /
\ ← 坡面 /
\ /
└───────────────────┘ ← 底部轮廓(内缩)
5.5 属性系统
5.5.1 LcParameterSet
属性集用于存储元素的参数化属性:
// 获取属性值
var value = Properties.GetValue<double>("Bottom");
// 设置属性值
Properties.SetValue("Bottom", -4000.0);
// 检查属性是否存在
if (Properties.ContainsKey("Factor"))
{
// ...
}
5.5.2 属性变化通知
// 属性变化前
OnPropertyChangedBefore(string propName, object oldValue, object newValue);
// 属性变化后
OnPropertyChangedAfter(string propName, object oldValue, object newValue);
// 示例实现
public double MyProperty
{
get => Properties.GetValue<double>("MyProperty");
set
{
var oldValue = Properties.GetValue<double>("MyProperty");
OnPropertyChangedBefore("MyProperty", oldValue, value);
Properties.SetValue("MyProperty", value);
ResetCache(); // 重置缓存,触发重绘
OnPropertyChangedAfter("MyProperty", oldValue, value);
}
}
5.5.3 PropertyObserver
属性观察者用于在属性面板中显示和编辑属性:
public override List<PropertyObserver> GetPropertyObservers()
{
return new List<PropertyObserver>
{
new PropertyObserver
{
Name = "Bottom",
DisplayName = "底高",
CategoryName = "Geometry",
CategoryDisplayName = "几何图形",
PropType = PropertyType.Double,
Getter = (ele) => (ele as QdFoundationPit).Bottom,
Setter = (ele, value) =>
{
if (!double.TryParse(value.ToString(), out var bottom))
return;
(ele as QdFoundationPit).Bottom = bottom;
}
},
new PropertyObserver
{
Name = "Pattern",
DisplayName = "放坡方式",
CategoryName = "Geometry",
CategoryDisplayName = "几何图形",
PropType = PropertyType.Array,
Source = (ele) => new string[] { "向外放坡", "向内放坡" },
Getter = (ele) => (ele as QdFoundationPit).Pattern == 0 ? "向外放坡" : "向内放坡",
Setter = (ele, value) =>
{
var pattern = value?.ToString() == "向外放坡" ? 0 : 1;
(ele as QdFoundationPit).Pattern = pattern;
}
}
};
}
5.6 几何数据
5.6.1 Polyline2d
多段线是场布元素最常用的几何数据类型:
// 创建多段线
var poly = new Polyline2d();
// 添加线段
poly.AddCurve(new Line2d(new Vector2(0, 0), new Vector2(100, 0)));
poly.AddCurve(new Line2d(new Vector2(100, 0), new Vector2(100, 100)));
poly.AddCurve(new Line2d(new Vector2(100, 100), new Vector2(0, 100)));
poly.AddCurve(new Line2d(new Vector2(0, 100), new Vector2(0, 0)));
// 获取点列表
var points = poly.Curve2ds.SelectMany(c => c.GetPoints()).ToList();
// 克隆
var cloned = poly.Clone() as Polyline2d;
// 反向
poly.Reverse();
5.6.2 Curve2d基类
// 线段
public class Line2d : Curve2d
{
public Vector2 Start { get; set; }
public Vector2 End { get; set; }
public Vector2 Dir => (End - Start).Normalize();
}
// 圆弧
public class Arc2d : Curve2d
{
public Vector2 Center { get; set; }
public double Radius { get; set; }
public double StartAngle { get; set; }
public double EndAngle { get; set; }
public bool IsClockwise { get; set; }
}
5.6.3 Curve2dGroup
曲线组用于存储多条曲线:
var curveGroup = new Curve2dGroup
{
Curve2ds = new ListEx<Curve2d>()
};
curveGroup.Curve2ds.Add(new Line2d(...));
curveGroup.Curve2ds.Add(new Arc2d(...));
5.7 元素注册
5.7.1 注册元素类型
在插件的Loaded()方法中注册:
public void Loaded()
{
// 批量注册元素类型
LcDocument.RegistElementTypes(LayoutElementType.All);
// 或单独注册
LcDocument.RegistElementType(LayoutElementType.Lawn);
LcDocument.RegistElementType(LayoutElementType.FoundationPit);
}
5.7.2 注册元素操作
public void Loaded()
{
// 注册二维操作类
LcDocument.ElementActions.Add(LayoutElementType.Lawn, new LawnAction());
LcDocument.ElementActions.Add(LayoutElementType.FoundationPit, new FoundationPitAction());
// 注册三维操作类
LcDocument.Element3dActions.Add(LayoutElementType.Lawn, new Lawn3dAction());
LcDocument.Element3dActions.Add(LayoutElementType.FoundationPit, new FoundationPit3dAction());
}
5.8 创建自定义元素类型
5.8.1 完整示例
下面是创建一个自定义"停车位"元素的完整示例:
1. 定义ElementType
// 在LayoutElementType.cs中添加
public static ElementType ParkingSpace = new ElementType
{
Guid = Guid.NewGuid().ToLcGuid(), // 生产环境使用固定GUID
Name = "ParkingSpace",
DispalyName = "停车位",
ClassType = typeof(QdParkingSpace)
};
2. 创建元素类
// ParkingSpace/QdParkingSpace.cs
namespace QdLayout
{
public class QdParkingSpace : DirectComponent
{
/// <summary>
/// 轮廓
/// </summary>
public Polyline2d Outline
{
get => this.BaseCurve as Polyline2d;
set => this.BaseCurve = value;
}
/// <summary>
/// 停车位宽度
/// </summary>
public double Width
{
get => Properties.GetValue<double>("Width");
set => SetProps((GetPropId(nameof(Width)), value));
}
/// <summary>
/// 停车位长度
/// </summary>
public double Length
{
get => Properties.GetValue<double>("Length");
set => SetProps((GetPropId(nameof(Length)), value));
}
/// <summary>
/// 停车位编号
/// </summary>
public string Number
{
get => Properties.GetValue<string>("Number");
set => SetProps((GetPropId(nameof(Number)), value));
}
public QdParkingSpace(QdParkingSpaceDef def) : base(def)
{
Type = LayoutElementType.ParkingSpace;
Outline = new Polyline2d();
Width = 2500; // 默认宽度2.5米
Length = 5000; // 默认长度5米
}
public override Box2 GetBoundingBox()
{
if (Outline?.Curve2ds?.Count > 0)
{
return new Box2().ExpandByPoints(
Outline.Curve2ds.SelectMany(n => n.GetPoints()).ToArray()
);
}
return new Box2();
}
public override LcElement Clone()
{
var clone = new QdParkingSpace(Definition as QdParkingSpaceDef);
clone.Copy(this);
return clone;
}
public override void Copy(LcElement src)
{
base.Copy(src);
var ps = (QdParkingSpace)src;
this.Width = ps.Width;
this.Length = ps.Length;
this.Number = ps.Number;
}
}
public class QdParkingSpaceDef : LcComponentDefinition
{
// 定义类
}
}
3. 更新注册列表
// 在LayoutElementType.All中添加
public static ElementType[] All = new ElementType[]
{
Lawn, FoundationPit, Road, ... , ParkingSpace
};
4. 在LayoutPlugin中注册操作类
public void Loaded()
{
// ...
LcDocument.ElementActions.Add(LayoutElementType.ParkingSpace, new ParkingSpaceAction());
}
5.9 元素的序列化
5.9.1 属性序列化
元素通过Properties自动序列化:
// 属性会自动参与序列化
public double Width
{
get => Properties.GetValue<double>("Width");
set => SetProps((GetPropId(nameof(Width)), value));
}
5.9.2 自定义序列化
对于复杂数据,可以实现自定义序列化:
public override void WriteToStream(BinaryWriter writer)
{
base.WriteToStream(writer);
// 写入自定义数据
writer.Write(CustomData.Length);
writer.Write(CustomData);
}
public override void ReadFromStream(BinaryReader reader)
{
base.ReadFromStream(reader);
// 读取自定义数据
var length = reader.ReadInt32();
CustomData = reader.ReadBytes(length);
}
5.10 本章小结
本章详细介绍了FY_Layout的元素类型系统:
- ElementType定义:GUID、名称、类型的配置
- 元素基类:DirectComponent和LcElement的继承关系
- 草坪实现:QdLawn的完整实现
- 基坑实现:专业属性(标高、放坡)的处理
- 属性系统:LcParameterSet和PropertyObserver
- 几何数据:Polyline2d、Curve2d的使用
- 元素注册:类型和操作类的注册
- 自定义元素:创建新元素类型的完整流程
理解元素类型系统后,下一章我们将学习场布元素的具体实现细节。

浙公网安备 33010602011771号