第3章_布尔运算操作

news/2025/11/29 12:20:19/文章来源:https://www.cnblogs.com/znlgis/p/19285979

第三章 布尔运算操作(C#版)

3.1 引言

布尔运算是Clipper2最核心的功能之一,它允许我们对多边形进行交集、并集、差集和异或等操作。这些操作在计算机图形学、CAD设计、GIS分析等领域有着广泛的应用。本章将深入介绍Clipper2 C#版本中布尔运算的原理、使用方法和最佳实践。

3.2 布尔运算的基本概念

3.2.1 什么是布尔运算

布尔运算源自数学中的集合论,用于组合两个或多个几何形状。在二维几何中,布尔运算主要包括四种基本操作:

交集(Intersection)

交集运算返回两个多边形的重叠区域。只有同时属于两个输入多边形的点才会出现在结果中。

数学表示:A ∩ B

应用场景:

  • 计算两个区域的公共部分
  • 视野裁剪
  • 碰撞检测的精确区域

并集(Union)

并集运算将两个多边形合并为一个,得到它们的完整覆盖区域。

数学表示:A ∪ B

应用场景:

  • 合并相邻区域
  • 创建复合形状
  • 地块合并

差集(Difference)

差集运算从第一个多边形中减去第二个多边形的区域。

数学表示:A - B 或 A \ B

应用场景:

  • 从形状中切除区域
  • 创建孔洞
  • 遮罩效果

异或(XOR)

异或运算返回两个多边形不重叠的区域,即只属于其中一个多边形的部分。

数学表示:A ⊕ B 或 (A ∪ B) - (A ∩ B)

应用场景:

  • 检测变化区域
  • 创建环形效果
  • 对称差运算

3.2.2 主体与裁剪

在Clipper2中,参与布尔运算的多边形被分为两类:

主体(Subject):布尔运算的主要对象,差集运算中被减去的一方

裁剪(Clip):用于与主体进行运算的多边形,差集运算中作为"刀"的一方

using Clipper2Lib;class SubjectClipExample
{static void Main(){Paths64 subject = new Paths64();  // 主体多边形subject.Add(Clipper.MakePath(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }));Paths64 clip = new Paths64();     // 裁剪多边形clip.Add(Clipper.MakePath(new long[] { 50, 50, 150, 50, 150, 150, 50, 150 }));// 对于交集、并集、异或,subject和clip的顺序不影响结果// 对于差集,subject - clip 和 clip - subject 的结果不同}
}

3.2.3 填充规则的作用

填充规则决定了多边形的哪些区域被认为是"内部"。这对于处理自交多边形和复杂的嵌套结构至关重要。

Clipper2支持四种填充规则:

public enum FillRule
{EvenOdd,   // 偶奇规则NonZero,   // 非零规则Positive,  // 正数规则Negative   // 负数规则
}

详细的填充规则说明请参见第一章。

3.3 使用简化API进行布尔运算

Clipper2提供了一组简化的静态函数,可以用一行代码完成布尔运算。

3.3.1 交集运算

using System;
using Clipper2Lib;class IntersectionExample
{static void Main(){// 创建两个相交的正方形Paths64 subject = new Paths64();subject.Add(Clipper.MakePath(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }));Paths64 clip = new Paths64();clip.Add(Clipper.MakePath(new long[] { 50, 50, 150, 50, 150, 150, 50, 150 }));// 执行交集运算Paths64 result = Clipper.Intersect(subject, clip, FillRule.NonZero);// result 包含一个50x50的正方形:(50,50) → (100,50) → (100,100) → (50,100)Console.WriteLine($"交集结果包含 {result.Count} 个多边形");foreach (Path64 path in result){Console.Write("顶点: ");foreach (Point64 pt in path){Console.Write($"({pt.X},{pt.Y}) ");}Console.WriteLine();}}
}

3.3.2 并集运算

using Clipper2Lib;class UnionExample
{static void Main(){// 合并两个正方形Paths64 subject = new Paths64();subject.Add(Clipper.MakePath(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }));Paths64 clip = new Paths64();clip.Add(Clipper.MakePath(new long[] { 50, 50, 150, 50, 150, 150, 50, 150 }));Paths64 result = Clipper.Union(subject, clip, FillRule.NonZero);// result 包含一个L形多边形,覆盖两个正方形的区域Console.WriteLine($"并集结果包含 {result.Count} 个多边形");}
}
**单一集合的并集**对于只有主体多边形(没有裁剪多边形)的情况,Union函数可以用于:
- 合并重叠的多边形
- 简化自交多边形
- 分解复杂多边形为简单多边形```csharp
using Clipper2Lib;class UnionSingleSetExample
{static void Main(){// 合并多个重叠的多边形Paths64 circles = new Paths64();circles.Add(Clipper.MakePath(new long[] { 0, 0, 50, 0, 50, 50, 0, 50 }));circles.Add(Clipper.MakePath(new long[] { 40, 0, 90, 0, 90, 50, 40, 50 }));circles.Add(Clipper.MakePath(new long[] { 80, 0, 130, 0, 130, 50, 80, 50 }));// 使用单参数版本的UnionPaths64 merged = Clipper.Union(circles, FillRule.NonZero);Console.WriteLine($"合并后结果包含 {merged.Count} 个多边形");}
}

3.3.3 差集运算

using Clipper2Lib;class DifferenceExample
{static void Main(){// 从大正方形中减去小正方形Paths64 subject = new Paths64();subject.Add(Clipper.MakePath(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }));Paths64 clip = new Paths64();clip.Add(Clipper.MakePath(new long[] { 25, 25, 75, 25, 75, 75, 25, 75 }));Paths64 result = Clipper.Difference(subject, clip, FillRule.NonZero);// result 包含一个带有中心孔洞的正方形Console.WriteLine($"差集结果包含 {result.Count} 个路径");// 注意:差集运算的顺序很重要// subject - clip ≠ clip - subjectPaths64 result2 = Clipper.Difference(clip, subject, FillRule.NonZero);// result2 是空的,因为clip完全在subject内部Console.WriteLine($"反向差集结果包含 {result2.Count} 个路径");}
}

3.3.4 异或运算

using Clipper2Lib;class XorExample
{static void Main(){// 两个部分重叠的正方形进行异或运算Paths64 subject = new Paths64();subject.Add(Clipper.MakePath(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }));Paths64 clip = new Paths64();clip.Add(Clipper.MakePath(new long[] { 50, 50, 150, 50, 150, 150, 50, 150 }));Paths64 result = Clipper.Xor(subject, clip, FillRule.NonZero);// result 包含两个L形区域Console.WriteLine($"异或结果包含 {result.Count} 个多边形");}
}

3.3.5 使用浮点数坐标

对于浮点数坐标,可以使用PathsD类型:

using Clipper2Lib;class FloatCoordinatesExample
{static void Main(){// 使用浮点数坐标PathsD subject = new PathsD();subject.Add(Clipper.MakePath(new double[] { 0.0, 0.0, 10.5, 0.0, 10.5, 10.5, 0.0, 10.5 }));PathsD clip = new PathsD();clip.Add(Clipper.MakePath(new double[] { 5.25, 5.25, 15.75, 5.25, 15.75, 15.75, 5.25, 15.75 }));// 第四个参数指定精度(小数位数)PathsD result = Clipper.Intersect(subject, clip, FillRule.NonZero, 2);Console.WriteLine($"结果包含 {result.Count} 个多边形");}
}

3.4 使用Clipper类进行高级控制

对于需要更多控制的场景,可以使用Clipper64或ClipperD类。

3.4.1 Clipper64类

using System;
using Clipper2Lib;class Clipper64Example
{static void Main(){Clipper64 clipper = new Clipper64();// 添加主体多边形Paths64 subject = new Paths64();subject.Add(Clipper.MakePath(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }));clipper.AddSubject(subject);// 添加裁剪多边形Paths64 clip = new Paths64();clip.Add(Clipper.MakePath(new long[] { 50, 50, 150, 50, 150, 150, 50, 150 }));clipper.AddClip(clip);// 执行布尔运算Paths64 result = new Paths64();clipper.Execute(ClipType.Intersection, FillRule.NonZero, result);Console.WriteLine($"结果包含 {result.Count} 个多边形");}
}

3.4.2 ClipType枚举

public enum ClipType
{None,Intersection,Union,Difference,Xor
}

3.4.3 添加开放路径

除了闭合的多边形,Clipper2还支持开放的折线:

using Clipper2Lib;class OpenPathExample
{static void Main(){Clipper64 clipper = new Clipper64();// 添加闭合的主体多边形Paths64 subject = new Paths64();subject.Add(Clipper.MakePath(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }));clipper.AddSubject(subject);// 添加开放的主体折线Paths64 subjectOpen = new Paths64();subjectOpen.Add(Clipper.MakePath(new long[] { 0, 50, 150, 50 }));  // 一条水平线clipper.AddOpenSubject(subjectOpen);// 添加裁剪多边形Paths64 clip = new Paths64();clip.Add(Clipper.MakePath(new long[] { 25, 25, 75, 25, 75, 75, 25, 75 }));clipper.AddClip(clip);// 执行运算并分别获取闭合和开放结果Paths64 closedResult = new Paths64();Paths64 openResult = new Paths64();clipper.Execute(ClipType.Difference, FillRule.NonZero, closedResult, openResult);// closedResult 包含裁剪后的多边形// openResult 包含裁剪后的折线Console.WriteLine($"闭合路径结果: {closedResult.Count} 个");Console.WriteLine($"开放路径结果: {openResult.Count} 个");}
}

3.4.4 使用PolyTree输出

当需要保留多边形的层次结构时,使用PolyTree输出:

using Clipper2Lib;class PolyTreeOutputExample
{static void Main(){Clipper64 clipper = new Clipper64();Paths64 subject = new Paths64();subject.Add(Clipper.MakePath(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }));clipper.AddSubject(subject);Paths64 clip = new Paths64();clip.Add(Clipper.MakePath(new long[] { 25, 25, 75, 25, 75, 75, 25, 75 }));clipper.AddClip(clip);// 使用PolyTree输出PolyTree64 tree = new PolyTree64();Paths64 openPaths = new Paths64();clipper.Execute(ClipType.Intersection, FillRule.NonZero, tree, openPaths);// 遍历PolyTree获取层次结构for (int i = 0; i < tree.Count; i++){PolyPath64 child = tree[i];if (!child.IsHole){// 这是一个外边界多边形Path64 exterior = child.Polygon;Console.WriteLine($"外边界多边形顶点数: {exterior.Count}");// 获取其孔洞for (int j = 0; j < child.Count; j++){Path64 holePath = child[j].Polygon;Console.WriteLine($"  孔洞顶点数: {holePath.Count}");}}}}
}

3.4.5 复用Clipper对象

在循环中复用Clipper对象可以提高性能:

using Clipper2Lib;class ReuseClipperExample
{static void Main(){Clipper64 clipper = new Clipper64();Paths64 clip = new Paths64();clip.Add(Clipper.MakePath(new long[] { 50, 50, 150, 50, 150, 150, 50, 150 }));for (int i = 0; i < 1000; i++){// 清空之前的数据,但保留已分配的内存clipper.Clear();// 添加新的多边形Paths64 subject = new Paths64();subject.Add(Clipper.MakePath(new long[] { i, 0, i + 100, 0, i + 100, 100, i, 100 }));clipper.AddSubject(subject);clipper.AddClip(clip);// 执行运算Paths64 result = new Paths64();clipper.Execute(ClipType.Intersection, FillRule.NonZero, result);// 处理结果...}}
}

3.5 处理复杂多边形

3.5.1 带孔洞的多边形

using Clipper2Lib;class PolygonWithHoleExample
{static void Main(){// 创建一个带孔洞的多边形Paths64 subject = new Paths64();// 外边界(逆时针方向)subject.Add(Clipper.MakePath(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }));// 孔洞(顺时针方向)subject.Add(Clipper.MakePath(new long[] { 25, 25, 25, 75, 75, 75, 75, 25 }));// 裁剪多边形Paths64 clip = new Paths64();clip.Add(Clipper.MakePath(new long[] { 50, 0, 150, 0, 150, 100, 50, 100 }));// 执行交集运算Paths64 result = Clipper.Intersect(subject, clip, FillRule.NonZero);// 结果会正确处理孔洞Console.WriteLine($"结果包含 {result.Count} 个路径");}
}

3.5.2 自交多边形

自交多边形是指边界与自身相交的多边形。不同的填充规则会产生不同的结果:

using Clipper2Lib;class SelfIntersectingExample
{static void Main(){// 创建一个8字形的自交多边形Paths64 figure8 = new Paths64();figure8.Add(Clipper.MakePath(new long[] { 0, 0, 100, 100, 100, 0, 0, 100 }));// 使用NonZero规则 - 两个半部分都被填充Paths64 resultNonZero = Clipper.Union(figure8, FillRule.NonZero);Console.WriteLine($"NonZero规则结果: {resultNonZero.Count} 个多边形");// 使用EvenOdd规则 - 中间交叉点处会有孔洞Paths64 resultEvenOdd = Clipper.Union(figure8, FillRule.EvenOdd);Console.WriteLine($"EvenOdd规则结果: {resultEvenOdd.Count} 个多边形");}
}

3.5.3 多个分离的多边形

using Clipper2Lib;class MultiplePolygonsExample
{static void Main(){// 主体包含多个分离的多边形Paths64 subject = new Paths64();subject.Add(Clipper.MakePath(new long[] { 0, 0, 50, 0, 50, 50, 0, 50 }));subject.Add(Clipper.MakePath(new long[] { 100, 0, 150, 0, 150, 50, 100, 50 }));subject.Add(Clipper.MakePath(new long[] { 200, 0, 250, 0, 250, 50, 200, 50 }));// 裁剪多边形跨越多个主体Paths64 clip = new Paths64();clip.Add(Clipper.MakePath(new long[] { 25, -25, 225, -25, 225, 75, 25, 75 }));// 交集运算会正确处理每个主体Paths64 result = Clipper.Intersect(subject, clip, FillRule.NonZero);// result 包含3个多边形Console.WriteLine($"结果包含 {result.Count} 个多边形");}
}

3.5.4 重叠的边

当两个多边形有共享的边时,Clipper2能够正确处理:

using Clipper2Lib;class OverlappingEdgesExample
{static void Main(){// 两个共享一条边的正方形Paths64 subject = new Paths64();subject.Add(Clipper.MakePath(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }));Paths64 clip = new Paths64();clip.Add(Clipper.MakePath(new long[] { 100, 0, 200, 0, 200, 100, 100, 100 }));// clip的左边与subject的右边重合// 并集运算会正确合并它们Paths64 result = Clipper.Union(subject, clip, FillRule.NonZero);// result 是一个200x100的矩形Console.WriteLine($"结果包含 {result.Count} 个多边形");}
}

3.6 布尔运算的性能优化

3.6.1 减少顶点数量

顶点数量是影响性能的主要因素。可以使用路径简化来减少顶点:

using Clipper2Lib;class SimplifyExample
{static void Main(){Paths64 complexPaths = new Paths64();// ... 添加复杂路径Paths64 clip = new Paths64();clip.Add(Clipper.MakePath(new long[] { 50, 50, 150, 50, 150, 150, 50, 150 }));// 在布尔运算前简化路径PathsD complexPathsD = Clipper.Paths64ToPathsD(complexPaths);PathsD simplified = Clipper.SimplifyPaths(complexPathsD, 2.0);Paths64 simplifiedPaths = Clipper.PathsDToPaths64(simplified);Paths64 result = Clipper.Intersect(simplifiedPaths, clip, FillRule.NonZero);}
}

3.6.2 使用矩形裁剪

当裁剪区域是矩形时,使用专门的RectClip函数:

using Clipper2Lib;class RectClipPerformanceExample
{static void Main(){Paths64 subject = new Paths64();subject.Add(Clipper.MakePath(new long[] { 0, 0, 200, 0, 200, 200, 0, 200 }));// 常规布尔运算裁剪Paths64 clipPolygon = new Paths64();clipPolygon.Add(Clipper.MakePath(new long[] { 50, 50, 150, 50, 150, 150, 50, 150 }));Paths64 result1 = Clipper.Intersect(subject, clipPolygon, FillRule.NonZero);// 使用优化的矩形裁剪Rect64 clipRect = new Rect64(50, 50, 150, 150);Paths64 result2 = Clipper.RectClip(clipRect, subject);// result2 与 result1 相同,但速度更快}
}

3.6.3 批量处理

对于大量相同裁剪区域的操作,可以复用Clipper对象:

using Clipper2Lib;class BatchProcessingExample
{static void ProcessBatch(List<Paths64> subjects, Paths64 clip){Clipper64 clipper = new Clipper64();foreach (Paths64 subject in subjects){clipper.Clear();clipper.AddClip(clip);clipper.AddSubject(subject);Paths64 result = new Paths64();clipper.Execute(ClipType.Intersection, FillRule.NonZero, result);// 处理结果...}}
}

3.6.4 多线程处理

Clipper2的Clipper对象不是线程安全的,但可以在不同线程中使用不同的Clipper实例:

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Clipper2Lib;class MultiThreadExample
{static void ProcessInParallel(List<Paths64> allSubjects, Paths64 clip){int processorCount = Environment.ProcessorCount;Parallel.ForEach(allSubjects, subject =>{// 每个任务有自己的Clipper实例Clipper64 clipper = new Clipper64();clipper.AddSubject(subject);clipper.AddClip(clip);Paths64 result = new Paths64();clipper.Execute(ClipType.Intersection, FillRule.NonZero, result);// 处理结果...});}
}

3.7 布尔运算的常见问题

3.7.1 结果为空

问题:执行布尔运算后结果为空。

可能的原因

  1. 两个多边形不相交(对于交集运算)
  2. 多边形的方向不正确
  3. 坐标精度问题

解决方法

using Clipper2Lib;class EmptyResultTroubleshooting
{static void DiagnoseEmptyResult(Paths64 subject, Paths64 clip){// 检查多边形是否有效bool valid = subject.Count > 0 && clip.Count > 0;Console.WriteLine($"多边形有效: {valid}");// 检查边界框是否相交Rect64 subjectBounds = Clipper.GetBounds(subject);Rect64 clipBounds = Clipper.GetBounds(clip);bool mayIntersect = !(subjectBounds.right < clipBounds.left || clipBounds.right < subjectBounds.left ||subjectBounds.bottom < clipBounds.top ||clipBounds.bottom < subjectBounds.top);Console.WriteLine($"边界框可能相交: {mayIntersect}");// 确保多边形方向正确for (int i = 0; i < subject.Count; i++){bool isPositive = Clipper.IsPositive(subject[i]);Console.WriteLine($"Subject[{i}] 是正方向: {isPositive}");}}
}

3.7.2 意外的孔洞

问题:并集运算后出现意外的孔洞。

原因:填充规则与多边形方向不匹配。

解决方法

using Clipper2Lib;class UnexpectedHolesSolution
{static Paths64 UnionWithCorrectOrientation(Paths64 paths){// 使用EvenOdd规则时,方向不重要Paths64 result1 = Clipper.Union(paths, FillRule.EvenOdd);// 或者使用NonZero规则并确保方向一致Paths64 correctedPaths = new Paths64();foreach (Path64 path in paths){if (!Clipper.IsPositive(path)){correctedPaths.Add(Clipper.ReversePath(path));}else{correctedPaths.Add(path);}}Paths64 result2 = Clipper.Union(correctedPaths, FillRule.NonZero);return result2;}
}

3.7.3 自交多边形的处理

问题:自交多边形产生意外的结果。

解决方法

using Clipper2Lib;class SelfIntersectionHandling
{static Paths64 SimplifySelfIntersecting(Paths64 selfIntersecting){// 使用Union简化自交多边形Paths64 simplified = Clipper.Union(selfIntersecting, FillRule.NonZero);return simplified;}

3.7.4 精度损失

问题:浮点数转换导致精度损失。

解决方法

using Clipper2Lib;class PrecisionHandling
{static void HandlePrecision(){// 使用足够大的缩放因子double scale = 1000000.0;  // 6位小数精度PathsD subjectD = new PathsD();subjectD.Add(Clipper.MakePath(new double[] { 0.0, 0.0, 10.123456, 0.0, 10.123456, 10.123456, 0.0, 10.123456 }));PathsD clipD = new PathsD();clipD.Add(Clipper.MakePath(new double[] { 5.0, 5.0, 15.0, 5.0, 15.0, 15.0, 5.0, 15.0 }));// 手动缩放Paths64 scaledSubject = Clipper.ScalePaths64(subjectD, scale);Paths64 scaledClip = Clipper.ScalePaths64(clipD, scale);Paths64 result = Clipper.Intersect(scaledSubject, scaledClip, FillRule.NonZero);// 缩放回原始尺寸PathsD resultD = Clipper.ScalePathsD(result, 1.0 / scale);}
}

3.8 实际应用示例

3.8.1 创建复合形状

using Clipper2Lib;class CompositeShapeExample
{static Path64 MakeCircle(Point64 center, int radius, int segments = 32){Path64 circle = new Path64();for (int i = 0; i < segments; i++){double angle = 2 * Math.PI * i / segments;circle.Add(new Point64(center.X + (long)(radius * Math.Cos(angle)),center.Y + (long)(radius * Math.Sin(angle))));}return circle;}static void Main(){// 创建圆形Paths64 circle = new Paths64();circle.Add(MakeCircle(new Point64(100, 100), 80));// 创建内部小圆Paths64 innerCircle = new Paths64();innerCircle.Add(MakeCircle(new Point64(100, 100), 40));Paths64 result = Clipper.Difference(circle, innerCircle, FillRule.NonZero);// result 是一个圆环Console.WriteLine($"圆环包含 {result.Count} 个路径");}
}

3.8.2 区域合并

using Clipper2Lib;class RegionMergeExample
{static void Main(){// 合并多个重叠的区域Paths64 regions = new Paths64();regions.Add(Clipper.MakePath(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }));regions.Add(Clipper.MakePath(new long[] { 80, 0, 180, 0, 180, 100, 80, 100 }));regions.Add(Clipper.MakePath(new long[] { 160, 0, 260, 0, 260, 100, 160, 100 }));Paths64 merged = Clipper.Union(regions, FillRule.NonZero);// merged 是一个连续的区域Console.WriteLine($"合并后: {merged.Count} 个多边形");}
}

3.8.3 遮罩裁剪

using Clipper2Lib;class MaskClipExample
{static void Main(){// 使用遮罩裁剪图像区域Paths64 image = new Paths64();image.Add(Clipper.MakePath(new long[] { 0, 0, 800, 0, 800, 600, 0, 600 }));Paths64 mask = new Paths64();mask.Add(Clipper.MakePath(new long[] { 100, 100, 700, 100, 700, 500, 100, 500 }));// 添加一个孔洞mask.Add(Clipper.MakePath(new long[] { 200, 200, 200, 400, 600, 400, 600, 200 }));Paths64 result = Clipper.Intersect(image, mask, FillRule.NonZero);// result 是带有中心孔洞的遮罩区域Console.WriteLine($"遮罩裁剪结果: {result.Count} 个路径");}
}

3.8.4 地块拆分

using Clipper2Lib;class ParcelSplitExample
{static void Main(){// 用一条线将地块拆分为两部分Paths64 parcel = new Paths64();parcel.Add(Clipper.MakePath(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }));// 创建一个非常薄的矩形作为分割线Paths64 splitLine = new Paths64();splitLine.Add(Clipper.MakePath(new long[] { 50, -10, 51, -10, 51, 110, 50, 110 }));// 左半部分裁剪区域Paths64 leftClip = new Paths64();leftClip.Add(Clipper.MakePath(new long[] { -10, -10, 50, -10, 50, 110, -10, 110 }));// 右半部分裁剪区域Paths64 rightClip = new Paths64();rightClip.Add(Clipper.MakePath(new long[] { 51, -10, 110, -10, 110, 110, 51, 110 }));Paths64 left = Clipper.Intersect(parcel, leftClip, FillRule.NonZero);Paths64 right = Clipper.Intersect(parcel, rightClip, FillRule.NonZero);Console.WriteLine($"左半部分: {left.Count} 个多边形");Console.WriteLine($"右半部分: {right.Count} 个多边形");}
}

3.8.5 碰撞区域计算

using Clipper2Lib;class CollisionDetectionExample
{static Paths64 GetObjectBounds(Point64 position, int size){Paths64 bounds = new Paths64();bounds.Add(Clipper.MakePath(new long[] { position.X, position.Y, position.X + size, position.Y, position.X + size, position.Y + size, position.X, position.Y + size }));return bounds;}static void Main(){// 计算两个移动物体的碰撞区域Paths64 objectA = GetObjectBounds(new Point64(0, 0), 100);Paths64 objectB = GetObjectBounds(new Point64(50, 50), 100);Paths64 collision = Clipper.Intersect(objectA, objectB, FillRule.NonZero);if (collision.Count > 0){// 发生碰撞double collisionArea = Clipper.Area(collision);Console.WriteLine($"碰撞面积: {collisionArea}");// 根据碰撞面积决定反应}else{Console.WriteLine("未发生碰撞");}}
}

3.9 与其他库的比较

3.9.1 与NetTopologySuite的比较

特性 Clipper2 NetTopologySuite
精度 整数运算,精确 浮点运算,可能有误差
性能 非常快 较快
鲁棒性 极佳 良好
拓扑处理 需要后处理 内置
曲线支持
.NET集成 NuGet包 NuGet包

3.9.2 与其他C#几何库的比较

特性 Clipper2 其他库
学习曲线 简单 各异
依赖 各异
精度 64位整数 各异
功能范围 2D多边形裁剪 综合几何功能
许可证 Boost 各异

3.10 算法原理简介

Clipper2使用改进的Vatti算法进行布尔运算。这里简要介绍其工作原理。

3.10.1 扫描线算法

Vatti算法是一种扫描线算法。它通过一条水平扫描线从下向上扫过所有的多边形,在扫描过程中维护当前扫描线与多边形边的交点。

          y↑|     ┌────────┐|     │        │
扫描线 ───┼─────┼────────┼───→|     │   ∩    │|  ┌──┴────────┴──┐|  │              │└──┴──────────────┴────→ x

3.10.2 事件处理

算法在以下事件点进行处理:

  • 顶点事件:扫描线遇到多边形顶点
  • 交点事件:两条边相交

3.10.3 活动边列表

算法维护一个"活动边列表"(Active Edge List),包含当前与扫描线相交的所有边。随着扫描线移动,边被添加或移除。

// 伪代码示意
while (eventQueue.Count > 0)
{Event evt = eventQueue.Dequeue();if (evt.Type == EventType.Vertex){// 处理顶点事件UpdateActiveEdges(evt.Vertex);}else if (evt.Type == EventType.Intersection){// 处理交点事件SwapEdges(evt.Edge1, evt.Edge2);}// 根据填充规则和裁剪类型决定输出DetermineOutput(activeEdges, clipType, fillRule);
}

3.11 本章小结

本章我们深入学习了Clipper2 C#版本的布尔运算功能:

  1. 基本概念:交集、并集、差集、异或四种运算类型
  2. 简化API:Clipper.Intersect、Clipper.Union、Clipper.Difference、Clipper.Xor静态方法
  3. Clipper类:Clipper64和ClipperD提供更多控制选项
  4. 复杂多边形处理:孔洞、自交、重叠边的处理
  5. 性能优化:路径简化、矩形裁剪、批量处理、多线程
  6. 常见问题:结果为空、意外孔洞、自交处理、精度损失
  7. 实际应用:复合形状、区域合并、遮罩裁剪、碰撞检测等

在下一章中,我们将学习Clipper2的另一个核心功能——多边形偏移操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/980691.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第1章_Clipper2概述与安装

第一章 Clipper2概述与安装(C#版) 1.1 引言 在计算机图形学和地理信息系统(GIS)领域,多边形的布尔运算和偏移操作是极其重要的基础功能。无论是CAD软件、游戏开发、地图制作还是激光切割机的路径规划,都需要对多…

about

关于我 大家好,我是 znlgis,一名专注于 GIS(地理信息系统)开发的技术爱好者。 我做什么📺 在 B 站直播分享 GIS 开发技术 📝 撰写技术文档和教程 💻 开发开源 GIS 工具联系方式QQ群: 289280914 B站: znlgis的…

2025年质量好的mpp电力管TOP品牌厂家排行榜

2025年质量好的MPP电力管TOP品牌厂家排行榜行业背景与市场趋势随着我国电力基础设施建设的持续投入和城市电网改造的深入推进,MPP电力管作为电力电缆保护的重要材料,市场需求呈现稳定增长态势。MPP(改性聚丙烯)电力…

2025年热门的开口圆轮水泥垫块厂家最新权威实力榜

2025年热门的开口圆轮水泥垫块厂家权威实力榜行业背景与市场趋势随着中国基础设施建设的持续投入和城市化进程的加速推进,水泥制品行业迎来了新的发展机遇。作为建筑基础工程中的重要组成部分,开口圆轮水泥垫块因其优…

2025年河南十大叛逆孩子学校推荐:靠谱的问题少年教育学校有

本榜单依托河南本地青少年教育市场调研与真实家长口碑,聚焦10-18岁偏差行为青少年教育需求,筛选出十家专业机构,为陷入教育困境的家庭提供客观选型依据,助力孩子回归成长正轨。 TOP1 推荐:护航专门教育 推荐指数…

2025哈尔滨短期西点培训排名:甜品西点培训学校有哪些

近年来,哈尔滨及周边城市(齐齐哈尔、大庆、绥化、牡丹江)的西式烘焙市场快速升温,连锁饼房、网红甜品店数量年增30%,催生了对专业西点技能的旺盛需求。但市场上培训学校鱼龙混杂:部分机构低学费引流却暗藏二次收…

2025年热门的隐藏三折轨最新TOP厂家排名

2025年热门的隐藏三折轨TOP厂家排名行业背景与市场趋势随着家居智能化与空间优化需求的不断提升,隐藏三折轨作为现代家居五金的核心部件,正迎来前所未有的市场机遇。2024年全球家居五金市场规模已突破1200亿美元,其…

WGLOG日志审计系统如何采集网络设备的日志

WGLOG是一款轻量友好的日志管理平台,可以采集各种类型的日志文件,我们只需要在主机安装好采集端agent即可 那么WGLOG如何采集网络设备的日志呢,比如交换机这样的设备,因为它们可不能安装agent程序 WGLOG也可以支持…

2025年精品甜酒酿/醪糟甜酒酿厂家选购指南与推荐

2025年精品甜酒酿/醪糟甜酒酿厂家选购指南与推荐行业背景与市场趋势甜酒酿,又称醪糟,是中国传统发酵食品的代表之一,具有悠久的历史和深厚的文化底蕴。近年来,随着消费者对健康食品需求的增长和传统饮食文化的复兴…

2025年优秀的酚醛胶厂家最新TOP排行榜

2025年优秀的酚醛胶厂家TOP排行榜 行业背景与市场趋势 酚醛胶作为一种高性能粘合剂,广泛应用于木材加工、建筑、汽车制造、电子封装等领域。随着环保要求的提高和工业技术的进步,市场对酚醛胶的性能、环保性及稳定…

2025年热门的大口径缠绕管设备/pph缠绕管设备热门厂家推荐榜单

2025年热门的大口径缠绕管设备/PPH缠绕管设备热门厂家推荐榜单行业背景与市场趋势随着城市化进程加速和环保要求日益严格,大口径塑料缠绕管在市政排水、工业排污、农业灌溉等领域的应用越来越广泛。2025年,预计全球塑…

AI智能媒体助理,一款为自媒体人提供的神器测评报告

说到AI大家都懂,说到自媒体大家也懂,那么把AI和自媒体结合到一起会是怎么样的? 我觉得应该是更便利,更方便,原来我们在写自媒体文章的时候也会用到AI来辅助一下,帮助我们润色、修饰、给初稿、给大纲,最后发现该…

测评系统开发公司TOP5权威推荐:助力企业高效获客与管理升级

数字化时代,测评系统已成为企业获客引流、用户分层的核心工具,但市场上服务商质量参差不齐,企业常面临功能不符需求、数据安全无保障、落地效果差等痛点。据2024年行业调研,超35%企业因选错测评系统定制服务商导致…

2025防伪公司TOP5权威推荐:深度测评防伪产品供应商

在假冒伪劣商品年造成超2000亿元市场损失的背景下,企业对专业防伪溯源服务的需求激增。2024年数据显示,防伪技术市场规模突破350亿元,年增速达32%,但41%的企业反馈现有方案存在易仿制数据不可信功能单一三大痛点—…

11.11总结

1.今天完成了修改页面信息的部分 2.明天完成查看信息

2025年评价高的燃气壁挂炉厂家推荐及采购参考

2025年评价高的燃气壁挂炉厂家推荐及采购参考行业背景与市场趋势随着我国"双碳"目标的持续推进和居民生活水平的不断提高,燃气壁挂炉作为高效、环保的供暖设备,近年来市场需求持续增长。据中国家用电器协会…

03-几何建模基础

第三章:几何建模基础 3.1 OpenCascade内核介绍 3.1.1 什么是OpenCascade OpenCascade Technology(简称OCCT或OCC)是一个开源的3D CAD/CAM/CAE内核,由法国Matra Datavision公司最初开发,后来开源并由OpenCascade公…

01-概述与入门

第一章:Chili3D概述与入门 1.1 什么是Chili3D Chili3D是一款基于Web浏览器的开源3D CAD(计算机辅助设计)应用程序,专为在线模型设计和编辑而构建。该项目由开发者"仙阁"创建,采用TypeScript编写,通过将…

08-性能优化与高级配置

第8章 性能优化与高级配置 8.1 性能监控与分析 8.1.1 性能监控的重要性 在生产环境中,性能监控是确保GeoServer稳定运行的关键。通过监控可以:及时发现性能瓶颈 预测容量需求 优化资源配置 诊断问题根源8.1.2 GeoSer…

07-REST-API与自动化

第7章 REST API与自动化 7.1 REST API概述 7.1.1 什么是REST API GeoServer提供了完整的REST API(Representational State Transfer),允许通过HTTP请求对GeoServer进行程序化管理。REST API是实现自动化部署、批量操…