znlgis 博客

GIS开发与技术分享

第4章:枚举类型与常量定义

4.1 概述

Clipper1 定义了多个枚举类型和常量,用于控制裁剪行为、指定填充规则、设置偏移选项等。理解这些定义对于正确使用 Clipper 至关重要。

4.2 ClipType 枚举

ClipType 定义了四种布尔运算类型:

public enum ClipType { 
    ctIntersection,  // 交集
    ctUnion,         // 并集
    ctDifference,    // 差集
    ctXor            // 异或
}

4.2.1 Intersection(交集)

  Subject      Clip        Result
  ┌───┐       ┌───┐       
  │ S │       │ C │         ┌─┐
  │ ┌─┼───┐   │   │    =    │█│
  └─┼─┘   │   └───┘         └─┘
    └─────┘

特点:返回两个多边形重叠的区域。

应用场景

  • 计算两个区域的公共部分
  • 遮罩/裁剪效果
  • 碰撞检测中的重叠区域

4.2.2 Union(并集)

  Subject      Clip        Result
  ┌───┐       ┌───┐       ┌───────┐
  │ S │       │ C │       │███████│
  │ ┌─┼───┐   │   │    =  │███████│
  └─┼─┘   │   └───┘       └───────┘
    └─────┘

特点:返回两个多边形覆盖的所有区域。

应用场景

  • 合并相邻区域
  • 消除内部边界
  • 构建复合形状

4.2.3 Difference(差集)

  Subject      Clip        Result
  ┌───┐       ┌───┐       ┌──┐
  │ S │       │ C │       │██│
  │ ┌─┼───┐   │   │    =  │██│
  └─┼─┘   │   └───┘       └──┘
    └─────┘

特点:返回 Subject 中不与 Clip 重叠的部分。

应用场景

  • 从形状中挖孔
  • 计算边界区域
  • 布尔减法运算

4.2.4 Xor(异或)

  Subject      Clip        Result
  ┌───┐       ┌───┐       ┌──┬──┐
  │ S │       │ C │       │██│  │
  │ ┌─┼───┐   │   │    =  │  └──┤
  └─┼─┘   │   └───┘       └────█│
    └─────┘                     │
                                └┘

特点:返回两个多边形不重叠的部分(对称差)。

应用场景

  • 检测变化区域
  • 高亮差异
  • 边界效果

4.3 PolyType 枚举

PolyType 指定多边形在布尔运算中的角色:

public enum PolyType { 
    ptSubject,  // 主体多边形
    ptClip      // 裁剪多边形
}

4.3.1 使用方式

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);

4.3.2 对布尔运算的影响

运算类型 Subject Clip 结果
Intersection Subject ∩ Clip
Union Subject ∪ Clip
Difference Subject - Clip
Xor Subject ⊕ Clip

注意:Difference 运算是不对称的,Subject - Clip ≠ Clip - Subject。

4.4 PolyFillType 枚举

PolyFillType 定义多边形填充规则,决定哪些区域被视为”内部”:

public enum PolyFillType { 
    pftEvenOdd,   // 奇偶规则
    pftNonZero,   // 非零规则
    pftPositive,  // 正向规则
    pftNegative   // 负向规则
}

4.4.1 EvenOdd(奇偶规则)

从点发射射线,计数穿过的边数:
- 奇数:内部
- 偶数:外部

    ┌─────────────┐
    │  1(内)      │
┌───┼───┐         │
│ 2 │ 1 │         │
│(外)│(内)        │
└───┴───┴─────────┘

特点

  • 最常用的规则
  • 自动处理自相交多边形
  • 交替填充重叠区域

4.4.2 NonZero(非零规则)

从点发射射线:
- 向上穿过:winding_number++
- 向下穿过:winding_number--
- winding_number ≠ 0:内部
- winding_number = 0:外部

    →→→→→→→→→→→→→→
    ┌─────────────┐
    │ winding=1   │
    │    (内)     │
    ↓   ┌───┐     ↑
    │   │w=2│     │
    │   │(内)     │
    └───┴───┴─────┘

特点

  • 考虑边的方向
  • 同向重叠区域保持填充
  • 适合路径描边

4.4.3 Positive(正向规则)

// 只有 winding_number > 0 的区域被填充

特点

  • 只填充正向缠绕的区域
  • 忽略负向边的贡献

4.4.4 Negative(负向规则)

// 只有 winding_number < 0 的区域被填充

特点

  • 只填充负向缠绕的区域
  • 忽略正向边的贡献

4.4.5 填充规则对比

// 自相交多边形(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);

4.5 JoinType 枚举

JoinType 用于 ClipperOffset,指定偏移时拐角的连接方式:

public enum JoinType { 
    jtSquare,  // 方形连接
    jtRound,   // 圆形连接
    jtMiter    // 斜接连接
}

4.5.1 Square(方形连接)

原始角:      偏移后:
    ╱          ┌───┐
   ╱           │   │
  ╱            │   │

特点

  • 在拐角处创建 45° 斜切
  • 产生明确的边界
  • 适合工程应用

4.5.2 Round(圆形连接)

原始角:      偏移后:
    ╱          ╭───╮
   ╱           │   │
  ╱            │   │

特点

  • 在拐角处创建圆弧
  • 视觉效果平滑
  • 适合图形设计

4.5.3 Miter(斜接连接)

原始角:      偏移后:
    ╱             ╱
   ╱           ╱
  ╱          ╱
           ╱

特点

  • 延伸边线直到相交
  • 锐角可能产生很长的尖角
  • 使用 MiterLimit 控制最大延伸

4.6 EndType 枚举

EndType 用于 ClipperOffset,指定开放路径端点的处理方式:

public enum EndType { 
    etClosedPolygon,  // 闭合多边形
    etClosedLine,     // 闭合线
    etOpenButt,       // 开放-平头
    etOpenSquare,     // 开放-方头
    etOpenRound       // 开放-圆头
}

4.6.1 ClosedPolygon

// 用于闭合多边形,首尾自动连接
// 偏移会产生内外两个轮廓(或只有外轮廓)

4.6.2 ClosedLine

// 用于闭合线(首尾连接但可能自相交)
// 偏移两侧但不在端点处闭合

4.6.3 OpenButt(平头)

原始线:        偏移后:
───────        ┌───────┐
               └───────┘

4.6.4 OpenSquare(方头)

原始线:        偏移后:
───────       ┌─────────┐
              └─────────┘
              (延伸 delta 距离)

4.6.5 OpenRound(圆头)

原始线:        偏移后:
───────       ╭─────────╮
              ╰─────────╯
              (圆弧端点)

4.7 内部枚举类型

4.7.1 EdgeSide

internal enum EdgeSide { esLeft, esRight }

指定边相对于输出多边形的位置(左侧或右侧)。

4.7.2 Direction

internal enum Direction { dRightToLeft, dLeftToRight }

指定边的扫描方向,用于水平边处理。

4.8 常量定义

4.8.1 ClipperBase 常量

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 高精度模式的坐标范围

4.8.2 Clipper 常量

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 保留共线顶点

4.8.3 使用 InitOptions

// 默认选项
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;

4.8.4 ClipperOffset 常量

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 完整圆的弧度
def_arc_tolerance 0.25 默认弧度容差
ArcTolerance 0.25 圆弧近似的容差
MiterLimit 2.0 斜接限制

4.9 横向边斜率常量

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);
}

4.10 坐标范围常量详解

4.10.1 32位模式

#if use_int32
    public const cInt loRange = 0x7FFF;     // 32,767
    public const cInt hiRange = 0x7FFF;     // 32,767
#endif

在 32 位模式下,坐标范围被严格限制,因为中间计算可能产生溢出。

4.10.2 64位模式

#else
    public const cInt loRange = 0x3FFFFFFF;           // 约 10⁹
    public const cInt hiRange = 0x3FFFFFFFFFFFFFFFL; // 约 4.6×10¹⁸
#endif

两个范围的意义

  • loRange:低精度范围,中间计算使用普通 64 位整数
  • hiRange:高精度范围,中间计算使用 128 位整数(Int128)

当坐标超过 loRange 时,Clipper 自动切换到 128 位计算模式,以避免溢出。

4.11 本章小结

本章详细介绍了 Clipper1 中的枚举类型和常量:

  1. 布尔运算类型(ClipType)
    • 交集、并集、差集、异或
  2. 多边形类型(PolyType)
    • 主体和裁剪多边形的区分
  3. 填充规则(PolyFillType)
    • 奇偶、非零、正向、负向规则
  4. 偏移选项
    • JoinType:方形、圆形、斜接连接
    • EndType:各种端点处理方式
  5. 重要常量
    • 坐标范围限制
    • 初始化选项
    • 特殊标记值

理解这些枚举和常量是正确配置 Clipper 的基础,不同的选项组合可以实现各种复杂的几何运算需求。


上一章:路径与多边形表示 返回目录 下一章:Int128高精度运算