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

推荐订阅源

H
Hackread – Cybersecurity News, Data Breaches, AI and More
Security Archives - TechRepublic
Security Archives - TechRepublic
I
Intezer
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
CXSECURITY Database RSS Feed - CXSecurity.com
A
Arctic Wolf
T
Threatpost
P
Proofpoint News Feed
AWS News Blog
AWS News Blog
C
Cybersecurity and Infrastructure Security Agency CISA
G
GRAHAM CLULEY
Cisco Talos Blog
Cisco Talos Blog
Simon Willison's Weblog
Simon Willison's Weblog
L
Lohrmann on Cybersecurity
Scott Helme
Scott Helme
T
Tenable Blog
L
LINUX DO - 最新话题
Help Net Security
Help Net Security
WordPress大学
WordPress大学
Hacker News: Ask HN
Hacker News: Ask HN
人人都是产品经理
人人都是产品经理
MyScale Blog
MyScale Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Recent Announcements
Recent Announcements
Vercel News
Vercel News
The Hacker News
The Hacker News
J
Java Code Geeks
博客园 - 【当耐特】
D
Docker
V
V2EX
H
Heimdal Security Blog
GbyAI
GbyAI
博客园 - 叶小钗
Google DeepMind News
Google DeepMind News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
N
News | PayPal Newsroom
The Register - Security
The Register - Security
The Cloudflare Blog
C
CERT Recently Published Vulnerability Notes
T
The Blog of Author Tim Ferriss
博客园 - Franky
MongoDB | Blog
MongoDB | Blog
SecWiki News
SecWiki News
S
Secure Thoughts
Attack and Defense Labs
Attack and Defense Labs
Microsoft Security Blog
Microsoft Security Blog
S
Schneier on Security
Latest news
Latest news
Project Zero
Project Zero

博客园 - 我才是银古

第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章:二次开发实战与综合案例 第17章:与 Qt VTK Python pythonOCC 生态集成 第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位整数 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
第8章:ClipperBase 基类详解
我才是银古 · 2026-04-11 · via 博客园 - 我才是银古

第8章:ClipperBase 基类详解

8.1 概述

ClipperBase 是 Clipper2 裁剪引擎的核心基类,定义了多边形裁剪算法的主要数据结构和基础方法。Clipper64ClipperD 都继承自这个类。

8.2 类层次结构

ClipperBase (抽象基类)
├── Clipper64 (整数坐标裁剪)
└── ClipperD (浮点坐标裁剪)

8.3 核心成员变量

8.3.1 变量声明

public abstract class ClipperBase
{
    // 选项标志
    private ClipType _cliptype;
    private FillRule _fillrule;
    
    // 数据列表
    internal List<LocalMinima> _minimaList;
    internal List<List<Vertex>> _vertexList;
    internal List<long> _scanlineList;
    
    // 活动边表
    internal Active? _actives;
    internal Active? _sel;
    
    // 输出结构
    internal List<OutRec> _outrecList;
    internal List<HorzSegment>? _horz_seg_list;
    internal List<HorzJoin>? _horz_join_list;
    
    // 状态标志
    internal bool _isSortedMinimaList;
    internal bool _hasOpenPaths;
    
    // 其他
    internal int _currentLocMinIdx;
    internal long _currentBotY;
    
    // 对象池
    private readonly Pool<OutPt> _outPtPool;
    private readonly Pool<OutRec> _outRecPool;
    
    // ...
}

8.3.2 变量用途说明

变量名 类型 用途
_minimaList List<LocalMinima> 局部极小值列表
_vertexList List<List<Vertex>> 顶点列表的列表
_scanlineList List<long> 扫描线Y坐标列表
_actives Active? 活动边表头指针
_outrecList List<OutRec> 输出记录列表
_hasOpenPaths bool 是否包含开放路径

8.4 局部极小值列表 (MinimaList)

8.4.1 LocalMinima 结构

internal class LocalMinima
{
    public readonly Vertex vertex;
    public readonly PathType pathtype;
    public readonly bool isOpen;
    
    public LocalMinima(Vertex vertex, PathType pathtype, bool isOpen = false)
    {
        this.vertex = vertex;
        this.pathtype = pathtype;
        this.isOpen = isOpen;
    }
}

8.4.2 极小值的几何意义

在多边形轮廓中,局部极小值是Y坐标最低的点(在Y轴向上的坐标系中):

           ▲ Y
           │
    ○──────●──────○     ← 局部最大值
    │             │
    │             │
    ○             ○
     ╲           ╱
      ╲         ╱
       ╲       ╱
        ●─────●         ← 局部极小值
           │

8.4.3 添加到极小值列表

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddLocMin(Vertex vert, PathType pathtype, bool isOpen)
{
    // 如果需要构建扫描线列表
    if (!_scanlineList.Contains(vert.pt.Y))
        _scanlineList.Add(vert.pt.Y);
    
    // 添加到局部极小值列表
    _minimaList.Add(new LocalMinima(vert, pathtype, isOpen));
}

8.4.4 排序极小值

private void SortMinima()
{
    if (!_isSortedMinimaList)
    {
        // 按 Y 坐标降序排序(从上到下扫描)
        _minimaList.Sort((a, b) => b.vertex.pt.Y.CompareTo(a.vertex.pt.Y));
        _isSortedMinimaList = true;
    }
}

8.5 顶点列表 (VertexList)

8.5.1 Vertex 结构

internal class Vertex
{
    public Point64 pt;
    public Vertex? next;
    public Vertex? prev;
    public VertexFlags flags;
}

每个多边形路径都有自己的顶点链表,存储在 _vertexList 中。

8.5.2 顶点标志

[Flags]
internal enum VertexFlags
{
    None = 0,
    OpenStart = 1,   // 开放路径起点
    OpenEnd = 2,     // 开放路径终点
    LocalMax = 4,    // 局部最大值
    LocalMin = 8     // 局部最小值
}

8.5.3 顶点链表构建

private void AddPathsToVertexList(Paths64 paths, PathType pathType, bool isOpen)
{
    foreach (Path64 path in paths)
    {
        Vertex? firstVert = null;
        Vertex? prevVert = null;
        
        foreach (Point64 pt in path)
        {
            // 跳过重复点
            if (prevVert != null && pt == prevVert.pt) continue;
            
            Vertex v = new Vertex
            {
                pt = pt,
                flags = VertexFlags.None
            };
            
            if (firstVert == null)
                firstVert = v;
            else
            {
                prevVert!.next = v;
                v.prev = prevVert;
            }
            prevVert = v;
        }
        
        // 闭合路径
        if (!isOpen && firstVert != null && prevVert != null)
        {
            prevVert.next = firstVert;
            firstVert.prev = prevVert;
        }
        
        // 将顶点列表添加到 _vertexList
        if (firstVert != null)
            _vertexList.Add(CreateVertexList(firstVert));
        
        // 识别并标记局部极值
        MarkLocalMinMax(firstVert, pathType, isOpen);
    }
}

8.6 扫描线列表 (ScanlineList)

8.6.1 扫描线的作用

扫描线算法的核心是维护一个有序的Y坐标列表,算法在每个Y坐标处进行事件处理。

internal List<long> _scanlineList;

8.6.2 扫描线操作

// 添加扫描线
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddScanline(long y)
{
    // 使用二分查找保持有序
    int index = _scanlineList.BinarySearch(y);
    if (index < 0)
        _scanlineList.Insert(~index, y);
}

// 弹出下一个扫描线
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool PopScanline(out long y)
{
    if (_scanlineList.Count == 0)
    {
        y = 0;
        return false;
    }
    
    // 获取最后一个(最大Y值)
    int lastIndex = _scanlineList.Count - 1;
    y = _scanlineList[lastIndex];
    _scanlineList.RemoveAt(lastIndex);
    return true;
}

8.6.3 扫描线事件类型

在每个扫描线位置,可能发生以下事件:

  1. 插入边:局部极小值点,开始向上扫描
  2. 删除边:局部最大值点,边结束
  3. 交点处理:两条边相交
  4. 水平边处理:水平边的特殊处理

8.7 AddPath/AddPaths 方法

8.7.1 AddPath 方法

public void AddPath(Path64 path, PathType pathtype, bool isOpen = false)
{
    AddPaths(new Paths64 { path }, pathtype, isOpen);
}

8.7.2 AddPaths 核心实现

public void AddPaths(Paths64 paths, PathType pathtype, bool isOpen = false)
{
    if (isOpen) _hasOpenPaths = true;
    
    // 标记需要重新排序
    _isSortedMinimaList = false;
    
    // 将路径添加到顶点列表
    AddPathsToVertexList(paths, pathtype, isOpen);
}

8.7.3 便捷方法

public void AddSubject(Path64 path)
{
    AddPath(path, PathType.Subject);
}

public void AddSubject(Paths64 paths)
{
    AddPaths(paths, PathType.Subject);
}

public void AddOpenSubject(Path64 path)
{
    AddPath(path, PathType.Subject, true);
}

public void AddClip(Path64 path)
{
    AddPath(path, PathType.Clip);
}

public void AddClip(Paths64 paths)
{
    AddPaths(paths, PathType.Clip);
}

8.8 清理和重置

8.8.1 Clear 方法

public void Clear()
{
    // 清空所有数据
    ClearSolution();
    _minimaList.Clear();
    _vertexList.Clear();
    _scanlineList.Clear();
    _currentLocMinIdx = 0;
    _isSortedMinimaList = false;
    _hasOpenPaths = false;
}

8.8.2 ClearSolution 方法

internal void ClearSolution()
{
    // 清空输出结构
    _actives = null;
    _sel = null;
    
    // 回收对象到池中
    foreach (var outrec in _outrecList)
    {
        if (outrec.pts != null)
            DisposeOutPts(outrec.pts);
        _outRecPool.Return(outrec);
    }
    
    _outrecList.Clear();
    _horz_seg_list?.Clear();
    _horz_join_list?.Clear();
}

8.9 对象池

8.9.1 Pool 类

Clipper2 使用对象池来减少GC压力:

internal class Pool<T> where T : class, new()
{
    private readonly Stack<T> _pool = new Stack<T>();
    
    public T Get()
    {
        return _pool.Count > 0 ? _pool.Pop() : new T();
    }
    
    public void Return(T item)
    {
        _pool.Push(item);
    }
}

8.9.2 使用对象池

// 获取对象
OutPt op = _outPtPool.Get();
op.pt = pt;
op.next = null;
op.prev = null;

// 归还对象
_outPtPool.Return(op);

8.9.3 对象池的好处

  1. 减少内存分配:重用已创建的对象
  2. 减少GC压力:避免频繁创建和销毁对象
  3. 提高性能:特别是在处理大量多边形时

8.10 PreserveCollinear 属性

8.10.1 定义

public bool PreserveCollinear { get; set; }

8.10.2 作用

控制是否保留共线点:

// 三个共线点
// A ─── B ─── C

// PreserveCollinear = false(默认)
// 结果:A ─── C(移除B)

// PreserveCollinear = true
// 结果:A ─── B ─── C(保留B)

8.10.3 实现

private void AddVertex(Vertex v, Vertex prev)
{
    if (!PreserveCollinear)
    {
        // 检查是否共线
        if (prev.prev != null && 
            IsCollinear(prev.prev.pt, prev.pt, v.pt))
        {
            // 移除 prev 顶点
            prev.prev.next = v;
            v.prev = prev.prev;
            return;
        }
    }
    
    // 正常添加
    prev.next = v;
    v.prev = prev;
}

8.11 ReverseSolution 属性

8.11.1 定义

public bool ReverseSolution { get; set; }

8.11.2 作用

控制输出多边形的方向:

// ReverseSolution = false(默认)
// 外轮廓:逆时针
// 内轮廓(孔洞):顺时针

// ReverseSolution = true
// 外轮廓:顺时针
// 内轮廓(孔洞):逆时针

8.12 状态检查方法

8.12.1 IsEmpty

internal bool IsEmpty()
{
    return _minimaList.Count == 0;
}

8.12.2 HasOpenPaths

internal bool HasOpenPaths()
{
    return _hasOpenPaths;
}

8.13 内部辅助方法

8.13.1 GetPolyType

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static PathType GetPolyType(Active ae)
{
    return ae.localMin.pathtype;
}

8.13.2 IsOpen

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsOpen(Active ae)
{
    return ae.localMin.isOpen;
}

8.13.3 IsHotEdge

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsHotEdge(Active ae)
{
    return ae.outrec != null;
}

"热边"是指当前正在构建输出多边形的边。

8.14 调试支持

8.14.1 ToString 方法

#if DEBUG
public override string ToString()
{
    return $"ClipperBase: {_minimaList.Count} minima, " +
           $"{_vertexList.Count} vertex lists, " +
           $"{_outrecList.Count} output records";
}
#endif

8.14.2 调试可视化

在调试模式下,可以添加方法来可视化内部状态:

#if DEBUG
internal void DumpActives()
{
    Active? ae = _actives;
    int count = 0;
    while (ae != null)
    {
        Console.WriteLine($"Active {count++}: bot=({ae.bot.X},{ae.bot.Y}), " +
                          $"top=({ae.top.X},{ae.top.Y})");
        ae = ae.nextInAEL;
    }
}
#endif

8.15 本章小结

ClipperBase 是 Clipper2 的核心基类,包含:

  1. 核心数据结构

    • _minimaList:局部极小值列表
    • _vertexList:顶点列表
    • _scanlineList:扫描线Y坐标列表
    • _actives:活动边表
    • _outrecList:输出记录列表
  2. 关键方法

    • AddPath/AddPaths:添加路径
    • Clear/ClearSolution:清理状态
    • 各种辅助方法
  3. 性能优化

    • 对象池减少GC
    • 内联优化
    • 懒排序

理解 ClipperBase 的结构是理解 Clipper2 裁剪算法的基础。


上一章:高精度运算 | 返回目录 | 下一章:Active活动边结构