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

推荐订阅源

S
Schneier on Security
J
Java Code Geeks
小众软件
小众软件
WordPress大学
WordPress大学
T
Tailwind CSS Blog
雷峰网
雷峰网
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
腾讯CDC
The Cloudflare Blog
博客园 - Franky
罗磊的独立博客
V
Visual Studio Blog
宝玉的分享
宝玉的分享
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 【当耐特】
博客园_首页
V
V2EX
博客园 - 三生石上(FineUI控件)
Jina AI
Jina AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
SegmentFault 最新的问题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Security Affairs
C
Cybersecurity and Infrastructure Security Agency CISA
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
月光博客
月光博客
P
Palo Alto Networks Blog
L
Lohrmann on Cybersecurity
爱范儿
爱范儿
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
人人都是产品经理
人人都是产品经理
C
CERT Recently Published Vulnerability Notes
Hugging Face - Blog
Hugging Face - Blog
Attack and Defense Labs
Attack and Defense Labs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Troy Hunt's Blog
博客园 - 司徒正美
博客园 - 叶小钗
Last Week in AI
Last Week in AI
Google DeepMind News
Google DeepMind News
Simon Willison's Weblog
Simon Willison's Weblog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Privacy International News Feed
IT之家
IT之家
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Help Net Security
Help Net Security
NISL@THU
NISL@THU
H
Hacker News: Front Page

博客园 - 我才是银古

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

第十一章:视图构建系统

11.1 视图构建概述

11.1.1 ViewBuilder的定位

视图构建系统是LightCAD.Runtime模块中最核心也最复杂的子系统。它负责将三维模型转换为各种二维投影视图,是CAD工程制图的关键技术。仅ViewBuilder.cs一个文件就达到71KB,ProjectioinProcessor.cs达到39KB,可见其复杂程度。

11.1.2 视图类型

LightCAD支持以下视图类型:

视图类型 目录/类 说明
全局俯视图 GlobalTopView/ 从正上方向下的投影视图
立面视图 ElevationView/ 从正面或侧面的投影视图
平面详图 PlaneDetailView/ 指定平面的详细视图
剖面视图 SectionView/ 通过切割平面的截面视图
投影视图 ProjectionApp/ 通用的正交投影框架

11.1.3 模块结构

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/              # 投影应用框架

11.2 投影处理器(ProjectioinProcessor)

11.2.1 投影原理

正交投影将三维空间中的对象投影到二维平面上。投影方向由视图的法线方向决定:

public class ProjectioinProcessor
{
    /// <summary>
    /// 投影方向
    /// </summary>
    public Vector3d ProjectionDirection { get; set; }

    /// <summary>
    /// 投影平面的法线
    /// </summary>
    public Vector3d PlaneNormal => ProjectionDirection.Normalize();

    /// <summary>
    /// 投影平面上的U方向(水平)
    /// </summary>
    public Vector3d UDirection { get; set; }

    /// <summary>
    /// 投影平面上的V方向(垂直)
    /// </summary>
    public Vector3d VDirection { get; set; }

    /// <summary>
    /// 将三维点投影到二维平面
    /// </summary>
    public Point2d Project(Point3d point)
    {
        var u = point.X * UDirection.X +
                point.Y * UDirection.Y +
                point.Z * UDirection.Z;
        var v = point.X * VDirection.X +
                point.Y * VDirection.Y +
                point.Z * VDirection.Z;
        return new Point2d(u, v);
    }

    /// <summary>
    /// 将三维线段投影为二维线段
    /// </summary>
    public LcLine ProjectLine(LcLine3d line3d)
    {
        var start2d = Project(line3d.StartPoint);
        var end2d = Project(line3d.EndPoint);
        return new LcLine
        {
            StartPoint = start2d,
            EndPoint = end2d
        };
    }

    /// <summary>
    /// 将三维圆投影为二维圆或椭圆
    /// </summary>
    public LcEntity ProjectCircle(LcCircle3d circle3d)
    {
        var normalDot = Math.Abs(
            circle3d.Normal.Dot(ProjectionDirection.Normalize()));

        if (normalDot > 0.999)
        {
            // 圆的法线与投影方向平行 → 投影为圆
            return new LcCircle
            {
                Center = Project(circle3d.Center),
                Radius = circle3d.Radius
            };
        }
        else if (normalDot < 0.001)
        {
            // 圆的法线与投影方向垂直 → 投影为线段
            var center2d = Project(circle3d.Center);
            var dir = circle3d.Normal.Cross(ProjectionDirection).Normalize();
            var dir2d = new Vector2d(
                dir.Dot(UDirection), dir.Dot(VDirection)).Normalize();

            return new LcLine
            {
                StartPoint = center2d - dir2d * circle3d.Radius,
                EndPoint = center2d + dir2d * circle3d.Radius
            };
        }
        else
        {
            // 一般情况 → 投影为椭圆
            return ProjectCircleAsEllipse(circle3d);
        }
    }

    /// <summary>
    /// 设置标准投影方向
    /// </summary>
    public void SetupProjection(StandardView view)
    {
        switch (view)
        {
            case StandardView.Top:
                ProjectionDirection = -Vector3d.ZAxis;
                UDirection = Vector3d.XAxis;
                VDirection = Vector3d.YAxis;
                break;
            case StandardView.Front:
                ProjectionDirection = Vector3d.YAxis;
                UDirection = Vector3d.XAxis;
                VDirection = Vector3d.ZAxis;
                break;
            case StandardView.Right:
                ProjectionDirection = -Vector3d.XAxis;
                UDirection = Vector3d.YAxis;
                VDirection = Vector3d.ZAxis;
                break;
        }
    }
}

11.3 ViewBuilder主构建器

11.3.1 视图构建流程

public class ViewBuilder
{
    private ProjectioinProcessor projector;
    private GpuVisibleFilter visibilityFilter;
    private PrjResultCollector resultCollector;

    /// <summary>
    /// 构建视图的主入口
    /// </summary>
    public ViewResult BuildView(ViewBuildParams buildParams)
    {
        // 第1步:设置投影参数
        projector = new ProjectioinProcessor();
        projector.SetupProjection(buildParams.ViewDirection);

        // 第2步:收集需要投影的三维实体
        var entities3d = CollectEntities(buildParams.Document);

        // 第3步:可见性过滤
        var visibleEntities = visibilityFilter.Filter(
            entities3d, buildParams.ViewDirection);

        // 第4步:投影每个实体
        var projectedEntities = new List<ProjectedEntity>();
        foreach (var entity in visibleEntities)
        {
            var projected = ProjectEntity(entity);
            projectedEntities.Add(projected);
        }

        // 第5步:隐藏线处理
        if (buildParams.HiddenLineRemoval)
        {
            ProcessHiddenLines(projectedEntities);
        }

        // 第6步:收集结果
        return resultCollector.Collect(projectedEntities);
    }

    /// <summary>
    /// 投影单个实体
    /// </summary>
    private ProjectedEntity ProjectEntity(LcEntity entity)
    {
        var result = new ProjectedEntity
        {
            SourceEntity = entity,
            VisibleLines = new List<LcLine>(),
            HiddenLines = new List<LcLine>(),
            SilhouetteLines = new List<LcLine>()
        };

        switch (entity)
        {
            case LcSolid3d solid:
                ProjectSolid(solid, result);
                break;
            case LcLine3d line:
                result.VisibleLines.Add(projector.ProjectLine(line));
                break;
            case LcCircle3d circle:
                var projected = projector.ProjectCircle(circle);
                // 转为线段集合
                break;
        }

        return result;
    }

    /// <summary>
    /// 投影实体(最复杂的情况)
    /// </summary>
    private void ProjectSolid(LcSolid3d solid, ProjectedEntity result)
    {
        var faces = solid.GetFaces();
        var edges = solid.GetEdges();

        // 对每条边进行投影
        foreach (var edge in edges)
        {
            var projectedEdge = projector.ProjectLine(
                new LcLine3d
                {
                    StartPoint = edge.StartPoint,
                    EndPoint = edge.EndPoint
                });

            // 判断边的类型(轮廓线/可见线/隐藏线)
            var edgeType = ClassifyEdge(edge, faces, projector);

            switch (edgeType)
            {
                case EdgeClassification.Visible:
                    result.VisibleLines.Add(projectedEdge);
                    break;
                case EdgeClassification.Hidden:
                    result.HiddenLines.Add(projectedEdge);
                    break;
                case EdgeClassification.Silhouette:
                    result.SilhouetteLines.Add(projectedEdge);
                    break;
            }
        }

        // 生成轮廓线(曲面体的边界投影)
        GenerateSilhouetteLines(solid, result);
    }
}

public class ViewBuildParams
{
    public LcDocument Document { get; set; }
    public StandardView ViewDirection { get; set; }
    public bool HiddenLineRemoval { get; set; } = true;
    public double Scale { get; set; } = 1.0;
    public Point2d ViewCenter { get; set; }
}

public class ViewResult
{
    public List<LcLine> VisibleLines { get; set; }
    public List<LcLine> HiddenLines { get; set; }
    public List<LcLine> SilhouetteLines { get; set; }
    public BoundingBox2d Bounds { get; set; }
}

11.4 隐藏线消除

11.4.1 算法概述

隐藏线消除(Hidden Line Removal, HLR)是工程制图中的核心算法,用于确定从特定视角观察时哪些线是可见的,哪些线被其他实体遮挡:

public class HiddenLineProcessor
{
    /// <summary>
    /// 处理隐藏线
    /// </summary>
    public void Process(List<ProjectedEntity> entities,
                        Vector3d viewDirection)
    {
        // 按深度排序实体(从近到远)
        var sortedEntities = entities
            .OrderBy(e => GetDepth(e, viewDirection))
            .ToList();

        // 对每条线段,检查是否被其他实体遮挡
        for (int i = 0; i < sortedEntities.Count; i++)
        {
            var currentEntity = sortedEntities[i];

            foreach (var line in currentEntity.VisibleLines.ToList())
            {
                // 检查这条线是否被前面的实体遮挡
                for (int j = 0; j < i; j++)
                {
                    var occluder = sortedEntities[j];
                    var clippedSegments = ClipLineAgainstEntity(
                        line, occluder, viewDirection);

                    if (clippedSegments.hidden.Count > 0)
                    {
                        // 将被遮挡的部分移到隐藏线列表
                        currentEntity.VisibleLines.Remove(line);
                        currentEntity.VisibleLines
                            .AddRange(clippedSegments.visible);
                        currentEntity.HiddenLines
                            .AddRange(clippedSegments.hidden);
                    }
                }
            }
        }
    }
}

11.5 B-Rep视图构建器

11.5.1 B-Rep概念

B-Rep(Boundary Representation,边界表示)是实体建模中的一种表示方法,通过面、边和顶点来描述实体的边界:

public class BrepViewBuilder
{
    /// <summary>
    /// 从B-Rep实体生成投影视图
    /// </summary>
    public ViewResult BuildFromBrep(LcSolid3d solid,
                                     ProjectioinProcessor projector)
    {
        var result = new ViewResult
        {
            VisibleLines = new List<LcLine>(),
            HiddenLines = new List<LcLine>(),
            SilhouetteLines = new List<LcLine>()
        };

        var faces = solid.GetFaces();
        var edges = solid.GetEdges();

        // 分类每个面是否面向观察者
        var facingMap = new Dictionary<SolidFace, bool>();
        foreach (var face in faces)
        {
            var dot = face.Normal.Dot(projector.PlaneNormal);
            facingMap[face] = dot < 0; // 面向观察者
        }

        // 分类每条边
        foreach (var edge in edges)
        {
            var adjacentFaces = GetAdjacentFaces(edge, faces);
            var edgeType = ClassifyEdge(adjacentFaces, facingMap);

            var projectedLine = projector.ProjectLine(
                new LcLine3d
                {
                    StartPoint = edge.StartPoint,
                    EndPoint = edge.EndPoint
                });

            switch (edgeType)
            {
                case EdgeType.Contour:
                    // 轮廓线:一个相邻面朝前,一个朝后
                    result.VisibleLines.Add(projectedLine);
                    break;
                case EdgeType.Crease:
                    // 折线:两个相邻面都朝前
                    result.VisibleLines.Add(projectedLine);
                    break;
                case EdgeType.Hidden:
                    // 隐藏线:两个相邻面都朝后
                    result.HiddenLines.Add(projectedLine);
                    break;
            }
        }

        return result;
    }
}

11.6 具体视图类型

11.6.1 全局俯视图(GlobalTopView)

public class GlobalTopViewBuilder
{
    /// <summary>
    /// 创建俯视图
    /// </summary>
    public ViewResult Build(LcDocument document, TopViewParams viewParams)
    {
        var builder = new ViewBuilder();
        var result = builder.BuildView(new ViewBuildParams
        {
            Document = document,
            ViewDirection = StandardView.Top,
            HiddenLineRemoval = viewParams.ShowHiddenLines,
            Scale = viewParams.Scale
        });

        // 添加标注
        if (viewParams.IncludeDimensions)
        {
            AddAutoDimensions(result, document);
        }

        // 添加标高标注
        if (viewParams.ShowElevations)
        {
            AddElevationMarks(result, document);
        }

        return result;
    }
}

11.6.2 立面视图(ElevationView)

public class ElevationViewBuilder
{
    /// <summary>
    /// 创建立面视图
    /// </summary>
    public ViewResult Build(LcDocument document,
                            ElevationViewParams viewParams)
    {
        // 确定立面方向
        var direction = viewParams.Direction switch
        {
            ElevationDirection.South => StandardView.Front,
            ElevationDirection.North => StandardView.Back,
            ElevationDirection.East => StandardView.Right,
            ElevationDirection.West => StandardView.Left,
            _ => StandardView.Front
        };

        var builder = new ViewBuilder();
        var result = builder.BuildView(new ViewBuildParams
        {
            Document = document,
            ViewDirection = direction,
            HiddenLineRemoval = true,
            Scale = viewParams.Scale
        });

        // 添加地面线
        AddGroundLine(result);

        // 添加标高标注
        AddElevationAnnotations(result, viewParams);

        return result;
    }
}

public enum ElevationDirection
{
    South,  // 南立面
    North,  // 北立面
    East,   // 东立面
    West    // 西立面
}

11.6.3 剖面视图(SectionView)

public class SectionViewBuilder
{
    /// <summary>
    /// 创建剖面视图
    /// </summary>
    public SectionViewResult Build(LcDocument document,
                                    SectionViewParams viewParams)
    {
        var result = new SectionViewResult();

        // 定义剖切平面
        var sectionPlane = new Plane(
            viewParams.PlanePoint,
            viewParams.PlaneNormal
        );

        // 对每个实体进行剖切
        foreach (var entity in document.Entities)
        {
            if (entity is LcSolid3d solid)
            {
                var sectionResult = CutSolid(solid, sectionPlane);

                // 剖切线(填充区域的轮廓)
                result.SectionLines.AddRange(sectionResult.Outlines);

                // 剖面填充
                if (viewParams.ShowHatching)
                {
                    result.HatchRegions.AddRange(
                        GenerateHatching(sectionResult.Outlines,
                            viewParams.HatchPattern));
                }
            }
        }

        // 投影剖切面后方的可见内容
        if (viewParams.ShowBeyondSection)
        {
            var behindResult = ProjectBehindSection(
                document, sectionPlane, viewParams.ViewDirection);
            result.ProjectedLines.AddRange(behindResult);
        }

        return result;
    }

    /// <summary>
    /// 对实体进行剖切
    /// </summary>
    private SectionCutResult CutSolid(LcSolid3d solid, Plane sectionPlane)
    {
        var result = new SectionCutResult();
        var meshData = solid.GenerateMesh();

        // 遍历所有三角形,找出与剖切平面相交的
        for (int i = 0; i < meshData.Indices.Count; i += 3)
        {
            var v0 = meshData.Vertices[meshData.Indices[i]];
            var v1 = meshData.Vertices[meshData.Indices[i + 1]];
            var v2 = meshData.Vertices[meshData.Indices[i + 2]];

            var intersection = TrianglePlaneIntersection(
                v0, v1, v2, sectionPlane);

            if (intersection != null)
            {
                result.IntersectionSegments.Add(intersection);
            }
        }

        // 将交线段连接成闭合轮廓
        result.Outlines = ConnectSegments(result.IntersectionSegments);

        return result;
    }
}

11.7 投影结果收集器

11.7.1 PrjResultCollector

public class PrjResultCollector
{
    /// <summary>
    /// 收集并整理投影结果
    /// </summary>
    public ViewResult Collect(List<ProjectedEntity> projectedEntities)
    {
        var result = new ViewResult
        {
            VisibleLines = new List<LcLine>(),
            HiddenLines = new List<LcLine>(),
            SilhouetteLines = new List<LcLine>()
        };

        foreach (var entity in projectedEntities)
        {
            // 合并重叠线段
            var mergedVisible = MergeOverlappingLines(entity.VisibleLines);
            var mergedHidden = MergeOverlappingLines(entity.HiddenLines);

            result.VisibleLines.AddRange(mergedVisible);
            result.HiddenLines.AddRange(mergedHidden);
            result.SilhouetteLines.AddRange(entity.SilhouetteLines);
        }

        // 去除重复线段
        result.VisibleLines = RemoveDuplicates(result.VisibleLines);
        result.HiddenLines = RemoveDuplicates(result.HiddenLines);

        // 计算包围盒
        var allLines = result.VisibleLines
            .Concat(result.HiddenLines)
            .Concat(result.SilhouetteLines);

        result.Bounds = CalculateBounds(allLines);

        return result;
    }
}

11.8 PolygonExt多边形扩展

11.8.1 多边形操作

PolygonExt.cs(22KB)提供了大量的多边形操作方法,用于支持视图构建中的几何处理:

public static class PolygonExt
{
    /// <summary>
    /// 多边形裁剪(Sutherland-Hodgman算法)
    /// </summary>
    public static List<Point2d> ClipPolygon(
        List<Point2d> polygon, BoundingBox2d clipBox)
    {
        var output = new List<Point2d>(polygon);

        // 依次用四条边裁剪
        output = ClipByEdge(output, clipBox.Min.X, true, true);   // 左
        output = ClipByEdge(output, clipBox.Max.X, true, false);  // 右
        output = ClipByEdge(output, clipBox.Min.Y, false, true);  // 下
        output = ClipByEdge(output, clipBox.Max.Y, false, false); // 上

        return output;
    }

    /// <summary>
    /// 多边形偏移
    /// </summary>
    public static List<Point2d> OffsetPolygon(
        List<Point2d> polygon, double distance)
    {
        var result = new List<Point2d>();
        int n = polygon.Count;

        for (int i = 0; i < n; i++)
        {
            var prev = polygon[(i - 1 + n) % n];
            var curr = polygon[i];
            var next = polygon[(i + 1) % n];

            // 计算两条边的法线方向
            var dir1 = (curr - prev).Normalize();
            var dir2 = (next - curr).Normalize();
            var normal1 = dir1.Perpendicular();
            var normal2 = dir2.Perpendicular();

            // 偏移后的边
            var offsetLine1Start = prev + normal1 * distance;
            var offsetLine1Dir = dir1;
            var offsetLine2Start = curr + normal2 * distance;
            var offsetLine2Dir = dir2;

            // 求交点
            var intersection = LineLineIntersect.Compute(
                offsetLine1Start, offsetLine1Dir,
                offsetLine2Start, offsetLine2Dir
            );

            result.Add(intersection ?? (curr + normal1 * distance));
        }

        return result;
    }

    /// <summary>
    /// 多边形布尔运算(并集)
    /// </summary>
    public static List<List<Point2d>> Union(
        List<Point2d> polygonA, List<Point2d> polygonB)
    {
        // 使用Weiler-Atherton算法
        // ...
        return new List<List<Point2d>>();
    }

    /// <summary>
    /// 多边形三角化
    /// </summary>
    public static List<(int, int, int)> Triangulate(List<Point2d> polygon)
    {
        // 使用耳朵裁剪法(Ear Clipping)
        var triangles = new List<(int, int, int)>();
        var indices = Enumerable.Range(0, polygon.Count).ToList();

        while (indices.Count > 2)
        {
            bool earFound = false;
            for (int i = 0; i < indices.Count; i++)
            {
                var prev = indices[(i - 1 + indices.Count) % indices.Count];
                var curr = indices[i];
                var next = indices[(i + 1) % indices.Count];

                if (IsEar(polygon, indices, prev, curr, next))
                {
                    triangles.Add((prev, curr, next));
                    indices.RemoveAt(i);
                    earFound = true;
                    break;
                }
            }
            if (!earFound) break;
        }

        return triangles;
    }
}

11.9 本章小结

本章详细介绍了LightCAD的视图构建系统。该系统负责将三维模型转换为各种二维投影视图,支持俯视图、立面图、剖面图等多种视图类型。核心的投影处理器实现了三维到二维的坐标变换,隐藏线消除算法确定了从特定视角观察时的线段可见性,B-Rep视图构建器针对实体模型进行了优化处理。视图构建系统是LightCAD中技术难度最高的子系统之一,是工程制图功能的技术基石。


上一章第十章:二维绘图与视口管理

下一章第十二章:输入与交互系统