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

推荐订阅源

Spread Privacy
Spread Privacy
A
Arctic Wolf
T
Threatpost
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The GitHub Blog
The GitHub Blog
博客园 - 聂微东
Cyberwarzone
Cyberwarzone
博客园 - Franky
V2EX - 技术
V2EX - 技术
The Hacker News
The Hacker News
量子位
TaoSecurity Blog
TaoSecurity Blog
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
P
Palo Alto Networks Blog
Scott Helme
Scott Helme
D
DataBreaches.Net
T
Troy Hunt's Blog
T
Threat Research - Cisco Blogs
美团技术团队
雷峰网
雷峰网
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
H
Hacker News: Front Page
D
Docker
C
Check Point Blog
G
GRAHAM CLULEY
H
Heimdal Security Blog
IT之家
IT之家
博客园 - 叶小钗
The Cloudflare Blog
H
Help Net Security
M
MIT News - Artificial intelligence
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Security Archives - TechRepublic
Security Archives - TechRepublic
N
Netflix TechBlog - Medium
Apple Machine Learning Research
Apple Machine Learning Research
I
Intezer
P
Proofpoint News Feed
S
Security Affairs
P
Privacy International News Feed
U
Unit 42
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Webroot Blog
Webroot Blog
Google DeepMind News
Google DeepMind News
大猫的无限游戏
大猫的无限游戏
人人都是产品经理
人人都是产品经理
N
News and Events Feed by Topic
Know Your Adversary
Know Your Adversary
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org

博客园 - 我才是银古

第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章:布尔运算执行流程 第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
第13章:ClipperD 浮点裁剪类
我才是银古 · 2026-04-11 · via 博客园 - 我才是银古

第13章:ClipperD 浮点裁剪类

13.1 概述

ClipperD 是 Clipper2 提供的浮点坐标裁剪类。它内部将浮点坐标缩放为整数进行计算,然后将结果转换回浮点数。这种设计既保留了整数运算的精确性,又提供了浮点接口的便利性。

13.2 类定义

13.2.1 类声明

public class ClipperD : ClipperBase
{
    private readonly double _scale;
    private readonly double _invScale;
    
#if USINGZ
    private ZCallbackD? _zCallback;
    public ZCallbackD? ZCallback
    {
        get => _zCallback;
        set => _zCallback = value;
    }
#endif
    
    public ClipperD(int roundingDecimalPrecision = 2)
    {
        InternalClipper.CheckPrecision(roundingDecimalPrecision);
        _scale = Math.Pow(10, roundingDecimalPrecision);
        _invScale = 1 / _scale;
    }
}

13.2.2 精度参数

// roundingDecimalPrecision 范围:-8 到 8
// 
// precision = 2:  _scale = 100,    精确到小数点后2位
// precision = 3:  _scale = 1000,   精确到小数点后3位
// precision = 0:  _scale = 1,      整数
// precision = -2: _scale = 0.01,   精确到百位

13.3 缩放原理

13.3.1 坐标转换

浮点坐标 → 整数坐标(乘以 scale)
(12.345, 67.890) → (1234, 6789) [precision=2]

整数坐标 → 浮点坐标(乘以 invScale)
(1234, 6789) → (12.34, 67.89) [precision=2]

13.3.2 Scale 和 InvScale

public ClipperD(int roundingDecimalPrecision = 2)
{
    // 检查精度范围
    InternalClipper.CheckPrecision(roundingDecimalPrecision);
    
    // 计算缩放因子
    _scale = Math.Pow(10, roundingDecimalPrecision);
    _invScale = 1 / _scale;
}

13.4 AddPath/AddPaths 方法

13.4.1 添加浮点路径

public void AddPath(PathD path, PathType pathType, bool isOpen = false)
{
    AddPaths(new PathsD { path }, pathType, isOpen);
}

public void AddPaths(PathsD paths, PathType pathType, bool isOpen = false)
{
    if (paths.Count == 0) return;
    
    // 转换为整数路径
    Paths64 paths64 = new Paths64(paths.Count);
    foreach (PathD path in paths)
    {
        Path64 path64 = new Path64(path.Count);
        foreach (PointD pt in path)
        {
            path64.Add(new Point64(pt, _scale));
        }
        paths64.Add(path64);
    }
    
    // 调用基类方法
    base.AddPaths(paths64, pathType, isOpen);
}

13.4.2 便捷方法

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

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

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

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

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

13.5 Execute 方法

13.5.1 主要重载

// 输出到 PathsD
public bool Execute(ClipType clipType, FillRule fillRule, PathsD closedSolution)
{
    return Execute(clipType, fillRule, closedSolution, new PathsD());
}

// 分离闭合和开放路径
public bool Execute(ClipType clipType, FillRule fillRule,
    PathsD closedSolution, PathsD openSolution)
{
    closedSolution.Clear();
    openSolution.Clear();
    
    try
    {
        ExecuteInternal(clipType, fillRule);
        
        // 构建整数路径
        Paths64 closedPaths64 = new Paths64();
        Paths64 openPaths64 = new Paths64();
        BuildPaths(closedPaths64, openPaths64);
        
        // 转换为浮点路径
        ConvertPaths(closedPaths64, closedSolution);
        ConvertPaths(openPaths64, openSolution);
    }
    catch
    {
        return false;
    }
    
    ClearSolution();
    return true;
}

// 输出到 PolyTreeD
public bool Execute(ClipType clipType, FillRule fillRule, PolyTreeD polytree)
{
    return Execute(clipType, fillRule, polytree, new PathsD());
}

public bool Execute(ClipType clipType, FillRule fillRule,
    PolyTreeD polytree, PathsD openSolution)
{
    polytree.Clear();
    openSolution.Clear();
    
    try
    {
        ExecuteInternal(clipType, fillRule);
        
        // 构建整数多边形树
        PolyTree64 polytree64 = new PolyTree64();
        Paths64 openPaths64 = new Paths64();
        BuildTree(polytree64, openPaths64);
        
        // 转换为浮点
        ConvertPolyTree(polytree64, polytree);
        ConvertPaths(openPaths64, openSolution);
    }
    catch
    {
        return false;
    }
    
    ClearSolution();
    return true;
}

13.5.2 ConvertPaths

private void ConvertPaths(Paths64 paths64, PathsD pathsD)
{
    pathsD.Capacity = paths64.Count;
    
    foreach (Path64 path64 in paths64)
    {
        PathD pathD = new PathD(path64.Count);
        
        foreach (Point64 pt in path64)
        {
            pathD.Add(new PointD(pt, _invScale));
        }
        
        pathsD.Add(pathD);
    }
}

13.5.3 ConvertPolyTree

private void ConvertPolyTree(PolyPath64 polytree64, PolyPathD polytreeD)
{
    polytreeD.Clear();
    polytreeD.Scale = _invScale;
    
    ConvertPolyTreeNode(polytree64, polytreeD);
}

private void ConvertPolyTreeNode(PolyPath64 node64, PolyPathD nodeD)
{
    // 转换当前节点的路径
    if (node64.Polygon != null)
    {
        PathD pathD = new PathD(node64.Polygon.Count);
        foreach (Point64 pt in node64.Polygon)
        {
            pathD.Add(new PointD(pt, _invScale));
        }
        nodeD.Polygon = pathD;
    }
    
    // 递归转换子节点
    foreach (PolyPath64 child64 in node64)
    {
        PolyPathD childD = nodeD.AddChild();
        ConvertPolyTreeNode(child64, childD);
    }
}

13.6 精度控制

13.6.1 精度检查

internal static void CheckPrecision(int precision)
{
    if (precision < -8 || precision > 8)
        throw new Exception("Error: Precision is out of range.");
}

13.6.2 精度选择建议

应用场景 建议精度 说明
屏幕坐标 0-1 像素级精度
CAD 应用 2-4 毫米级精度
地理坐标 6-8 经纬度精度
科学计算 8 最高精度

13.6.3 精度与性能

// 高精度意味着更大的整数值
// precision = 8: _scale = 100000000
// 
// 坐标值被放大后可能接近 MaxCoord 限制
// 需要平衡精度和坐标范围

13.7 Z 坐标处理

13.7.1 ZCallbackD 委托

#if USINGZ
public delegate void ZCallbackD(PointD bot1, PointD top1,
    PointD bot2, PointD top2, ref PointD intersectPt);
#endif

13.7.2 Z 回调转换

#if USINGZ
private void InternalZCallback(Point64 bot1, Point64 top1,
    Point64 bot2, Point64 top2, ref Point64 intersectPt)
{
    if (_zCallback == null) return;
    
    // 转换为浮点
    PointD bot1D = new PointD(bot1, _invScale);
    PointD top1D = new PointD(top1, _invScale);
    PointD bot2D = new PointD(bot2, _invScale);
    PointD top2D = new PointD(top2, _invScale);
    PointD intersectPtD = new PointD(intersectPt, _invScale);
    
    // 调用用户回调
    _zCallback(bot1D, top1D, bot2D, top2D, ref intersectPtD);
    
    // Z 值保持整数
    intersectPt = new Point64(intersectPt.X, intersectPt.Y, intersectPtD.z);
}
#endif

13.7.3 使用示例

#if USINGZ
ClipperD clipperD = new ClipperD(2);
clipperD.ZCallback = (bot1, top1, bot2, top2, ref PointD pt) =>
{
    // 线性插值
    if (Math.Abs(top1.y - bot1.y) > 0.0001)
    {
        double t = (pt.y - bot1.y) / (top1.y - bot1.y);
        pt.z = (long)(bot1.z + t * (top1.z - bot1.z));
    }
};
#endif

13.8 与 Clipper64 的对比

13.8.1 功能对比

特性 Clipper64 ClipperD
输入类型 Point64, Path64 PointD, PathD
输出类型 Paths64, PolyTree64 PathsD, PolyTreeD
内部计算 整数 整数(缩放后)
精度控制 可配置
性能 略快 有转换开销

13.8.2 选择建议

// 使用 Clipper64 当:
// - 数据本身是整数
// - 需要最高性能
// - 精确控制坐标

// 使用 ClipperD 当:
// - 数据是浮点数
// - 需要小数精度
// - 便利性优先

13.9 使用示例

13.9.1 基本使用

ClipperD clipper = new ClipperD(3);  // 3 位小数精度

PathD subject = new PathD {
    new PointD(0.0, 0.0),
    new PointD(100.5, 0.0),
    new PointD(100.5, 100.5),
    new PointD(0.0, 100.5)
};
clipper.AddSubject(subject);

PathD clip = new PathD {
    new PointD(50.25, 50.25),
    new PointD(150.75, 50.25),
    new PointD(150.75, 150.75),
    new PointD(50.25, 150.75)
};
clipper.AddClip(clip);

PathsD result = new PathsD();
clipper.Execute(ClipType.Intersection, FillRule.NonZero, result);

// 结果坐标精确到 0.001
foreach (PathD path in result)
{
    foreach (PointD pt in path)
    {
        Console.WriteLine($"({pt.x:F3}, {pt.y:F3})");
    }
}

13.9.2 混合使用

// 可以混合使用静态方法和类实例

// 方法1:使用 ClipperD 类
ClipperD clipper = new ClipperD(2);
clipper.AddSubject(subjectPaths);
clipper.AddClip(clipPaths);
PathsD result1 = new PathsD();
clipper.Execute(ClipType.Union, FillRule.NonZero, result1);

// 方法2:使用静态方法
PathsD result2 = Clipper.Union(subjectPaths, clipPaths, 
    FillRule.NonZero, precision: 2);

13.10 常见问题

13.10.1 精度丢失

// 问题:输出坐标与输入略有不同
ClipperD clipper = new ClipperD(2);  // 2 位小数

PointD input = new PointD(12.345, 67.891);
// 内部:(1234, 6789)
// 输出:(12.34, 67.89) - 第三位小数丢失

// 解决:使用更高精度
ClipperD clipper = new ClipperD(4);  // 4 位小数

13.10.2 坐标溢出

// 问题:高精度 + 大坐标可能溢出
ClipperD clipper = new ClipperD(8);  // scale = 100000000

PointD bigPt = new PointD(1e12, 1e12);
// 缩放后:1e20 > MaxCoord,溢出!

// 解决:限制坐标范围或降低精度
// 使用精度 6,坐标范围约 ±9e12

13.10.3 性能优化

// 批量处理时,避免重复创建 ClipperD
ClipperD clipper = new ClipperD(2);

foreach (var data in dataList)
{
    clipper.Clear();
    clipper.AddSubject(data.Subject);
    clipper.AddClip(data.Clip);
    
    PathsD result = new PathsD();
    clipper.Execute(ClipType.Intersection, FillRule.NonZero, result);
    
    // 处理结果...
}

13.11 内部实现细节

13.11.1 Point64 构造

// PointD 转 Point64
public Point64(PointD pt, double scale)
{
    X = (long)Math.Round(pt.x * scale, MidpointRounding.AwayFromZero);
    Y = (long)Math.Round(pt.y * scale, MidpointRounding.AwayFromZero);
#if USINGZ
    Z = pt.z;  // Z 直接复制,不缩放
#endif
}

13.11.2 PointD 构造

// Point64 转 PointD
public PointD(Point64 pt, double scale)
{
    x = pt.X * scale;  // scale 通常是 invScale
    y = pt.Y * scale;
#if USINGZ
    z = pt.Z;
#endif
}

13.12 本章小结

ClipperD 提供了浮点坐标的裁剪接口:

  1. 核心原理:浮点坐标缩放为整数计算
  2. 精度控制:通过 roundingDecimalPrecision 参数
  3. API 对应:与 Clipper64 对应的浮点版本方法
  4. Z 坐标:支持浮点 Z 回调
  5. 使用场景:需要浮点精度的应用

选择 ClipperD 还是 Clipper64 取决于数据类型和精度需求。


上一章:Clipper64裁剪类详解 | 返回目录 | 下一章:布尔运算执行流程