






















Clipper1 定义了多个枚举类型和常量,用于控制裁剪行为、指定填充规则、设置偏移选项等。理解这些定义对于正确使用 Clipper 至关重要。
ClipType 定义了四种布尔运算类型:
public enum ClipType {
ctIntersection, // 交集
ctUnion, // 并集
ctDifference, // 差集
ctXor // 异或
}
Subject Clip Result
┌───┐ ┌───┐
│ S │ │ C │ ┌─┐
│ ┌─┼───┐ │ │ = │█│
└─┼─┘ │ └───┘ └─┘
└─────┘
特点:返回两个多边形重叠的区域。
应用场景:
Subject Clip Result
┌───┐ ┌───┐ ┌───────┐
│ S │ │ C │ │███████│
│ ┌─┼───┐ │ │ = │███████│
└─┼─┘ │ └───┘ └───────┘
└─────┘
特点:返回两个多边形覆盖的所有区域。
应用场景:
Subject Clip Result
┌───┐ ┌───┐ ┌──┐
│ S │ │ C │ │██│
│ ┌─┼───┐ │ │ = │██│
└─┼─┘ │ └───┘ └──┘
└─────┘
特点:返回 Subject 中不与 Clip 重叠的部分。
应用场景:
Subject Clip Result
┌───┐ ┌───┐ ┌──┬──┐
│ S │ │ C │ │██│ │
│ ┌─┼───┐ │ │ = │ └──┤
└─┼─┘ │ └───┘ └────█│
└─────┘ │
└┘
特点:返回两个多边形不重叠的部分(对称差)。
应用场景:
PolyType 指定多边形在布尔运算中的角色:
public enum PolyType {
ptSubject, // 主体多边形
ptClip // 裁剪多边形
}
Clipper clipper = new Clipper();
// 添加主体多边形
clipper.AddPath(subjectPath, PolyType.ptSubject, true);
clipper.AddPaths(subjectPaths, PolyType.ptSubject, true);
// 添加裁剪多边形
clipper.AddPath(clipPath, PolyType.ptClip, true);
clipper.AddPaths(clipPaths, PolyType.ptClip, true);
| 运算类型 | Subject | Clip | 结果 |
|---|---|---|---|
| Intersection | ✓ | ✓ | Subject ∩ Clip |
| Union | ✓ | ✓ | Subject ∪ Clip |
| Difference | ✓ | ✓ | Subject - Clip |
| Xor | ✓ | ✓ | Subject ⊕ Clip |
注意:Difference 运算是不对称的,Subject - Clip ≠ Clip - Subject。
PolyFillType 定义多边形填充规则,决定哪些区域被视为"内部":
public enum PolyFillType {
pftEvenOdd, // 奇偶规则
pftNonZero, // 非零规则
pftPositive, // 正向规则
pftNegative // 负向规则
}
从点发射射线,计数穿过的边数:
- 奇数:内部
- 偶数:外部
┌─────────────┐
│ 1(内) │
┌───┼───┐ │
│ 2 │ 1 │ │
│(外)│(内) │
└───┴───┴─────────┘
特点:
从点发射射线:
- 向上穿过:winding_number++
- 向下穿过:winding_number--
- winding_number ≠ 0:内部
- winding_number = 0:外部
→→→→→→→→→→→→→→
┌─────────────┐
│ winding=1 │
│ (内) │
↓ ┌───┐ ↑
│ │w=2│ │
│ │(内) │
└───┴───┴─────┘
特点:
// 只有 winding_number > 0 的区域被填充
特点:
// 只有 winding_number < 0 的区域被填充
特点:
// 自相交多边形(8字形)
Path figure8 = new Path();
figure8.Add(new IntPoint(0, 0));
figure8.Add(new IntPoint(100, 100));
figure8.Add(new IntPoint(100, 0));
figure8.Add(new IntPoint(0, 100));
// 使用不同填充规则
Paths resultEvenOdd = new Paths();
Paths resultNonZero = new Paths();
clipper.AddPath(figure8, PolyType.ptSubject, true);
clipper.Execute(ClipType.ctUnion, resultEvenOdd,
PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
clipper.Clear();
clipper.AddPath(figure8, PolyType.ptSubject, true);
clipper.Execute(ClipType.ctUnion, resultNonZero,
PolyFillType.pftNonZero, PolyFillType.pftNonZero);
JoinType 用于 ClipperOffset,指定偏移时拐角的连接方式:
public enum JoinType {
jtSquare, // 方形连接
jtRound, // 圆形连接
jtMiter // 斜接连接
}
原始角: 偏移后:
╱ ┌───┐
╱ │ │
╱ │ │
特点:
原始角: 偏移后:
╱ ╭───╮
╱ │ │
╱ │ │
特点:
原始角: 偏移后:
╱ ╱
╱ ╱
╱ ╱
╱
特点:
EndType 用于 ClipperOffset,指定开放路径端点的处理方式:
public enum EndType {
etClosedPolygon, // 闭合多边形
etClosedLine, // 闭合线
etOpenButt, // 开放-平头
etOpenSquare, // 开放-方头
etOpenRound // 开放-圆头
}
// 用于闭合多边形,首尾自动连接
// 偏移会产生内外两个轮廓(或只有外轮廓)
// 用于闭合线(首尾连接但可能自相交)
// 偏移两侧但不在端点处闭合
原始线: 偏移后:
─────── ┌───────┐
└───────┘
原始线: 偏移后:
─────── ┌─────────┐
└─────────┘
(延伸 delta 距离)
原始线: 偏移后:
─────── ╭─────────╮
╰─────────╯
(圆弧端点)
internal enum EdgeSide { esLeft, esRight }
指定边相对于输出多边形的位置(左侧或右侧)。
internal enum Direction { dRightToLeft, dLeftToRight }
指定边的扫描方向,用于水平边处理。
public class ClipperBase
{
internal const double horizontal = -3.4E+38;
internal const int Skip = -2;
internal const int Unassigned = -1;
internal const double tolerance = 1.0E-20;
internal static bool near_zero(double val)
{
return (val > -tolerance) && (val < tolerance);
}
#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
}
| 常量 | 值 | 说明 |
|---|---|---|
horizontal |
-3.4E+38 | 标识水平边的特殊斜率值 |
Skip |
-2 | 标记需要跳过的边 |
Unassigned |
-1 | 表示未分配的输出索引 |
tolerance |
1.0E-20 | 浮点数零判断的容差 |
loRange |
0x3FFFFFFF | 低精度模式的坐标范围 |
hiRange |
0x3FFFFFFFFFFFFFFFL | 高精度模式的坐标范围 |
public class Clipper : ClipperBase
{
// InitOptions 可以传递给构造函数
public const int ioReverseSolution = 1;
public const int ioStrictlySimple = 2;
public const int ioPreserveCollinear = 4;
}
| 常量 | 值 | 说明 |
|---|---|---|
ioReverseSolution |
1 | 反转结果多边形的方向 |
ioStrictlySimple |
2 | 确保结果是严格简单的多边形 |
ioPreserveCollinear |
4 | 保留共线顶点 |
// 默认选项
Clipper clipper1 = new Clipper();
// 反转结果方向
Clipper clipper2 = new Clipper(Clipper.ioReverseSolution);
// 严格简单多边形
Clipper clipper3 = new Clipper(Clipper.ioStrictlySimple);
// 组合选项(使用位或)
Clipper clipper4 = new Clipper(
Clipper.ioReverseSolution | Clipper.ioStrictlySimple
);
// 也可以通过属性设置
clipper1.ReverseSolution = true;
clipper1.StrictlySimple = true;
clipper1.PreserveCollinear = true;
public class ClipperOffset
{
private const double two_pi = Math.PI * 2;
private const double def_arc_tolerance = 0.25;
public double ArcTolerance { get; set; }
public double MiterLimit { get; set; }
}
| 常量/属性 | 默认值 | 说明 |
|---|---|---|
two_pi |
2π | 完整圆的弧度 |
def_arc_tolerance |
0.25 | 默认弧度容差 |
ArcTolerance |
0.25 | 圆弧近似的容差 |
MiterLimit |
2.0 | 斜接限制 |
internal const double horizontal = -3.4E+38;
这个特殊值用于标识水平边。在 Clipper 的坐标系中(Y 轴向上),水平边的斜率定义为无穷大或极大值。使用 -3.4E+38 而非正无穷,是为了便于比较和特殊处理。
使用示例:
internal static bool IsHorizontal(TEdge e)
{
return e.Delta.Y == 0;
}
private void SetDx(TEdge e)
{
e.Delta.X = (e.Top.X - e.Bot.X);
e.Delta.Y = (e.Top.Y - e.Bot.Y);
if (e.Delta.Y == 0)
e.Dx = horizontal;
else
e.Dx = (double)(e.Delta.X) / (e.Delta.Y);
}
#if use_int32
public const cInt loRange = 0x7FFF; // 32,767
public const cInt hiRange = 0x7FFF; // 32,767
#endif
在 32 位模式下,坐标范围被严格限制,因为中间计算可能产生溢出。
#else
public const cInt loRange = 0x3FFFFFFF; // 约 10⁹
public const cInt hiRange = 0x3FFFFFFFFFFFFFFFL; // 约 4.6×10¹⁸
#endif
两个范围的意义:
当坐标超过 loRange 时,Clipper 自动切换到 128 位计算模式,以避免溢出。
本章详细介绍了 Clipper1 中的枚举类型和常量:
布尔运算类型(ClipType):
多边形类型(PolyType):
填充规则(PolyFillType):
偏移选项:
重要常量:
理解这些枚举和常量是正确配置 Clipper 的基础,不同的选项组合可以实现各种复杂的几何运算需求。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。