前言

Clipper库是目前计算机图形届广为使用的图形处理库,可以用于解决平面二维图形的多边形简化、布尔运算和偏置处理,在CAD、加工路径与3D打印方面都有着比较重要的应用。

本文使用Love2.io驱动。

本文源文件来自Clipper库,英文版文档参见ClipperLib Overview。

下载地址:https://download.csdn.net/download/hanfeidyx/11268643

概述

简介

Clipper Library(以下简称为Clipper库或者ClipperLib或Clipper)提供了对线段和多边形的裁剪(clipping)以及偏置(offseting)功能。

和其他裁剪库相比,Clipper具有以下特征:

  1. 它能够接受各类多边形输入,包含自交的多边形;
  2. 它支持多种填充原则(奇偶填充、非零填充、正填充、负填充);
  3. 它相较于其他库来说效率极高;
  4. 它数值稳定(鲁棒性强);
  5. 它支持多边形和线段的偏置;
  6. 它对于商用以及免费软件来说都是免费使用的。

术语:

  1. 裁剪(Clipping):通常是指在二维平面上把一个图形在指定的矩形框以外的部分去除掉。在更广义的角度,指定的裁剪范围不一定要是一个矩形,可以是各种各样的多边形,甚至是多个多边形;同样的,我们一般裁剪指的是形态学上的“求交”,但是在Clipper库中裁剪可以实现4种布尔运算(求交、求和、求异、求异或);
  2. 路径(Path):是指一些列有序的点的集合,用来定义一个轮廓(contour),这个轮廓既可以是代指一条线/开放路径(line/open path),也可以代指一个多边形/闭合轮廓(polygon/closed path)。
  3. 线(Line):与Polyline同义,代指一个具有两个以上点的开放路径。
  4. 轮廓(Contour):与路径同义;
  5. 孔洞/内轮廓(Hole):代指一个多边形内部表示该部分不属于该多边形的闭合区域;一个“内轮廓(Hole Polygon)”就是一个代指孔的外围点集的封闭路径;
  6. 孔洞/内轮廓(Hole):代指一个多边形内部表示该部分不属于该多边形的闭合区域;一个“内轮廓(Hole Polygon)”就是一个代指孔的外围点集的封闭路径;
  7. 多边形填充规则(Polygon Filling Rule):即在一系列的闭合路径中,来定义哪些属于“内部”,哪些属于“外部”

参考内容:

The Library is based on but significantly extends Bala Vatti’s polygon clipping algorithm as described in “A generic solution to polygon clipping”, Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63.

A section in “Computer graphics and geometric modeling: implementation and algorithms” by By Max K. Agoston (Springer, 2005) discussing Vatti Polygon Clipping was also helpful in creating the initial Clipper implementation.

The paper titled “Polygon Offsetting by Computing Winding Numbers” by Chen & McMains (Paper no. DETC2005-85513, ASME 2005. Pages 565-575) contains helpful discussion on the complexities of polygon offsetting together with some solutions.

结构

预定义宏(Defines)

编译器预定义以下类型的宏:

  1. use_int32:当启用32位精度,效率提升,但是使用范围缩小;
  2. use_xyz:添加一个Z位数据,对性能影响很小;
  3. use_lines:启用开放路径的裁剪;如果该功能关闭(默认开启),大约有一个小于5%的性能的提升;
  4. use_deprecated:确保代码与6.0版本以前的内容相兼容;该功能未来会删除;

取整(Rounding)

通过使用一个整形数据,Clipper库已经能够避免因为数值稳定性造成的重大错误;关于数值取整问题和它们可能的解决办法将要在下面进行讨论;

首先需要强调,取整导致点会对他们的本来的理论坐标形成一定程度的偏移;但是,结果的不精确性是可以通过正确的缩放来进行避免的;

Clipper库自身支持可以缩放到一个相对较高精度,它所支持的整形坐标值范围在±0x3FFFFFFFFFFFFFFF (± 4.6e+18)之间。

另一个使用离散数据(相较于使用浮动类型数据)的隐患在于可能在极少数的情况下会造成小的自交情况。在没有缩放的左侧图片中(这里单位为1像素),两个多边形的相交部分被用亮绿色标出;

一个30倍放大的交点部分下部图片显示图该图其实有很小程度的自交情况。三个黑点标明了实际的交点情况(通过展示它们小数点部分);红色点显示在取整之后交点的情况。你很容易就会观察到取整让方向变反并且引起了一定程度的小的自交;

尽管这些小的自交是不常见的,如果这些被认为是有必要被考虑的,最好使用CleanPolygon的属性来对这种情况进行清除;(将Clipper对象的StrictlySimple属性设定为true同样会对这类自交产生影响但是小的多余的人为引起的错误方向的多边形仍然是不可避免的)

在最后的例子中,左侧的简单多边形仍然有轻微的自交。但是,在裁剪算法中处理时,算法将其当做仅仅是“触碰到”,而不是自交到右侧线段上去(虽然仅仅只有小于1个像素的部分);既然这样的自交一般不会被检测到,裁剪算法(例如求并运算)仍然会包含这样的小的自交。将Clipper库的StrictlySimple属性设定为true可以避免这一类情况;

数据类型

cInt

Del.»

{$IFDEF use_int32}

cInt = Int32;

{$ELSE}

cInt = Int64;

{$ENDIF}

C++ »

#ifdef use_int32

typedef int cInt;

#else

typedef signed long long cInt;

#endif

C#  »

#if use_int32

using cInt = Int32;

#else

using cInt = Int64;

#endif

cInt是Clipper库用来表示点坐标数据使用的。目前Clipper库使用整形数据代替浮点数据来保证数据的鲁棒性。(在一开始的版本中曾经使用Clipper库中的浮点数据,但是和明显浮点数的不精确性引起了一些偶然性错误);

默认的cInt代表了一个有符号64位整形数据并且整个多边形的坐标范围可以达到±9.2e+18的范围内。这容纳了整个浮点数坐标可以使其精度得到最完整的保留。但是,如果坐标范围可以被控制在 ± 3.0e+9之内,那么通过避免了大数的运算,可以得到大约百分之十的效率提升。如果预编译器定义了use_int32,则效率可以更高,详情见下方。

IntPoint

Del.» TIntPoint = record X, Y: cInt; end;

C++ » struct IntPoint { cInt X; cInt Y; ... };

C#  » public class IntPoint { public cInt X; { get; set; } public cInt Y; { get; set; } ... };

整形点数据结构用来代表Clipper库中相关的所有点;Clipper库刻意选择了整形存储类型类保证数值稳定。(同上文)

一系列的IntPoint被保存在Path结构中,构成了一个轮廓。

在版本6.0以上,IntPoint现在可以拥有第三个成员“Z”。这可以通过在预编译器中定义“use_xyz”来实现。当Z轴数据也被加入时,它的值也会被考虑进裁剪的行为中去;但是,在那些没有对应的Z值的交点处,该值会被设定为0,除非用户提供了回调函数;

用户如果希望使用浮点数进行Clip,那么必须手动的进行正确的对浮点数进行缩放为IntPoint,再使用Clipper数进行处理;

Path

这个结构包含一系列的整形数据点来定义一个单独的轮廓(可以参考术语部分);路径由两个以上的点数据组成,并且可以是开放的,当它为闭合的时候也可以代表多边形(Polygon);路径的开放与否是由其内容决定的。封闭的路径可能是“外轮廓”或者“内轮廓(内孔)”,这主要是由其方向(顺时针或者逆时针)决定的。

多个路径可以添加入Paths结构体形成一组路径;

Paths

由多个Path组成的基本类型;

可以包含开轮廓或闭合轮廓;

InitOptions

Del.»

Del.» type TInitOption = (ioReverseSolution, ioStrictlySimple, ioPreserveCollinear);

C++ » enum InitOptions {

ioReverseSolution  = 1,

ioStrictlySimple   = 2,

ioPreserveCollinear = 4};

C#  » public const int ioReverseSolution  = 1;

public const int ioStrictlySimple   = 2;

public const int ioPreserveCollinear = 4;

IntRect

Del.»

TIntRect = record left, top, right, bottom: cInt; end;

C++ »

struct IntRect { cInt left; cInt top; cInt right; cInt bottom; ... };

C#  »

public class IntRect {

public cInt left; { get; set; }

public cInt top; { get; set; }

public cInt right; { get; set; }

public cInt bottom; { get; set; } ... };

用于接受Clipper库的GetBounds()函数的结果(包围盒);

ClipType

Del.» type TClipType = (ctIntersection, ctUnion, ctDifference, ctXor);

C++ » enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };

C#  » public enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };

总共有四种裁剪运算类型 AND,OR,NOT和XOR

他们的类型主要取决于他们的点集信息和填充规则,规则如下:

  1. AND(Intersection求交) 获取两者相交的部分;
  2. OR(Union求并) 获取两者并集部分;
  3. NOT(difference求异) 获取Clip区域以外的区域;
  4. XOR(exclusive求异或) 获取两个区域互相不重复的区域;

所有的多边形裁剪都是通过一个Clipper对象通过传递一个具体的布尔运算类型给它的Execute方法来实现的;

考虑到非封闭线段(多段线),裁剪的原则基本上和封闭图形是相同的;但是,如果一个裁剪案例中出现了以下几种基本情况,会采取以下的裁剪原则:

并集:多段线将会被任何重叠的多边形裁剪,导致没有被覆盖的部分会联通重叠多边形一同返回;

交集、非运算和异或运算:多段线只会被Clip的多边形切割,并且在多段线原多边形之间不会有任何交点;

下图中是最终的例子:

PolyType

多边形的布尔运算类型主要来设置两组多边形的属性,来分别代表被裁剪(subject)和裁剪(clip)的多边形。无论何时路径组(Paths)被添加到Clipper对象当中,他们都应该被指定类型到底是subject还是clip。

求和运算(UNION)可以在单个路径组或者两个路径组当中,但是所有其他布尔运算需要设定两个路径组的类型类获取有意义的结果。

PolyFillType

FIlling(填充)代指存在一个封闭路径内的区域。Clipper主要支持4种情况:Even-Odd, Non-Zero, Positive以及Negative;

最简单的填充策略属于Even-Odd(有时候称作为可选择性填充Alternate filling)。给定了一组封闭路径,从该组路径的外侧的一个点出发,通过一根假想的线,当第1个被交到的路径将会被填充。当下一个路径相交时,则不被填充。同样的,每次一个路径是被环绕的,都会交叠式的被填充;

和Even-odd填充方式不同,其他填充准则都是基于 边缘方向 和 环绕次数。轮廓的方向是由点集的顺序确定的,同时轮廓的方向也是用来决定环绕次数的依据;

环绕次数的获取方法:

  1. 从0开始计数;
  2. 从整个轮廓组外一点p1,直到需要确认填充与否的区域内一点p2;
  3. 当遍历p1到p2的每一个经过的点时,对于每一个穿越了这条假想线段如果是由右向左穿越,则穿越数加一;如果是从右向左穿越,则减一;
  4. 得到最终的穿越数,即环绕次数;
  1. Even-Odd:奇数次环绕的部分被填充,偶数次的部分不填充;
  2. Non_Zero:所有的非零环绕次数部分都被填充;
  3. Positive:所有环绕次数大于零的部分被填充;
  4. Negative:所有环绕次数小于零的部分被填充;

多边形是由一系列自交或者不自交的封闭线段构成;一个单独多边形区域(单连通域)可以定义为一个无自交的路径,常常由一个外轮廓和多个内轮廓(也可以没有内轮廓)构成。由上图,中间的图形由两个同轴的矩形,它们都是顺时针方向。根据奇偶填充规则,即方向问题可以基本被忽略,那么内矩形将在外矩形内部创造一个孔。但是,对于就非零填充原则来说,这个孔肯定没有。在最右侧的例子中,当内矩形和外矩形的方向不一致,则不论是非零填充还是奇偶填充,他们都会创造一个孔(这可以看出轮廓方向的规范性可以在不同渲染规则下获得相同正确的效果,这一点是很重要的)。

目前最广泛使用的填充规则莫过于Even-Odd和None-Zero填充方法;

有一条定律是轮廓的方向不会影响最终计算出环绕次数奇偶性(这也是为什么在Even-Odd中方向性不需要确定的原因);

Y轴的方向的确会影响多边形的顺逆时针性。但是,改变Y轴的方向只会改变环绕次数的正负号,包括量级和各类填充方法的结果,是不会改变的。

JoinType

Del.» type TJoinType = (jtSquare, jtRound, jtMiter);

C++ » enum JoinType {jtSquare, jtRound, jtMiter};

C#  » public enum JoinType {jtSquare, jtRound, jtMiter};

当在ClipperOffset对象中通过AddPaths函数添加路径时,相交类型参数可从jtMiter,jtSquare和jtRound中选一个;

jtMiter:对于斜接式交点来说,有必要设定一个阈值,因为偏置的轮廓在极其窄的相交点处可能会造成过“尖角”。为了将这些潜在的尖角包含在内,ClipperOffset对象的MiterLimit属性用来制定一个偏置点所能容忍的最大值;对于所有给定的边缘交点处,一旦斜接式交点超过了该阈值,那么方形交角将会被应用;

jtRound:当扁平的路径始终无法完美的获取角度信息,他们等价于一系列的圆弧曲线(可以查阅ClipperObject的ArcTolerance属性)

jtSquare:相当于斜接式当中的delta值始终为1的情况;

EndType

Del.» type TClipType = (ctIntersection, ctUnion, ctDifference, ctXor);

C++ » enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };

C#  » public enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };

关于EndType(结束类型)总共有五种值:

etClosedPolygon:末点是相交的,并且使用了JoinType来使路径视作一个多边形进行填充;

etClosedLine:末点是相交的,并且使用了JoinType来使路径视作一条线进行填充;

etOpenSqure:末点使用方形尾和扩展的delta角度;

etOpenRound:末点使用圆形尾和扩展的delta角度;

etOpenButt:末点使用了方向尾,并没有任何扩展;

etOpenSingle:对一个开放式的多段线进行了偏置,等待未来更新;

注意:在etClosedPolygon和etClosedLine类型中,无论首末点是否重合,路径结束的情况将会相同。(主要针对部分图形格式,规定首末点相同,这里Clipper统一采用了非首末点相同的情况);

ZFillCallback

Del.» type TZFillCallback = procedure (const E1Bot, E1Top, E2Bot, E2Top: TIntPoint; var Pt: TIntPoint);

C++ » typedef void (*ZFillCallback)(const IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);

C#  » public delegate void ZFillCallback(IntPoint bot1, IntPoint top1, IntPoint bot2, IntPoint top2, ref IntPoint pt);

如果启用了use_xyz预处理器指令,则IntPoint类将具有额外的“Z”成员,并且将公开Clipper类的ZFillFunction属性,以便为其分配自定义回调函数。

此自定义回调过程需要五个IntPoint参数:前两个参数是定义交点中涉及的一个线段的顶点,后两个参数是另一个线段。 (由于Clipper库是在使用反转Y轴显示的环境中开发的,因此e1bot和e2bot的Y值始终大于或等于其对应的e1top和e2top Y值。)最后一个IntPoint参数包含实际交点坐标。 最后一个参数通过引用传递,以便可以为其Z成员分配自定义值。

ClipperBase

ClipperBase是Clipper的一个抽象基类,不应当直接单独实例化使用一个ClipperBase对象;

函数

  1. ClipperBase.AddPath

Del.» function AddPath(const path: TPath; polyType: TPolyType; Closed: Boolean): Boolean;

C++ » bool AddPath(const Path &pg, PolyType polyType, bool closed);

C#  » public virtual bool AddPath(Path pg, PolyType polyType, bool closed);

任何数量的实体路径和剪切路径都可以被添加到一个AddPath()方法中去,或者通过AddPaths()来实现添加到一个组当中去,或者两者混用;

实体路径可以是开线段集或者闭合线段;但是裁剪线段必须是闭合的.

在闭合的路径当中,方向问题应当结合filling rule来进行考虑,并且是通过Clipper库中的excecute()方法来传递的;

路径的坐标范围:类似前文中IntPoint中范围

返回值内容:如果输入的路径不正确,该函数会返回false;在以下情况路径会被视为不正确:

  1. 该路径少于两个点;
  2. 有两个点但是不是一个开放路径;
  3. 点集全部是共线的但是不是一个开放路径;
  1. ClipperBase.AddPaths

同AddPath

  1. ClipperBase.Clear

Del.» procedure Clear;

C++ » virtual void Clear();

C#  » public void Clear() {};

Clear()方法去除了Clipper对象中所有存在的被裁剪对象和裁剪对象,使得该Clipper对象可以重用。

  1. ClipperBase.GetBounds

Del.» function GetBounds: TIntRect;

C++ » IntRect GetBounds();

C#  » public IntRect GetBounds() {...};

该方法会返回Clipper对象中包含的所有多边形的最小包围矩形(该矩形长和高和坐标轴平行)。

Clipper

继承自:ClipperBase

Clipper类将布尔运算内容(包含交并否异或)进行封装,称之为多边形裁剪;

输入的多边形,不管是subject还是clip,都通过AddPath或者AddPaths方法传递给Clipper对象,最后使用Execute函数进行裁剪。多个布尔运算可以通过输入相同的多边形,然后反复执行execute函数来实现;

函数

  1. Clipper.Constructor

Del.» constructor TClipper.Create(InitOptions: TInitOptions = []);

C++ » Clipper::Clipper(int initOptions = 0) : ClipperBase();

C#  » public Clipper(initOptions = 0): base() {};

构建一个Clipper对象,可以使用InitOptions来进行初始化;

  1. Clipper.Execute

Del.»

function Execute(clipType: TClipType;

out solution: TPaths;

subjFillType: TPolyFillType = pftEvenOdd;

clipFillType: TPolyFillType = pftEvenOdd): boolean; overload;

function Execute(clipType: TClipType;

out solution: TPolyTree;

subjFillType: TPolyFillType = pftEvenOdd;

clipFillType: TPolyFillType = pftEvenOdd): boolean; overload;

C++ »

bool Execute(ClipType clipType,

Paths &solution,

PolyFillType subjFillType = pftEvenOdd,

PolyFillType clipFillType = pftEvenOdd);

bool Execute(ClipType clipType,

PolyTree &solution,

PolyFillType subjFillType = pftEvenOdd,

PolyFillType clipFillType = pftEvenOdd);

C#  »

public bool Execute(ClipType clipType,

Paths solution,

PolyFillType subjFillType,

PolyFillType clipFillType);

public bool Execute(ClipType clipType,

PolyTree solution,

PolyFillType subjFillType,

PolyFillType clipFillType);

一旦裁剪路径组和被裁剪路径组已经被设定(通过AddPath或者AddPaths方法),Execute方法可以执行布尔运算,具体运算类型由clipType来指定;

最终的solution参数可以是一个路径组(Path)或者一个多边形树(PolyTree)类型。因为路径组结构比多边形树结构简单,所以效率相对较高(大约10%);但是,PolyTree的信息结构可能提供给了用户更多的有用信息。首先,PolyTree结构保留了网状的多边形父子关系(例如外轮廓包含孔洞、孔洞包含内轮廓等)。相同的,只有PolyTree类型可以区别开轮廓和闭合轮廓,因为每一个PolyNode结构有IsOpen的属性(Path类型没有任何成员来告知它是否为开放的或者是闭合的)。正因为如此,当一个开放轮廓组被传递给一个Clipper对象,使用者必须使用一个PolyTree类型来接收solution的结果,否则会引起错误;

当在使用开放式路径进行裁剪时,Clipper库提供了两个有效的函数来快速的分离结果当中的开放路径部分和闭合轮廓组部分OpenPathsFromPolyTree和CLosedPathsFromPolyTree。同时Clipper也提供了PolyTreeToPaths来快速将PolyTree类型转换为Paths。

关于solution返回的路径组,有以下几点需要注意:

  1. 并没有固定的顺序;
  2. 不会出现叠加和自交的情况;
  3. 内孔的方向永远会和外孔相反;
  4. solution的填充类型可以认为是Odd-Even或者Non-Zero中的任意一种,即兼容这两种填充方式;
  5. 多边形该很低概率的情况下会分享一个共同的变(在版本6.0之后该情况变得非常少有);

被填充图形填充类型(subjFillType)和裁剪图形填充类型(clipFillType)决定了多边形的填充规则和相对规则;

Execute可以执行多次,不需要重置,即可以对一个多边形进行多次Execute的处理;

属性

  1. Clipper.PreserveCollinear

Del.» property PreserveCollinear: boolean; override;

C++ » void PreserveCollinear(bool value);

C#  » public bool PreserveCollinear { get {} set {} };

默认的,当输入的被裁剪和裁剪多边形的内容中三个或者更多的点是共线的,Clipper对象会在进行布尔运算之前对多余的共线点进行删除;当设定PreserveCollinear属性防止了这种行为,来允许共线的点的情况出现在结果当中;

  1. Clipper.StrictlySimple

Del.» property StrictlySimple: boolean; override;

C++ » void StrictlySimple(bool value);

C#  » public bool StrictlySimple { get {} set {} };

术语:

  1. 一个简单多边形是指没有自交现象的多边形;
  2. 一个弱简单多边形是指一个简单多边形包含重合点以及重合的线段;
  3. 一个强简单多边形是指一个简单多边形不包含重合点以及重合的线段;

对于点“重合”的定义是指如果多边形内的两个以上的点具有相同的坐标(并且在序列上来说不是相邻点,即相邻点除外)。一个边接触到另一条边的定义是指这条边的一个端点与接触到另一条边(相邻边除外)相接触,或者他们是共线或覆盖关系(包括相邻边)。

由Clipper库的裁剪操作返回的多边形都是简单多边形。当StrictlySimply属性被启用,返回的多边形将会是强简单多边形,否则会返回弱简单多边形。算法因为计算强简单多边形太大,导致该选项是默认关闭的;

注意:目前无法保证输出多边形是严格简单的因为“简单化”工作还在进行当中;

在上述图片中,两个例子显示出两个弱简单多边形被打断为两个强简单多边形(外围轮廓的方向是为了避免视觉上点集顺序的错误)

参考维基百科上的简单多边形词条;

  1. Clipper.ReverseSolution

Del.» property ReverseSolution: boolean; override;

C++ » void ReverseSolution(bool value);

C#  » public bool ReverseSolution { get {} set {} };

当这个属性被设定为true时,由execute方法返回的函数将会与其正常方向相反;

ClipperOffset

ClipperOffset类封装了偏置类的函数(膨胀/收缩),可对开放式轮廓和封闭轮廓进行处理,并支持使用不同的转接类型和终止类型;

(该类替代了原来的OffsetPath功能,提供了更大的灵活性)

偏置的先决条件:

闭合路径的方向必须是一致的,即所有外轮廓的方向相同,而所有内轮廓方向相反(除了None-Zero填充方法)。所有开轮廓必须和封闭外轮廓方向相同;

多边形一定不能自交;

限制:

在偏置时,小的人为物块可能会在多边形交叠的地方出现。为了防止这类情况的发生,可以对多边形组内容进行单独的偏置处理;

#include "clipper.hpp"

...

using namespace ClipperLib;

int main()

{

Path subj;

Paths solution;

subj <<

IntPoint(348,257) << IntPoint(364,148) << IntPoint(362,148) <<

IntPoint(326,241) << IntPoint(295,219) << IntPoint(258,88) <<

IntPoint(440,129) << IntPoint(370,196) << IntPoint(372,275);

ClipperOffset co;

co.AddPath(subj, jtRound, etClosedPolygon);

co.Execute(solution, -7.0);

//draw solution ...

DrawPolygons(solution, 0x4000FF00, 0xFF009900);

}

函数

  1. ClipperOffset.AddPath

Del.» procedure AddPath(const Path: TPath; JoinType: TJoinType; EndType: TEndType);

C++ » void AddPath(const Path& path, JoinType jointype, EndType endtype);

C#  » public void AddPath(Path path, JoinType jointype, EndType endtype);

向ClipperOffset对象添加一个路径用来准备偏置;

其中开放式路径只能被偏移一个正值;

  1. ClipperOffset.AddPaths

Del.» procedure AddPaths(const Paths: TPaths; JoinType: TJoinType; EndType: TEndType);

C++ » void AddPaths(const Paths& paths, JoinType jointype, EndType endtype);

C#  » public void AddPaths(Paths paths, JoinType jointype, EndType endtype);

同上页AddPath,为添加路径组;

  1. ClipperOffset.Constructor

Del.» constructor Create(MiterLimit: Double = 2; RoundPrecision: Double = 0.25);

C++ » ClipperOffset( double miterLimit = 2.0, double roundPrecision = 0.25);

C#  » public ClipperOffset( double miterLimit = 2.0, double roundPrecision = 0.25);

构造函数包含了两个可选的参数:MiterLimit和ArcTolerance,这两个参数和其同名的属性的含义是相同的。MiterLimit只有在JoinType是jtMiter的时候才启用,ArcTolerance只有在JoinType是jtRound或者当EndType是开放式轮廓的时候才有效;

  1. ClipperOffset.Execute

Del.» procedure Execute(out solution: TPaths; Delta: Double); overload;

C++ » void Execute(Paths& solution, double delta);

C#  » public void Execute(ref Paths solution, double delta);

Del.» procedure Execute(out PolyTree: TPolyTree; Delta: Double); overload;

C++ » void Execute(PolyTree& polytree, double delta);

C#  » public void Execute(ref PolyTree polytree, double delta);

该方法有两个参数,第一个是接受结果的结构(可以是PolyTree或者Paths中的一种)。第二个参数是指偏移量的大小——负值会缩水,正值会发生膨胀;

该方法可以被调用多次,来实现对相同路径实现不同程度的偏置;

#include "clipper.hpp"

...

using namespace ClipperLib;

int main()

{

Path subj;

Paths solution;

subj <<

IntPoint(348,257) << IntPoint(364,148) << IntPoint(362,148) <<

IntPoint(326,241) << IntPoint(295,219) << IntPoint(258,88) <<

IntPoint(440,129) << IntPoint(370,196) << IntPoint(372,275);

ClipperOffset co;

co.AddPath(subj, jtRound, etClosedPolygon);

co.Execute(solution, -7.0);

//draw solution ...

DrawPolygons(solution, 0x4000FF00, 0xFF009900);

}

  1. ClipperOffset.Clear

Del.» procedure Clear;

C++ » void Clear();

C#  » public void Clear();

清空所有多边形对象

属性

  1. ClipperOffset.ArcTolearance

Del.» property ArcTolerance: double; //read and write

C++ » double ArcTolerance;

C#  » public double ArcTolerance {get; set;}

首先,此字段/属性仅在JoinType = jtRound和/或EndType = etRound时相关。

由于展平路径永远不能完美地表示弧,因此当在偏移操作中近似弧时,此字段/属性指定最大可接受的不精确度(“容差”)。较小的值会增加“平滑度”,但会以性能为代价并创建更多顶点来构造弧。

默认的ArcTolerance是0.25个单位。这意味着扁平路径偏离“真实”弧的最大距离将不超过0.25个单位(在舍入之前)。

将公差降低到0.25以下将不会改善平滑度,因为顶点坐标仍将舍入为整数值。实现子整数精度的唯一方法是通过在偏移之前和之后的坐标缩放(参见下面的示例)。

使ArcTolerance成为偏移Δ(圆弧半径)的合理分数非常重要。相对于偏移三角形的大公差将产生较差的电弧近似,但同样重要的是,非常小的公差将显着减慢偏移性能,同时提供不必要的精度。当偏移坐标已缩放的多边形以保持浮点精度时,这很可能是一个问题。

示例:想象一组多边形(以浮点坐标定义),使用圆形连接将偏移10个单位,解决方案是将浮点精度保持在至少6个小数位。

为了保持这种浮点精度,并且假设Clipper和ClipperOffset都在整数坐标上运算,在偏移之前,多边形坐标将按比例放大108(并舍入为整数)。偏移delta和ArcTolerance也需要通过相同的因子进行缩放。如果ArcTolerance在默认的0.25单位处未缩放,则解中的每个弧将包含44 THOUSAND顶点的一小部分,而最终的弧不精确将是0.25×10-8个单位(即一旦缩放被反转)。但是,如果最终未缩放解决方案中0.1单位是可接受的不精确度,则应将ArcTolerance设置为0.1×scaling_factor(0.1×108)。现在,如果将缩放同等地应用于ArcTolerance和Delta Offset,则在此示例中,定义每个弧的顶点(步骤)的数量将是23的一小部分。

完整圆弧中的步数公式为...... Pi / acos(1  -  arc_tolerance / abs(delta))

  1. ClipperOffset.MiterLimit

Del.» property MiterLimit: double; //read and write

C++ » double MiterLimit;

C#  » public double MiterLimit {get; set;}

该属性包含了一个比例系数(= 最大容忍距离/偏置距离),当大于最大容忍距离时,则会使用1delta距离来进行阶段;

*默认的MiterLimit值大小是2,这也是允许的最小MiterLimit大小。如果没有规定合理的MiterLimit,在部分尖锐的拐角处就会形成长的突起;例子如下图所示

PolyNode

...

PolyTree

...

函数

Area

Del.» function Area(const pts: TPath): double;

C++ » double Area(const Path &poly);

C#  » public static double Area(Path poly);

该函数返回多边形的面积(默认输入的多边形为封闭轮廓)。根据方向的不同,该值大小可能为正或者负。如果方向为正,则结果为正,相反的,如果方向为负,则方向为负;

CleanPolygon

Del.» function CleanPolygon(const Poly: TPath; Distance: double = 1.415): TPath;

C++ » void CleanPolygon(const Path &in_poly, Path &out_poly, double distance = 1.415);

C++ » void CleanPolygon(Path &poly, double distance = 1.415);

C#  » public static Path CleanPolygon(Path poly, double distance = 1.415);

主要用来移除以下类型的点集:

  1. 连接点共线的边,或者近似共线的边(那么在指定距离内不会存在共线点);
  2. 距离过近的相邻点(在指定范围之内);
  3. 在次相邻的线段以及无关线段之间距离过小(在指定范围之内);

次相邻是指两点之间还有一个点(无关点);

函数中的distance默认值为√2,所以轮廓上每个点如果在X或者Y方向上与相邻点或者次相邻点之间的距离小于1个单位的点将会被去除。(如果相邻边也是与无关点次相邻也会被移除)。

C++限定:该函数被重载。在第一种定义中,in_poly和out_poly可以指同一个路径,来防止进入到第二种重载会自动清除Path中的内容(第二种是指Paths);

CleanPolygons

Del.» function CleanPolygons(const Polys: TPaths; Distance: double = 1.415): TPaths;

C++ » void CleanPolygons(const Paths &in_polys, Paths &out_polys, double distance = 1.415);

C++ » void CleanPolygons(Paths &polys, double distance = 1.415);

C#  » public static Paths CleanPolygons(Paths polys, double distance = 1.415);

主要用来移除以下类型的点集:

  1. 连接点共线的边,或者近似共线的边(那么在指定距离内不会存在共线点);
  2. 距离过近的相邻点(在指定范围之内);
  3. 在次相邻的线段以及无关线段之间距离过小(在指定范围之内);

次相邻是指两点之间还有一个点(无关点);

函数中的distance默认值为√2,所以轮廓上每个点如果在X或者Y方向上与相邻点或者次相邻点之间的距离小于1个单位的点将会被去除。(如果相邻边也是与无关点次相邻也会被移除)。

C++限定:该函数被重载。在第一种定义中,in_poly和out_poly可以指同一个路径,来防止进入到第二种重载会自动清除Path中的内容(第二种是指Paths);

ClosedPathsFromPolyTree

Del.» function ClosedPathsFromPolyTree(PolyTree: TPolyTree): TPaths;

C++ » void ClosedPathsFromPolyTree(PolyTree& polytree, Paths& paths);

C#  » public static void ClosedPathsFromPolyTree(PolyTree polytree, Paths paths);

从PolyTree中提取闭合轮廓部分。

MinkowskiDiff

Del.» function MinkowskiDiff(const Poly1: TPath; const Poly2: TPath): TPaths;

C++ » void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);

C#  » public static Paths MinkowskiDiff(Path poly1, Path poly2);

闵可夫斯基差,用来检测多边形碰撞使用。

Minkowski差异是通过从打开或闭合路径中的点集减去多边形中的每个点来执行的。 Minkowski差异的一个关键特征是,当它应用于两个多边形时,只要两个多边形接触或重叠,结果多边形就会包含坐标空间原点。 (此函数通常用于确定多边形何时发生碰撞。)

在左边的图像中,蓝色多边形是两个红色框的“minkowski差异”。 黑点表示坐标空间原点。

MinkowskiSum

Del.» function MinkowskiSum(const Pattern: TPath; const Path: TPath; PathIsClosed: Boolean): TPaths; overload;

Del.» function MinkowskiSum(const Pattern: TPath; const Paths: TPaths; PathFillType: TPolyFillType; PathIsClosed: Boolean): TPaths; overload;

C++ » void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);

C++ » void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, PolyFillType pathFillType, bool pathIsClosed);

C#  » public static Paths MinkowskiSum(Path pattern, Path path, bool pathIsClosed);

C#  » public static Paths MinkowskiSum(Path pattern, Paths paths, PolyFillType pathFillType, bool pathIsClosed);

闵可夫斯基和

Minkowski Addition是通过将多边形“pattern”中的每个点添加到打开或关闭路径中的点集来执行的。 生成的多边形(或多边形)定义“模式”在从“路径”的开头到结尾移动时将经过的区域。

Path path = new Path();

Path pattern = new Path();

Paths solution = new Paths();

//Greek capital sigma (sum sign) ...

Int64[] ints1 = new Int64[] { 300, 400, 100, 400, 200, 300, 100, 200, 300, 200 };

path = IntsToPolygon(ints1);

//diagonal brush pattern ...

Int64[] ints2 = new Int64[] { 4, -6, 6, -6, -4, 6, -6, 6 };

pattern = IntsToPolygon(ints2);

solution = Clipper.MinkowskiSum(pattern, path, false);

//move 'pattern' to the end of 'path' ...

pattern = TranslatePath(pattern, 300, 200);

//Display solution ± pattern ...

OffsetPaths

Del.» function OffsetPaths(const polys: Paths; const delta: double; JoinType: TJoinType = jtSquare; EndType: TEndType = etClosed; Limit: double = 0.0): TPaths;

C++ » void OffsetPaths(const Paths &in_polys, Paths &out_polys, double delta, JoinType jointype = jtSquare, EndType endtype = etClosed, double limit = 0.0);

C#  » public static Paths OffsetPaths(Paths polys, double delta, JoinType jointype = JoinType.jtSquare, EndType endtype = EndType.etClosed, double limit = 0.0);

弃用. (见 ClipperOffset.)

OpenPathsFromPolyTree

Del.» function OpenPathsFromPolyTree(PolyTree: TPolyTree): TPaths;

C++ » void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);

C#  » public static void OpenPathsFromPolyTree(PolyTree polytree, Paths paths);

提取PolyTree的开放路径部分。

Orientation

Del.» function Orientation(const poly: TPath): boolean;

C++ » bool Orientation(const Path &poly); // Function in the ClipperLib namespace.

C#  » public static bool Orientation(Path poly); // Static method of the Clipper class in the ClipperLib namespace.

方向只有在封闭的曲线里面是重要的,方向的定义就是在给定的封闭曲线中给出点集是按照顺时针还是逆时针进行排列;

方向同样是跟轴的方向有关联的:

  1. 在Y轴向上显示模式中,方向变量为true对应的是为逆时针轮廓;
  2. 在Y轴向下显示模式中,方向变量为true对应的是为顺时针轮廓;

Note:

自交多边形有着不确定的方向性,此时函数不会返回一个有意义的值;

大部分2D的图形显示库(例如GDI、GDI+)甚至SVG文件格式有其坐标原点(在左上角,y轴方向朝下)。但是,一部分显示库(OpenGL等)可以自定义坐标原点,或者自定义Y方向朝上;

对于None-Zero填充的多边形,内环(内孔)的方向必须和外环相反;

对于由CLipper库的Execute方法,他们的外孔结果永远为true,内孔结果永远为false;(除非ReverseSolution属性已经被启用)

PointInPolygon

Del.» function PointInPolygon(const Pt: TIntPoint; const poly: TPath): Integer;

C++ » int PointInPolygon(const IntPoint pt, const Path &poly); // Function in the ClipperLib namespace.

C#  » public static int PointInPolygon(IntPoint pt, Path poly); // Static method of the Clipper class.

判断点是否在多边形内,如果返回0则是不在,-1则为在多边形边缘上,1为在多边形内;

PolyTreeToPaths

Del.» function PolyTreeToPaths(PolyTree: TPolyTree): TPaths;

C++ » void PolyTreeToPaths(PolyTree& polytree, Paths& paths);

C#  » public static Paths PolyTreeToPaths(PolyTree polytree);

将PolyTree类型数据转换为Paths数据;

ReversePath

Del.» function ReversePath(const polys: TPath): TPath;

C++ » void ReversePath(const Path &p);

C#  » //Call Path.Reverse().

将点集组进行倒序排列.

ReversePaths

Del.» function ReversePaths(const p: TPaths): TPaths;

C++ » void ReversePaths(const Paths &p);

C#  » void ReversePaths( Paths p );

将点集组进行倒序排列.

SimplifyPolygon

Del.» function SimplifyPolygon(const Poly: TPath; FillType: TPolyFillType = pftEvenOdd): TPaths;

C++ » void SimplifyPolygon(const Path &in_poly, Paths &out_polys,

PolyFillType fillType = pftEvenOdd);

C#  » public static Paths SimplifyPolygon(Path poly,

PolyFillType fillType = PolyFillType.pftEvenOdd);

将自交部分从自身的部分中剔除(通过指定一个FillType来实现一个布尔的求和操作)。

多个没有相邻重复点(或者说没有“接触”)的多边形将会被分割为两个多边形组;

SimplifyPolygons

Del.» function SimplifyPolygons(const polys: TPaths;

FillType: TPolyFillType = pftEvenOdd): TPaths;

C++ » void SimplifyPolygons(const Paths &in_polys, Paths &out_polys,

PolyFillType fillType = pftEvenOdd);

C++ » void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);

C#  » public static Polygons SimplifyPolygons(Paths polys,

PolyFillType fillType = PolyFillType.pftEvenOdd);

将自交部分从自身的部分中剔除(通过指定一个FillType来实现一个布尔的求和操作)。

多个没有相邻重复点(或者说没有“接触”)的多边形将会被分割为两个多边形组;

ClipperLib库使用说明相关推荐

  1. 【ESP32_8266_WiFi (十四)】ESP8266多任务处理 – Ticker库使用说明

    文章目录 ESP8266多任务处理 – Ticker库使用说明 1 Ticker库基本操作 2 停止定时执行函数 3 向定时调用函数传递参数 4 利用多个Ticker对象让ESP8266处理多任务 5 ...

  2. TSCLIB.DLL函数库使用说明

    TSCLIB.DLL函式库使用说明方面的问题.注意:使用动态库TSCLIB.DLL前,安装TSC条码印表机驱动. 1. openport(a)  说明:指定电脑端的输出端 参数:  a:单机列印时,请 ...

  3. 【ESP32_8266_WiFi (十三)】ESP8266自动配网 – WiFiManager库使用说明

    文章目录 ESP8266自动配网 – WiFiManager库使用说明 1 WiFiManager库使用说明 1.1 WiFi配置流程 1.2 WiFi配置示例程序 1.2.1 预备程序 – 清理ES ...

  4. linux下libxml2库使用说明

    linux下libxml2库使用说明_lanlicen的专栏-CSDN博客_linux xml库 第一章 什么是XML? 1.xml简介 XML(Extensible Markup Language) ...

  5. [arduino][u8g2][12864] oled—u8g2库使用说明(例子是12864液晶屏)

    [arduino][u8g2][12864] oled-u8g2库使用说明(例子是12864液晶屏) 首先说一下写这个文档的目的,一是给自己做个笔记来以备后边使,二是写个文档给大家看,我从网上找了很多 ...

  6. appemit 支持chrome edge谷歌微软浏览器佳博Gprinter 标签打印机 TSCLIB.DLL 函数库使用说明

    支持谷歌 firefox edge 360 qq sogou等各种浏览器,在线使用 佳博Gprinter 标签打印机 直接js操作 TSCLIB.DLL 函数库使用说明 请在使用TSCLIB.DLL ...

  7. Jetson-GPIO_python库 使用说明

    Jetson-GPIO 使用说明 一.环境搭建 1.安装Jetson.GPIO 库 Jetson.GPIO库 已经预装在Nano,无需再安装其他GPIO Python库,如果安装了其他GPIO库需先卸 ...

  8. Python wordcloud库使用说明

    1. 介绍 wordcloud是优秀的词云展示第三方库 -词云以词语为基本单位,更加直观和艺术地展示文本 通过词云,我们可以快速提取大段文本的重要信息 2. 安装 (cmd命令行) pip insta ...

  9. C++11 多线程库使用说明

    多线程基础 1.1 进程与线程 根本区别: 进程是操作系统资源分配的基本单位,线程是任务调度和执行的基本单位 开销方面: 每个进程都有自己独立的代码和数据空间,程序之间的切换开销较大. 线程可以看作是 ...

最新文章

  1. 【Interfacenavigation】选择时间/日期组件(34)
  2. Python枚举类型的使用
  3. Python-OpenCV 笔记2 -- 图像的基本属性和操作
  4. 新手教程:用像素游戏制作大师MV开发游戏(一)
  5. 使用msui的回到顶部的一个小问题
  6. 简单的二叉树创建与遍历
  7. 优酷视频如何在手机进行安全设置?
  8. jdk1.6+Maven
  9. uri uri_什么是URI? 了解许可证术语以确保合规
  10. routing zuul_金三银四跳槽季快到了:送上Spring cloud全家桶系列之Zuul
  11. jquery+ajax验证不通过也提交表单问题处理
  12. 关于java重载函数,参数为null时,调用的处理。(精确性原则)
  13. 计算机网络-域名与IP地址详解
  14. 数据可视化:基本图表
  15. 黑色沙漠首发五职业PVP强度排行
  16. 甜椒刷机助手(安卓一键刷机助手) v3.5.1.1 电脑版
  17. Cacls Command Question
  18. 恢复系统设置或计算机点击没反应,win7自带还原里quot;恢复系统设置或计算机quot;点击没反应...
  19. 小功能⭐️Unity快捷键、路径及常用特性
  20. 【程序员生活志】百度、腾讯、阿里等互联网公司年终奖都发多少?我酸了!

热门文章

  1. 解压命令tar zxvf中zxvf的意思
  2. SpringBoot单元测试Mock静态方法
  3. 会话机制(session)
  4. BloomFilter--理解总结
  5. 文本相似度匹配-task1
  6. 【python】实现SIMM算法
  7. 大中型规模灵活定制的网吧组网方案(转)
  8. [渝粤教育] 广东-国家-开放大学 21秋期末考试大学英语210262k2 (2)
  9. SLF4J及其MDC详解
  10. springMVC 自定义类型转换器