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

推荐订阅源

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-19 · via 博客园 - 我才是银古

第五章:元素类型系统详解

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(全局唯一标识符)在元素类型系统中起到关键作用:

  1. 唯一性:确保每种元素类型在全球范围内唯一
  2. 持久化:文件保存和加载时识别元素类型
  3. 版本兼容:新版本软件能识别旧版本创建的元素
// 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的元素类型系统:

  1. ElementType定义:GUID、名称、类型的配置
  2. 元素基类:DirectComponent和LcElement的继承关系
  3. 草坪实现:QdLawn的完整实现
  4. 基坑实现:专业属性(标高、放坡)的处理
  5. 属性系统:LcParameterSet和PropertyObserver
  6. 几何数据:Polyline2d、Curve2d的使用
  7. 元素注册:类型和操作类的注册
  8. 自定义元素:创建新元素类型的完整流程

理解元素类型系统后,下一章我们将学习场布元素的具体实现细节。