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

推荐订阅源

有赞技术团队
有赞技术团队
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
P
Palo Alto Networks Blog
C
Cisco Blogs
The Hacker News
The Hacker News
T
Threatpost
S
Schneier on Security
K
Kaspersky official blog
Spread Privacy
Spread Privacy
博客园_首页
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
NISL@THU
NISL@THU
量子位
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Google DeepMind News
Google DeepMind News
Security Latest
Security Latest
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
博客园 - 叶小钗
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
News and Events Feed by Topic
爱范儿
爱范儿
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
Project Zero
Project Zero
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Cisco Talos Blog
Cisco Talos Blog
GbyAI
GbyAI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Apple Machine Learning Research
Apple Machine Learning Research
T
Tenable Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
Vulnerabilities – Threatpost
Forbes - Security
Forbes - Security
博客园 - 三生石上(FineUI控件)
C
Cyber Attacks, Cyber Crime and Cyber Security
N
News and Events Feed by Topic
V
V2EX
Webroot Blog
Webroot Blog
The Register - Security
The Register - Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
阮一峰的网络日志
阮一峰的网络日志
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Blog — PlanetScale
Blog — PlanetScale
M
MIT News - Artificial intelligence
Scott Helme
Scott Helme
Simon Willison's Weblog
Simon Willison's Weblog
L
LangChain Blog
W
WeLiveSecurity
Cloudbric
Cloudbric

博客园 - 我才是银古

第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
第1章:Clipper1 概述与入门
我才是银古 · 2026-06-24 · via 博客园 - 我才是银古

第1章:Clipper1 概述与入门

1.1 引言

在计算机图形学、地理信息系统(GIS)、计算机辅助设计(CAD)以及游戏开发等领域,多边形的几何运算是一个基础且至关重要的功能。无论是地图数据的处理、建筑设计中的布尔运算、游戏中的碰撞检测,还是工业制造中的刀具路径规划,都需要对多边形进行精确的裁剪、合并、求交、偏移等操作。

Clipper1(也称为 ClipperLib)是由澳大利亚程序员 Angus Johnson 开发的一个成熟、稳定的开源多边形裁剪库。本教程将深入解读其 C# 源代码实现,帮助读者理解核心算法原理和代码设计。

1.2 源代码文件结构

Clipper1 的 C# 实现位于单个源文件 clipper.cs 中,整个库约 4800 行代码。文件结构如下:

C#/clipper_library/
├── clipper.cs           # 核心源代码文件
├── clipper_library.csproj  # 项目文件
└── Properties/
    └── AssemblyInfo.cs  # 程序集信息

1.3 源代码头部声明

源代码以版权声明和算法来源说明开始:

/*******************************************************************************
*                                                                              *
* Author    :  Angus Johnson                                                   *
* Version   :  6.4.2                                                           *
* Date      :  27 February 2017                                                *
* Website   :  http://www.angusj.com                                           *
* Copyright :  Angus Johnson 2010-2017                                         *
*                                                                              *
* License:                                                                     *
* Use, modification & distribution is subject to Boost Software License Ver 1.*
* http://www.boost.org/LICENSE_1_0.txt                                         *
*                                                                              *
* Attributions:                                                                *
* The code in this library is an extension of Bala Vatti's clipping algorithm:*
* "A generic solution to polygon clipping"                                     *
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63.             *
* http://portal.acm.org/citation.cfm?id=129906                                 *
*                                                                              *
*******************************************************************************/

从声明中可以看出:

  1. 版本信息:当前版本为 6.4.2,发布于 2017 年 2 月 27 日
  2. 开源许可:采用 Boost Software License,允许商业和非商业使用
  3. 算法基础:基于 Bala Vatti 的多边形裁剪算法(1992 年发表于 ACM Communications)

1.4 预处理器指令

源代码开头定义了几个重要的预处理器指令:

//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
//improve performance but coordinate values are limited to the range +/- 46340
//#define use_int32

//use_xyz: adds a Z member to IntPoint. Adds a minor cost to performance.
//#define use_xyz

//use_lines: Enables open path clipping. Adds a very minor cost to performance.
#define use_lines

1.4.1 use_int32

#if use_int32
  using cInt = Int32;
#else
  using cInt = Int64;
#endif
  • 默认关闭:使用 64 位整数(Int64
  • 启用后:使用 32 位整数(Int32),提高性能但限制坐标范围为 ±46340
  • 实现方式:通过 using 别名定义 cInt 类型

坐标范围计算:32 位有符号整数最大值约为 2.1×10⁹,但由于中间计算可能产生溢出,实际安全范围被限制在 √(2³¹-1) ≈ 46340。

1.4.2 use_xyz

#if use_xyz
    public cInt Z;
    
    public IntPoint(cInt x, cInt y, cInt z = 0)
    {
      this.X = x; this.Y = y; this.Z = z;
    }
#endif
  • 默认关闭:IntPoint 只包含 X、Y 坐标
  • 启用后:IntPoint 增加 Z 坐标成员
  • 用途:支持 3D 坐标或存储额外属性信息

1.4.3 use_lines

#define use_lines
  • 默认开启:启用开放路径(线段)的裁剪支持
  • 关闭后:只支持闭合多边形的裁剪
  • 性能影响:轻微

1.5 命名空间与类型别名

using System;
using System.Collections.Generic;

namespace ClipperLib
{
  using Path = List<IntPoint>;
  using Paths = List<List<IntPoint>>;
  
  // ... 类定义 ...
}

1.5.1 命名空间

所有 Clipper 相关的类型都定义在 ClipperLib 命名空间中,避免与其他库的命名冲突。

1.5.2 类型别名

源代码使用 using 指令创建了两个重要的类型别名:

  • PathList<IntPoint>,表示一条路径(多边形轮廓或开放线段)
  • PathsList<List<IntPoint>>,表示多条路径的集合

这种设计使得代码更加简洁易读:

// 使用别名前
List<IntPoint> polygon = new List<IntPoint>();
List<List<IntPoint>> polygons = new List<List<IntPoint>>();

// 使用别名后
Path polygon = new Path();
Paths polygons = new Paths();

1.6 核心类概览

Clipper1 的 C# 实现包含以下核心类和结构:

1.6.1 公共类型(用户直接使用)

类型 说明
IntPoint 整数坐标点结构
DoublePoint 双精度浮点坐标点结构
IntRect 整数矩形边界结构
PolyNode 多边形树节点类
PolyTree 多边形树类,继承自 PolyNode
Clipper 多边形裁剪操作类
ClipperOffset 多边形偏移操作类

1.6.2 枚举类型

枚举 说明
ClipType 裁剪操作类型(交、并、差、异或)
PolyType 多边形类型(主体、裁剪)
PolyFillType 填充规则(奇偶、非零、正向、负向)
JoinType 连接类型(方形、圆形、斜接)
EndType 端点类型(闭合多边形、闭合线、开放端点等)

1.6.3 内部类型

类型 说明
Int128 128 位整数结构,用于高精度中间计算
TEdge 边缘数据结构
LocalMinima 局部极小值结构
Scanbeam 扫描线结构
OutRec 输出多边形记录
OutPt 输出点结构
Join 连接点结构
ClipperBase Clipper 的基类

1.7 Vatti 算法简介

Clipper1 基于 Bala Vatti 在 1992 年发表的多边形裁剪算法。该算法的核心思想是:

1.7.1 扫描线方法

算法使用自下而上的扫描线方法处理多边形:

  1. 预处理阶段:将多边形的边按 Y 坐标排序,识别所有局部极小值点
  2. 扫描阶段:维护一个活动边表(Active Edge List, AEL),记录当前扫描线穿过的边
  3. 事件处理:在每个关键 Y 坐标处理边的插入、删除和交点

1.7.2 关键概念

局部极小值(Local Minima)

  • 多边形轮廓中 Y 坐标最低的点
  • 算法从这些点开始向上扫描

活动边表(AEL)

  • 存储当前扫描线穿过的所有边
  • 按 X 坐标排序

扫描线(Scanbeam)

  • Y 坐标值,表示当前处理的水平线位置
  • 在事件点(边的起点、终点、交点)处更新

1.7.3 算法流程示意

扫描方向 ↑
          
Y=5  ━━━━━━━━━━━ 扫描线 5
     ╱╲        ╱╲
Y=4 ╱  ╲━━━━━━╱  ╲ 扫描线 4
   ╱    ╲    ╱    ╲
Y=3╱      ╲╱       ╲ 扫描线 3 (交点处理)
   ╲      ╱╲      ╱
Y=2 ╲    ╱  ╲    ╱ 扫描线 2
     ╲  ╱    ╲  ╱
Y=1   ╲╱      ╲╱   扫描线 1 (局部极小值)

1.8 整数坐标的设计哲学

Clipper1 使用整数坐标而非浮点数,这是一个关键的设计决策:

1.8.1 浮点数的问题

// 浮点数精度问题示例
double a = 0.1 + 0.2;  // 结果可能是 0.30000000000000004
bool equal = (0.1 + 0.2 == 0.3);  // 可能为 false

1.8.2 整数坐标的优势

  1. 精确比较:整数比较是精确的,不存在舍入误差
  2. 确定性结果:相同输入在任何平台上产生相同输出
  3. 数值稳定性:避免了浮点数累积误差

1.8.3 坐标转换策略

使用 Clipper 时,通常需要将浮点坐标转换为整数:

// 坐标转换示例
const double scale = 1000.0;  // 比例因子

// 输入转换:浮点 -> 整数
IntPoint ToIntPoint(double x, double y)
{
    return new IntPoint((cInt)(x * scale), (cInt)(y * scale));
}

// 输出转换:整数 -> 浮点
DoublePoint ToDoublePoint(IntPoint pt)
{
    return new DoublePoint(pt.X / scale, pt.Y / scale);
}

1.9 坐标范围限制

源代码定义了坐标范围常量:

#if use_int32
    public const cInt loRange = 0x7FFF;
    public const cInt hiRange = 0x7FFF;
#else
    public const cInt loRange = 0x3FFFFFFF;
    public const cInt hiRange = 0x3FFFFFFFFFFFFFFFL; 
#endif

1.9.1 范围说明

模式 loRange hiRange 说明
32位 32,767 32,767 约 ±3.2×10⁴
64位 1,073,741,823 4,611,686,018,427,387,903 约 ±10⁹

1.9.2 范围检查

void RangeTest(IntPoint Pt, ref bool useFullRange)
{
  if (useFullRange)
  {
    if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) 
      throw new ClipperException("Coordinate outside allowed range");
  }
  else if (Pt.X > loRange || Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) 
  {
    useFullRange = true;
    RangeTest(Pt, ref useFullRange);
  }
}

自动范围扩展:当坐标超出 loRange 但未超出 hiRange 时,自动切换到完整范围模式,启用 128 位中间计算。

1.10 异常处理

Clipper 定义了自己的异常类型:

public class ClipperException : Exception
{
    public ClipperException(string description) : base(description) { }
}

常见的异常情况:

  1. 坐标超出范围"Coordinate outside allowed range"
  2. 无效的裁剪操作"AddPath: Open paths must be subject."
  3. 处理错误"ProcessIntersections error"

1.11 快速使用示例

以下是一个简单的多边形裁剪示例:

using ClipperLib;

class Example
{
    static void Main()
    {
        // 创建主体多边形(正方形)
        Path subject = new Path();
        subject.Add(new IntPoint(0, 0));
        subject.Add(new IntPoint(100, 0));
        subject.Add(new IntPoint(100, 100));
        subject.Add(new IntPoint(0, 100));

        // 创建裁剪多边形(另一个正方形,部分重叠)
        Path clip = new Path();
        clip.Add(new IntPoint(50, 50));
        clip.Add(new IntPoint(150, 50));
        clip.Add(new IntPoint(150, 150));
        clip.Add(new IntPoint(50, 150));

        // 创建 Clipper 实例并添加多边形
        Clipper clipper = new Clipper();
        clipper.AddPath(subject, PolyType.ptSubject, true);
        clipper.AddPath(clip, PolyType.ptClip, true);

        // 执行交集运算
        Paths solution = new Paths();
        clipper.Execute(ClipType.ctIntersection, solution);

        // 输出结果
        Console.WriteLine($"结果多边形数量: {solution.Count}");
        foreach (var path in solution)
        {
            Console.WriteLine($"多边形顶点数: {path.Count}");
            foreach (var pt in path)
            {
                Console.WriteLine($"  ({pt.X}, {pt.Y})");
            }
        }
    }
}

1.12 本章小结

本章介绍了 Clipper1 C# 源代码的整体结构和设计理念:

  1. 文件结构:单一源文件 clipper.cs,约 4800 行代码
  2. 预处理器指令use_int32use_xyzuse_lines 控制编译选项
  3. 类型系统:使用整数坐标确保数值稳定性
  4. 算法基础:基于 Vatti 的扫描线多边形裁剪算法
  5. 核心类Clipper(裁剪)、ClipperOffset(偏移)、PolyTree(结果组织)

在后续章节中,我们将深入分析每个核心数据结构和算法实现的细节。


返回目录 | 下一章:核心数据结构