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

推荐订阅源

T
Tor Project blog
B
Blog RSS Feed
M
MIT News - Artificial intelligence
WordPress大学
WordPress大学
H
Hackread – Cybersecurity News, Data Breaches, AI and More
罗磊的独立博客
GbyAI
GbyAI
N
Netflix TechBlog - Medium
博客园 - 司徒正美
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
宝玉的分享
宝玉的分享
W
WeLiveSecurity
Stack Overflow Blog
Stack Overflow Blog
Y
Y Combinator Blog
SecWiki News
SecWiki News
V
Vulnerabilities – Threatpost
Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
T
Tailwind CSS Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Register - Security
The Register - Security
Cisco Talos Blog
Cisco Talos Blog
Martin Fowler
Martin Fowler
A
About on SuperTechFans
S
Security @ Cisco Blogs
T
Tenable Blog
C
Check Point Blog
N
News and Events Feed by Topic
S
SegmentFault 最新的问题
The GitHub Blog
The GitHub Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Attack and Defense Labs
Attack and Defense Labs
美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
Cisco Blogs
P
Palo Alto Networks Blog
V
V2EX
博客园 - 聂微东
Project Zero
Project Zero
酷 壳 – CoolShell
酷 壳 – CoolShell
D
Docker
N
News | PayPal Newsroom
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
小众软件
小众软件
Application and Cybersecurity Blog
Application and Cybersecurity Blog
人人都是产品经理
人人都是产品经理
V2EX - 技术
V2EX - 技术
I
Intezer
L
LINUX DO - 最新话题

博客园 - 我才是银古

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

第七章:三维图元系统

7.1 三维图元概述

7.1.1 三维图元分类

LightCAD的三维图元位于LightCAD.Core/Elements/Element3d/目录下,提供了完整的三维空间几何实体支持:

图元类型 类名 说明
三维直线 LcLine3d 三维空间中的线段
三维圆弧 LcArc3d 三维空间中的圆弧
三维圆 LcCircle3d 三维空间中的圆
三维曲线 LcCurve3d 通用三维曲线
三维轮廓 LcProfile3d 用于拉伸等操作的截面轮廓
工作平面 LcWorkPlane3d 自定义工作坐标系
三维组 LcGroup3 三维实体的分组容器
三维阵列 LcArray3 三维空间中的阵列复制
容器引用 LcContianerRef3 组件容器引用

7.1.2 二维与三维图元的关系

二维图元(在XY平面上)        三维图元(在任意空间位置)
LcLine    ──对应──>    LcLine3d
LcArc     ──对应──>    LcArc3d
LcCircle  ──对应──>    LcCircle3d
LcSpline  ──对应──>    LcCurve3d

三维图元在二维图元的基础上增加了Z坐标和空间方位信息,能够表示任意空间位置和方向的几何实体。

7.2 三维直线(LcLine3d)

7.2.1 数据结构

public class LcLine3d : LcEntity, IUpdateObject
{
    /// <summary>
    /// 起点(三维坐标)
    /// </summary>
    public Point3d StartPoint { get; set; }

    /// <summary>
    /// 终点(三维坐标)
    /// </summary>
    public Point3d EndPoint { get; set; }

    /// <summary>
    /// 长度
    /// </summary>
    public double Length => StartPoint.DistanceTo(EndPoint);

    /// <summary>
    /// 方向向量
    /// </summary>
    public Vector3d Direction =>
        new Vector3d(
            EndPoint.X - StartPoint.X,
            EndPoint.Y - StartPoint.Y,
            EndPoint.Z - StartPoint.Z
        ).Normalize();

    /// <summary>
    /// 中点
    /// </summary>
    public Point3d MidPoint => new Point3d(
        (StartPoint.X + EndPoint.X) / 2,
        (StartPoint.Y + EndPoint.Y) / 2,
        (StartPoint.Z + EndPoint.Z) / 2
    );

    public override string TypeName => "Line3d";

    /// <summary>
    /// 投影到XY平面获取二维线段
    /// </summary>
    public LcLine ToLine2d()
    {
        return new LcLine
        {
            StartPoint = StartPoint.ToPoint2d(),
            EndPoint = EndPoint.ToPoint2d()
        };
    }

    /// <summary>
    /// 计算点到三维直线的距离
    /// </summary>
    public double DistanceTo3d(Point3d point)
    {
        var ab = new Vector3d(
            EndPoint.X - StartPoint.X,
            EndPoint.Y - StartPoint.Y,
            EndPoint.Z - StartPoint.Z);
        var ap = new Vector3d(
            point.X - StartPoint.X,
            point.Y - StartPoint.Y,
            point.Z - StartPoint.Z);

        var cross = ab.Cross(ap);
        return cross.Length / ab.Length;
    }

    public override void TransformBy(Matrix4d matrix)
    {
        StartPoint = matrix.Transform(StartPoint);
        EndPoint = matrix.Transform(EndPoint);
    }
}

7.2.2 使用示例

// 创建一条在三维空间中的直线
var line3d = new LcLine3d
{
    StartPoint = new Point3d(0, 0, 0),
    EndPoint = new Point3d(100, 50, 75)
};

// 获取直线信息
Console.WriteLine($"长度: {line3d.Length}");
Console.WriteLine($"方向: {line3d.Direction}");
Console.WriteLine($"中点: {line3d.MidPoint}");

// 投影到二维
var line2d = line3d.ToLine2d();

7.3 三维圆弧(LcArc3d)

7.3.1 数据结构

三维圆弧需要额外的方向信息来确定其在空间中的位置:

public class LcArc3d : LcEntity, IUpdateObject
{
    /// <summary>
    /// 圆弧中心(三维坐标)
    /// </summary>
    public Point3d Center { get; set; }

    /// <summary>
    /// 半径
    /// </summary>
    public double Radius { get; set; }

    /// <summary>
    /// 圆弧所在平面的法向量
    /// </summary>
    public Vector3d Normal { get; set; } = Vector3d.ZAxis;

    /// <summary>
    /// 起始角度
    /// </summary>
    public double StartAngle { get; set; }

    /// <summary>
    /// 终止角度
    /// </summary>
    public double EndAngle { get; set; }

    /// <summary>
    /// X轴方向(定义角度参考方向)
    /// </summary>
    public Vector3d XDirection { get; set; } = Vector3d.XAxis;

    public override string TypeName => "Arc3d";

    /// <summary>
    /// 获取圆弧所在的平面
    /// </summary>
    public Plane GetPlane()
    {
        return new Plane(Center, Normal);
    }

    /// <summary>
    /// 获取三维起点
    /// </summary>
    public Point3d StartPoint3d
    {
        get
        {
            var yDir = Normal.Cross(XDirection).Normalize();
            return new Point3d(
                Center.X + Radius * (Math.Cos(StartAngle) * XDirection.X +
                    Math.Sin(StartAngle) * yDir.X),
                Center.Y + Radius * (Math.Cos(StartAngle) * XDirection.Y +
                    Math.Sin(StartAngle) * yDir.Y),
                Center.Z + Radius * (Math.Cos(StartAngle) * XDirection.Z +
                    Math.Sin(StartAngle) * yDir.Z)
            );
        }
    }

    /// <summary>
    /// 离散化为三维点序列
    /// </summary>
    public List<Point3d> Tessellate(int segments = 32)
    {
        var points = new List<Point3d>();
        var yDir = Normal.Cross(XDirection).Normalize();
        var sweep = EndAngle - StartAngle;
        if (sweep < 0) sweep += 2 * Math.PI;

        for (int i = 0; i <= segments; i++)
        {
            var t = (double)i / segments;
            var angle = StartAngle + sweep * t;
            var cos = Math.Cos(angle);
            var sin = Math.Sin(angle);

            points.Add(new Point3d(
                Center.X + Radius * (cos * XDirection.X + sin * yDir.X),
                Center.Y + Radius * (cos * XDirection.Y + sin * yDir.Y),
                Center.Z + Radius * (cos * XDirection.Z + sin * yDir.Z)
            ));
        }

        return points;
    }
}

7.4 三维圆(LcCircle3d)

7.4.1 数据结构

public class LcCircle3d : LcEntity, IUpdateObject
{
    /// <summary>
    /// 圆心(三维坐标)
    /// </summary>
    public Point3d Center { get; set; }

    /// <summary>
    /// 半径
    /// </summary>
    public double Radius { get; set; }

    /// <summary>
    /// 法向量(圆所在平面的法线)
    /// </summary>
    public Vector3d Normal { get; set; } = Vector3d.ZAxis;

    /// <summary>
    /// 周长
    /// </summary>
    public double Circumference => 2 * Math.PI * Radius;

    /// <summary>
    /// 面积
    /// </summary>
    public double Area => Math.PI * Radius * Radius;

    public override string TypeName => "Circle3d";

    /// <summary>
    /// 获取圆上指定角度的三维点
    /// </summary>
    public Point3d PointAt(double angle)
    {
        // 构建局部坐标系
        var xDir = GetLocalXDirection();
        var yDir = Normal.Cross(xDir).Normalize();

        return new Point3d(
            Center.X + Radius * (Math.Cos(angle) * xDir.X +
                Math.Sin(angle) * yDir.X),
            Center.Y + Radius * (Math.Cos(angle) * xDir.Y +
                Math.Sin(angle) * yDir.Y),
            Center.Z + Radius * (Math.Cos(angle) * xDir.Z +
                Math.Sin(angle) * yDir.Z)
        );
    }

    /// <summary>
    /// 构建局部X方向
    /// </summary>
    private Vector3d GetLocalXDirection()
    {
        // 选择一个与法向量不共线的向量
        var reference = Math.Abs(Normal.Dot(Vector3d.ZAxis)) < 0.9
            ? Vector3d.ZAxis : Vector3d.XAxis;
        return reference.Cross(Normal).Normalize();
    }

    /// <summary>
    /// 离散化为三维点序列
    /// </summary>
    public List<Point3d> Tessellate(int segments = 64)
    {
        var points = new List<Point3d>();
        for (int i = 0; i <= segments; i++)
        {
            var angle = 2 * Math.PI * i / segments;
            points.Add(PointAt(angle));
        }
        return points;
    }
}

7.5 工作平面(LcWorkPlane3d)

7.5.1 概念说明

工作平面是LightCAD三维建模中的重要概念。它定义了一个局部坐标系,允许用户在任意方向的平面上进行二维绘图操作,然后将结果映射到三维空间中。

7.5.2 数据结构

public class LcWorkPlane3d : LcEntity, IUpdateObject
{
    /// <summary>
    /// 工作平面原点
    /// </summary>
    public Point3d Origin { get; set; }

    /// <summary>
    /// 平面法向量
    /// </summary>
    public Vector3d Normal { get; set; } = Vector3d.ZAxis;

    /// <summary>
    /// 局部X轴方向
    /// </summary>
    public Vector3d XAxis { get; set; } = Vector3d.XAxis;

    /// <summary>
    /// 局部Y轴方向(自动计算)
    /// </summary>
    public Vector3d YAxis => Normal.Cross(XAxis).Normalize();

    /// <summary>
    /// 显示大小(工作平面网格的范围)
    /// </summary>
    public double DisplaySize { get; set; } = 1000;

    /// <summary>
    /// 网格间距
    /// </summary>
    public double GridSpacing { get; set; } = 10;

    /// <summary>
    /// 是否显示网格
    /// </summary>
    public bool ShowGrid { get; set; } = true;

    public override string TypeName => "WorkPlane3d";

    /// <summary>
    /// 将局部二维坐标转换为世界三维坐标
    /// </summary>
    public Point3d LocalToWorld(Point2d localPoint)
    {
        return new Point3d(
            Origin.X + localPoint.X * XAxis.X + localPoint.Y * YAxis.X,
            Origin.Y + localPoint.X * XAxis.Y + localPoint.Y * YAxis.Y,
            Origin.Z + localPoint.X * XAxis.Z + localPoint.Y * YAxis.Z
        );
    }

    /// <summary>
    /// 将世界三维坐标转换为局部二维坐标
    /// </summary>
    public Point2d WorldToLocal(Point3d worldPoint)
    {
        var v = new Vector3d(
            worldPoint.X - Origin.X,
            worldPoint.Y - Origin.Y,
            worldPoint.Z - Origin.Z
        );

        return new Point2d(
            v.Dot(XAxis),
            v.Dot(YAxis)
        );
    }

    /// <summary>
    /// 将三维点投影到工作平面上
    /// </summary>
    public Point3d ProjectPoint(Point3d point)
    {
        var local = WorldToLocal(point);
        return LocalToWorld(local);
    }

    /// <summary>
    /// 获取变换矩阵(局部到世界)
    /// </summary>
    public Matrix4d GetTransformMatrix()
    {
        return new Matrix4d(
            XAxis.X, YAxis.X, Normal.X, Origin.X,
            XAxis.Y, YAxis.Y, Normal.Y, Origin.Y,
            XAxis.Z, YAxis.Z, Normal.Z, Origin.Z,
            0, 0, 0, 1
        );
    }
}

7.5.3 使用示例

// 创建一个倾斜45度的工作平面
var workPlane = new LcWorkPlane3d
{
    Origin = new Point3d(0, 0, 100),
    Normal = new Vector3d(0, -Math.Sin(Math.PI / 4), Math.Cos(Math.PI / 4)),
    XAxis = Vector3d.XAxis,
    GridSpacing = 10,
    ShowGrid = true
};

// 在工作平面上创建一个矩形
var p1 = workPlane.LocalToWorld(new Point2d(0, 0));
var p2 = workPlane.LocalToWorld(new Point2d(100, 0));
var p3 = workPlane.LocalToWorld(new Point2d(100, 50));
var p4 = workPlane.LocalToWorld(new Point2d(0, 50));

var rect = new LcPolyline();
rect.AddVertex(new Point2d(p1.X, p1.Y));
rect.AddVertex(new Point2d(p2.X, p2.Y));
rect.AddVertex(new Point2d(p3.X, p3.Y));
rect.AddVertex(new Point2d(p4.X, p4.Y));
rect.IsClosed = true;

7.6 三维组(LcGroup3)

7.6.1 数据结构

LcGroup3实现了组合模式(Composite Pattern),将多个三维实体组织为一个逻辑单元:

public class LcGroup3 : LcEntity, IUpdateObject
{
    /// <summary>
    /// 子实体集合
    /// </summary>
    public List<LcEntity> Children { get; set; } = new();

    /// <summary>
    /// 组的变换矩阵
    /// </summary>
    public Matrix4d Transform { get; set; } = Matrix4d.Identity;

    /// <summary>
    /// 组名称
    /// </summary>
    public string GroupName { get; set; }

    public override string TypeName => "Group3";

    /// <summary>
    /// 添加子实体
    /// </summary>
    public void Add(LcEntity entity)
    {
        Children.Add(entity);
    }

    /// <summary>
    /// 移除子实体
    /// </summary>
    public bool Remove(LcEntity entity)
    {
        return Children.Remove(entity);
    }

    /// <summary>
    /// 将组展开为独立实体
    /// </summary>
    public List<LcEntity> Explode()
    {
        var result = new List<LcEntity>();
        foreach (var child in Children)
        {
            var clone = child.Clone();
            clone.TransformBy(Transform);
            result.Add(clone);
        }
        return result;
    }

    public override void TransformBy(Matrix4d matrix)
    {
        Transform = matrix * Transform;
    }

    public override BoundingBox2d Bounds
    {
        get
        {
            if (Children.Count == 0)
                return new BoundingBox2d();

            var bounds = Children[0].Bounds;
            for (int i = 1; i < Children.Count; i++)
            {
                bounds = bounds.Union(Children[i].Bounds);
            }
            return bounds;
        }
    }
}

7.7 三维阵列(LcArray3)

7.7.1 数据结构

LcArray3支持在三维空间中创建实体的参数化阵列复制:

public class LcArray3 : LcEntity, IUpdateObject
{
    /// <summary>
    /// 源实体
    /// </summary>
    public LcEntity SourceEntity { get; set; }

    /// <summary>
    /// 阵列类型
    /// </summary>
    public ArrayType Type { get; set; }

    /// <summary>
    /// X方向数量
    /// </summary>
    public int CountX { get; set; } = 1;

    /// <summary>
    /// Y方向数量
    /// </summary>
    public int CountY { get; set; } = 1;

    /// <summary>
    /// Z方向数量
    /// </summary>
    public int CountZ { get; set; } = 1;

    /// <summary>
    /// X方向间距
    /// </summary>
    public double SpacingX { get; set; }

    /// <summary>
    /// Y方向间距
    /// </summary>
    public double SpacingY { get; set; }

    /// <summary>
    /// Z方向间距
    /// </summary>
    public double SpacingZ { get; set; }

    /// <summary>
    /// 环形阵列中心
    /// </summary>
    public Point3d PolarCenter { get; set; }

    /// <summary>
    /// 环形阵列轴向量
    /// </summary>
    public Vector3d PolarAxis { get; set; } = Vector3d.ZAxis;

    /// <summary>
    /// 环形阵列总角度
    /// </summary>
    public double PolarAngle { get; set; } = 2 * Math.PI;

    /// <summary>
    /// 环形阵列数量
    /// </summary>
    public int PolarCount { get; set; }

    public override string TypeName => "Array3";

    /// <summary>
    /// 生成所有阵列实例
    /// </summary>
    public List<LcEntity> GenerateInstances()
    {
        return Type switch
        {
            ArrayType.Rectangular => GenerateRectangular(),
            ArrayType.Polar => GeneratePolar(),
            _ => new List<LcEntity>()
        };
    }

    private List<LcEntity> GenerateRectangular()
    {
        var instances = new List<LcEntity>();
        for (int ix = 0; ix < CountX; ix++)
        {
            for (int iy = 0; iy < CountY; iy++)
            {
                for (int iz = 0; iz < CountZ; iz++)
                {
                    if (ix == 0 && iy == 0 && iz == 0) continue;

                    var clone = SourceEntity.Clone();
                    var translation = Matrix4d.CreateTranslation(
                        ix * SpacingX,
                        iy * SpacingY,
                        iz * SpacingZ
                    );
                    clone.TransformBy(translation);
                    instances.Add(clone);
                }
            }
        }
        return instances;
    }

    private List<LcEntity> GeneratePolar()
    {
        var instances = new List<LcEntity>();
        var angleStep = PolarAngle / PolarCount;

        for (int i = 1; i < PolarCount; i++)
        {
            var clone = SourceEntity.Clone();
            var angle = angleStep * i;

            // 绕指定轴旋转
            var toOrigin = Matrix4d.CreateTranslation(
                -PolarCenter.X, -PolarCenter.Y, -PolarCenter.Z);
            var rotation = CreateRotationAroundAxis(PolarAxis, angle);
            var backToPos = Matrix4d.CreateTranslation(
                PolarCenter.X, PolarCenter.Y, PolarCenter.Z);

            clone.TransformBy(backToPos * rotation * toOrigin);
            instances.Add(clone);
        }
        return instances;
    }
}

public enum ArrayType
{
    Rectangular,  // 矩形阵列
    Polar         // 环形阵列
}

7.8 三维轮廓(LcProfile3d)

7.8.1 数据结构

LcProfile3d用于定义三维空间中的封闭轮廓,常用作拉伸、旋转等实体建模操作的截面:

public class LcProfile3d : LcEntity, IUpdateObject
{
    /// <summary>
    /// 轮廓所在的工作平面
    /// </summary>
    public LcWorkPlane3d WorkPlane { get; set; }

    /// <summary>
    /// 外轮廓曲线(必须闭合)
    /// </summary>
    public LcPolyline OuterLoop { get; set; }

    /// <summary>
    /// 内部孔洞轮廓(可选)
    /// </summary>
    public List<LcPolyline> InnerLoops { get; set; } = new();

    /// <summary>
    /// 轮廓面积(扣除孔洞)
    /// </summary>
    public double Area
    {
        get
        {
            var outerArea = OuterLoop.Area;
            var innerArea = InnerLoops.Sum(loop => loop.Area);
            return outerArea - innerArea;
        }
    }

    public override string TypeName => "Profile3d";

    /// <summary>
    /// 获取轮廓的三维点序列
    /// </summary>
    public List<Point3d> GetWorldPoints()
    {
        var points2d = OuterLoop.Tessellate();
        return points2d.Select(p => WorkPlane.LocalToWorld(p)).ToList();
    }

    /// <summary>
    /// 验证轮廓是否有效
    /// </summary>
    public bool IsValid()
    {
        if (OuterLoop == null || !OuterLoop.IsClosed) return false;
        if (OuterLoop.VertexCount < 3) return false;

        // 检查内部轮廓是否都在外部轮廓内
        foreach (var inner in InnerLoops)
        {
            if (!inner.IsClosed) return false;
            foreach (var vertex in inner.Vertices)
            {
                var polygon = OuterLoop.Vertices
                    .Select(v => v.Position).ToList();
                if (!GeometryUtils.PointInPolygon(vertex.Position, polygon))
                    return false;
            }
        }

        return true;
    }
}

7.9 容器引用(LcContianerRef3)

7.9.1 概念说明

LcContianerRef3是LightCAD中实现组件实例化和引用的机制。类似于AutoCAD中的块引用(Block Reference),它允许在文档中多次放置同一个组件定义的实例,而不必复制所有数据。

7.9.2 数据结构

public class LcContianerRef3 : LcEntity, IUpdateObject
{
    /// <summary>
    /// 引用的组件定义标识
    /// </summary>
    public LcGuid ComponentGuid { get; set; }

    /// <summary>
    /// 插入点(在世界坐标中的位置)
    /// </summary>
    public Point3d InsertionPoint { get; set; }

    /// <summary>
    /// 旋转角度
    /// </summary>
    public double Rotation { get; set; }

    /// <summary>
    /// 缩放因子
    /// </summary>
    public Vector3d Scale { get; set; } = new Vector3d(1, 1, 1);

    /// <summary>
    /// 完整的变换矩阵
    /// </summary>
    public Matrix4d InsertionMatrix
    {
        get
        {
            var scale = Matrix4d.CreateScale(Scale.X, Scale.Y, Scale.Z);
            var rotation = Matrix4d.CreateRotationZ(Rotation);
            var translation = Matrix4d.CreateTranslation(
                InsertionPoint.X, InsertionPoint.Y, InsertionPoint.Z);
            return translation * rotation * scale;
        }
    }

    /// <summary>
    /// 实例属性覆盖
    /// </summary>
    public Dictionary<string, object> PropertyOverrides { get; set; } = new();

    public override string TypeName => "ContainerRef3";

    /// <summary>
    /// 获取引用的组件定义
    /// </summary>
    public LcGroup3 GetDefinition()
    {
        // 从文档的块定义集合中查找
        return Document?.Blocks?.Find(ComponentGuid);
    }

    /// <summary>
    /// 展开引用为独立实体
    /// </summary>
    public List<LcEntity> Explode()
    {
        var definition = GetDefinition();
        if (definition == null) return new List<LcEntity>();

        var result = new List<LcEntity>();
        foreach (var child in definition.Children)
        {
            var clone = child.Clone();
            clone.TransformBy(InsertionMatrix);
            result.Add(clone);
        }
        return result;
    }
}

7.10 三维图元的坐标变换

7.10.1 世界坐标与局部坐标

在三维建模中,坐标变换是核心操作。LightCAD中的三维实体需要在不同坐标系之间转换:

// 世界坐标系(WCS)
// 全局固定的笛卡尔坐标系

// 用户坐标系(UCS)
// 用户自定义的坐标系,通过工作平面定义

// 对象坐标系(OCS)
// 实体自身的局部坐标系

// WCS → OCS 变换
public Point3d WorldToObject(Point3d worldPoint, Matrix4d objectTransform)
{
    var inverseTransform = objectTransform.Inverse();
    return inverseTransform.Transform(worldPoint);
}

// OCS → WCS 变换
public Point3d ObjectToWorld(Point3d objectPoint, Matrix4d objectTransform)
{
    return objectTransform.Transform(objectPoint);
}

7.10.2 常用三维变换

// 绕任意轴旋转
public static Matrix4d RotateAroundAxis(Point3d point, Vector3d axis, double angle)
{
    var toOrigin = Matrix4d.CreateTranslation(-point.X, -point.Y, -point.Z);
    var backToPos = Matrix4d.CreateTranslation(point.X, point.Y, point.Z);

    var n = axis.Normalize();
    var cos = Math.Cos(angle);
    var sin = Math.Sin(angle);
    var t = 1 - cos;

    var rotation = new Matrix4d(
        t * n.X * n.X + cos,       t * n.X * n.Y - sin * n.Z, t * n.X * n.Z + sin * n.Y, 0,
        t * n.X * n.Y + sin * n.Z, t * n.Y * n.Y + cos,       t * n.Y * n.Z - sin * n.X, 0,
        t * n.X * n.Z - sin * n.Y, t * n.Y * n.Z + sin * n.X, t * n.Z * n.Z + cos,       0,
        0,                          0,                          0,                          1
    );

    return backToPos * rotation * toOrigin;
}

// 三维镜像(关于平面镜像)
public static Matrix4d MirrorAboutPlane(Plane plane)
{
    var n = plane.Normal.Normalize();
    var d = -n.Dot(new Vector3d(
        plane.Origin.X, plane.Origin.Y, plane.Origin.Z));

    return new Matrix4d(
        1 - 2 * n.X * n.X, -2 * n.X * n.Y,     -2 * n.X * n.Z,     -2 * n.X * d,
        -2 * n.X * n.Y,     1 - 2 * n.Y * n.Y,  -2 * n.Y * n.Z,     -2 * n.Y * d,
        -2 * n.X * n.Z,     -2 * n.Y * n.Z,      1 - 2 * n.Z * n.Z,  -2 * n.Z * d,
        0,                   0,                    0,                    1
    );
}

7.11 本章小结

本章详细介绍了LightCAD的三维图元系统。三维图元在二维图元的基础上增加了Z坐标和空间方位信息,能够表示任意空间位置和方向的几何实体。工作平面(LcWorkPlane3d)提供了在任意方向的平面上进行绘图的能力,三维组(LcGroup3)和阵列(LcArray3)实现了实体的组织和批量复制,容器引用(LcContianerRef3)提供了高效的组件实例化机制。这些三维图元为后续的实体建模和视图构建奠定了基础。


上一章第六章:二维图元系统

下一章第八章:实体建模系统