Triangulation by Ear Clipping(耳切法处理多边形三角划分)

 

使用EarClipping三角化多边形(翻译)算法

---Triangulation by Ear Clipping(http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf)编程

内容提要数组

一、简介数据结构

二、Ear Clipping方法函数

三、含有岛洞的多边形测试

四、查找相互可见点翻译

五、含有多个岛洞的多边形3d

六、嵌套多边形日志

 

一、简介blog

将简单多边形转换成一组由一样顶点组成的三角形集合是计算机图形学中的一个经典问题。问题中,简单多边形是指由一组有序顶点组成的,点V0~点Vn-1。相邻的顶点之间经过边(Vi, Vi-1)链接,而且边(Vn-1,V0)链接起始点。每一个顶点被两条边所共享,而边的全部交点都是顶点。图1.1的示例则是说明

图中,左边的多边形是个简单多边形,中间的多边形点1被四条边共享,不符合定义的条件,不算是简单多边形,右侧的多边形中边14,边02的交点不是咱们定义的顶点之一,所以该图形也不符合简单多边形的定义。

 

图1.1 简单多边形示例

若是一个多边形是简单多边形,当你延长一条边的时候,内部有界区域中老是在边的一侧。假设多边形顶点逆时针排序,那么当你延长边的时候,内部指的即是你的左边。 咱们图1.1中的简单多边形顶点顺序使用的即是逆时针的方法。

       将一个简单多边分解成三角形集合的方法称之为多边形的三角形化(triangulation of the Polygon)。几何学的知识告诉咱们,由n个顶点组成的简单多边形老是能够分解成n -2个三角形。解决该问题的方法比较多,他们共同的特色就是算法的复杂度渐近阶随着n的增加没有约束(Various algorithms have been developed for triangulation, each characterized by its asymptotic order as n grows without bound.)。最简单的分割算法是耳剪裁(EarClipping),正是本文档中所要描述的算法。EarClipping的算法复杂度O(n2_n平方),虽然也存在效率更高的算法,可是被其余组织严格使用并无公开。水平分解成梯形随后被本身单调三角多边形的鉴定阳离子是一种复杂度为O(n log n)的算法[ 1 , 3 ] 。使用增量的改进随机算法产生一个O ( n日志? n),其中记录? n为重对数函数[ 5 ] 。此功能是电子?做为各自一个常数很是大的n ,你会在实践中看到的,因此对于全部的实际目的的随机方法是线性时间。理论存在的复杂度为O(n)的算法,比较复杂,到目前依旧没有看到具体的公开实现。

二、Ear Clipping方法

简单多边形的耳朵,是指由连续顶点V0,V1和V2组成的内部不包含其余任意顶点的三角形。在计算机几何术语中,v0与V2之间的连线 称之为多边形的对角线,点V1称之为耳尖。虽然你能够将耳尖放到三角形的任意一个顶点上,可是咱们认为三角形包含一个耳尖。一个由四个顶点(或者更多)组成的多变形至少有两个不重叠的耳尖。这个特性提供了一个经过递归来解决三角化分割的方法。针对由N个定点组成的多边形,找到其耳尖,移除惟一耳尖上的顶点,此时剩余顶点组成了一个n-1个顶点的简单多边形。咱们重复这个操做知道剩余三个顶点。这样的话会产生一个复杂度为O(N3)的算法

 

随着一些细节改进,耳朵消除能够在O ( N2)的时间来完成。第一步是将多边形使用双向链表存储,这样能够快速的移除耳朵。列表的构建复杂度是O(n),第二部是遍历顶点寻找耳朵。对于每个顶点Vi和围绕该顶点的三角形<Vi-1,Vi,Vi+1>,(总长度为N,因此Vn=V0,兵器V-1=Vn-1),测试其余顶点是否在当前三角形中,若是有一个顶点在三角形里面,则不是耳朵,只有都不在的状况,才算是找到一个耳朵。具体实现的时候咱们能够考虑如下因素让这个算法更为高效。当发现有一个点在三角形里面的时候即可以开始放弃当前测试。一个凹拐角其两边的夹角大于180,而一个凸拐角两边夹角小于180。存储多边形的数据结构使用四个链表,具体使用数组而不是标准的动态须要分配合释放存储器的链表。多边形的顶点存储在在一个循环链表中,凹顶点和凸顶点存储在线型表中,耳尖存储在一个循环列表中。

 

一旦凸顶点和耳朵的链表构建成功,每次遍历都会移除一个耳朵。假设当前Vi是个耳朵而且被移除掉,那么边结构的相邻点Vi-1,Vi+1则会发生变化,若是相邻点是凸顶点,那么依旧保持凸点,若是相邻点是个耳朵,那么当Vi被移除后则不必定能保持耳朵的状态,若是相邻点是个凹点,那么他则有可能变为一个凸点甚至是耳朵。所以当移除顶点Vi后,若是相邻点是凸点,则必须遍历相关顶点,经过遍历查看是否包含其余点,来测试它是不是一个耳朵。咱们有n个耳朵,每一次更新都会触发一个耳朵检测,每次过程当中更新O(n),因此移除进程的复杂度是O(n2)。

 

图2.1 右侧多边形展现了左侧耳朵2,3,4被移除后的的效果

       下面的示例使用图1.1中的简单多边形,具体展现算法的实现和构建。初始够将的时候凸顶点集合C={0,1,3,4,6,9},初始凹顶点集合R={2,5,7,8},初始的耳朵集合E={3,4,6,9},遍历,当顶点3被移除的时候,其对应的耳朵是三角形T0=<2,3,4>。图2.1展现了改进后的多边形效果。相邻点2是个凹节点,变化后依旧是凹的,顶点4以前是个耳朵,如今依旧耳朵,因此凹节点结合R保持不变,耳朵集合如今变成了E={4,6,9}(3已经被移除)。

       继续移除点点4,此时的三角形对应是T1=<2,4,5>。图2.2展现了变化后的效果。

 

图 2.2 移除三角形<2,4,5>后的效果

相邻顶点2依旧保持凹节点,相邻点5以前是凹顶点,如今变为了凸顶点,通过测试最终发现它是个耳朵。所以定点列表最终的变化结果是,凹节点几何R={2,7,8},耳朵集合E={5,6,9}(移除4,添加了新的5)。

若是一处顶点5,此时对应的三角形是T2=<2,5,6>,图2.3展现了变化后的效果。

 

图2.3 移除耳朵<2,5,6>后的效果

       相邻顶点2起初是个凹节点,如今变为另外一个图节点,从图上有点不大容易看出顶点7实际上是位于三角形<1,2,6>中间的,因此2不是个耳朵。顶点6起初是个耳朵,如今依旧。操做完成以后各顶点列表中,凹节点集合R={7,8}(移除了2),耳朵集合E={6,9}(移除了5)。

       继续,移除顶点6,此时对应的三角形是T3=<2,6,7>。图2.4是变化后的先后对比效果。

 

图2.4移除耳朵<2,6,7>

相邻点2是图节点,保持依旧,可是它由一个非耳朵变成了耳朵节点。相邻顶点7依旧是个凹节点,所以凹节点集合保持不变。各队列结果,耳朵集合E={9,2}(添加2移除6),耳朵列表这样写是由于新耳朵的加入是在移除了旧的耳朵操做以后(先来后到),在移除旧耳朵以前,它忍让能够被当作是列表的第一个元素(循环列表)。删除操做设置第一项是下一个指向的老耳朵,而不是之前的值。

       移除顶点9,对应的三角形T4=<8,9,0>。图2.5展现了操做先后的多边形对比

 

       相邻顶点8是个凹节点,操做后编程了一个凸点,而且是一个耳朵。相邻点0是个凸点,保持依旧,而且由非耳朵变成了耳朵。操做结束后的各队列集合以下:凹点集合R={7},耳朵集合E={0,2,8}(添加8 ,添加0,移除9,顺序按照了程序的产生方式)

       移除顶点0,对应的三角形是T5=<8,0,1>,图2.6是操做先后的多边形对比

 

       相邻顶点8和1都是凸节点而且保持依旧,顶点8依旧是个耳朵,顶点1依旧不是耳朵。所以凹节点集合不变,耳朵列表变为E={2,8}(移除了0)

       最后,移除耳朵2顶点,对应的三角形是T6=<1,2,7>。图2.7展现了操做先后的多边形对比。

 

       到如今,已经没有在须要更新的凹点和耳朵列表,到此为止咱们只剩下了三个顶点,这三个顶点组成最后的三角形T7=<7,8,1>。全部的三角形分割线是如图2.8

 

三、含有岛洞的多边形

耳朵裁剪法也能够应用到包含岛洞的多边形中。考虑如图3.1所示的一个包含岛洞的多边形,他有一个外多边形和一个内洞组成。外侧多边形的定点方形和内测岛洞的顶点方向必须是相反的。若是外侧的顶点是逆时针顺序,那么内测的顶点则必须是顺时针顺序。

 

       图中蓝色的顶点是互相可见的,经过绘制两条双向的边链接两个蓝色的顶点,能够把图3.1转变成一个简单多边形。图3.2中显示这两天便,一条蓝色,一条红色,两条边是重叠的,这里为了看得清晰特殊标绘出来。

 

图中经过小箭头标识出了边的方向。

       依照这种情景,互相可见的顶点必须复制到不一样的数据结构中以供使用。每一个数据结构存储当前点多是凹点也多是凸点。即便使用同一个坐标的两个点,也可能一个是凹点,一个是凸点,好比位于最下面的蓝色顶点11(18)。原始的顶点在最初的外多边形中是凹点,分割后在新的多边形中,V11与红色边相连,构成了一个凹点,与蓝色边相连构成另外一个凸点。

原始的外多边形顶点数据:

{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}

原始的内多边形数据

{15,16,17}

       分割后顶点V11被复制出V18,顶点V16复制出V19,这时候的简单多边形数据以下:

{0,1,2,3,4,5,6,7,8,9,10,11,16,17,15,19,18,12,13,14}

新的多边形便可以使用耳朵裁剪法切割。

四、查找相互可见点

视觉上,图3.1咱们能够直接看出顶点V11和顶点V16是两个相互可见的点。而实际上,这个多边形中存在不止一对这样的相互可见点(一个来自内多边形,一个来自外多边形)。咱们须要个算法来查找一对这样的可见点。

下面的算法即是如此。查找内多边形中x轴方向最大值的顶点,在图3.1中,这个点是V16,假设以此为原点构造左边系,沿着x轴正方向观察,该轴线可能与外对变形的边教育一点,或者直接链接到外多边形的顶点上,很大程度上,咱们会得到一个边的交点。此时,这条边的两个端点则颇有可能使咱们所要寻找的。若是是一个独立的点点,那么便和最初的V16组成了一对相互可见点。

下面,咱们考虑在x轴正方向的最近可视点是相交边的端点。如图4.1所示:

 

假设M是坐标轴的原点(其实是顶点V16)。向量M+t(1,0)则是图中蓝色标记的x轴射线,最近的交点使用红色标记,叫作点I。最近点所在的边使用绿色标绘出来。边的结束点用一个最大的x值P,假设P点是与M对应的最近相互可视点,那么链接他们的线,与点I组成的三角形<M,I,P>使用橙色绘制以下。

在图4.1中,P相对M可见,可是,也存在下面的这种状况,线段<M,P>与多边形其余的边相交,即P对M不可见,图4.2展现了这样的状况。

 

灰色表示该区域位于外部多边形和内部多边形之间,橙色是其中的一部分,在图4.1中,有<M.I,P>组成的三角形所有位于(外部-内部)多边形的一部分,4.2中,外部多边形被裁剪到了多个三角形中,只有部分子集三角形才算的上是外部-内部多边形的一部分。

 

外部多边形有四个点位于多边形<M,I,P>之间,通常来讲,若是一个顶点存在于一个三角形内部,则至少有一个链接边,对于全部链接边,至少有一个是对M可见的。图4.2中,三角形内有三个链接顶点,标记为A,B,C,这样的话链接顶点A对M是可见的,由于链接他们的边<M,R>和边<1,0>之间的夹角最小。

算法总结以下:

 

一、寻找内部多边形x周最大值的顶点M

二、沿X周正方向,寻找最近的相交边<Vi,Vi+1>,让其焦点设置为I,构成X轴方向对M的最近可见点

三、若是I是一个外部顶点,则M和I相互可见,算法执行结束

四、若是I只是边上的一个点,寻找端点中x值片的一个,设置为P

五、寻找位于P内的其余外多边形的可链接顶点。若是全部的顶点都在<M,I,P>以外,则M与P相互可见,反正结果

六、若是有至少一个点位于三角形<M,I,P>内部,则寻找其中的一个顶点,计算其与x轴(1,0)的夹角,夹角最小的顶点R与M构成相互可见边,算法结束

七、在这个算法中,有可能有多个顶点同事具备最小的角度,这种状况下,寻找距离M最近的一个点便可

五、含有多个岛洞的多边形

一个多边形有可能包含多个岛洞,这里假设全部的岛洞都仅被外多边形包含,彼此不存在嵌套岛洞的情形,图5.1展现了这样的一个多边形。

 

从图上能够清晰的看出,内多边形I1没有任何一个顶点与外部多边形相互可见,多边形I0则拥有多个与外部多边形相互可见的点。所以,咱们可使用前面介绍的算法,首先把I0和外部多边形拆分,合并成为一个简单多边形,这样,新造成的外多边形则和I1构成了一件简单多边形,使用耳切法分割集合。

 

假设有多个内多边形,拥有最大X值的内部多边形则可被选中做为与外多边形合并的首选。重复这个过程知道所有成为简单多边形便可。

六、嵌套多边形

内多边形也可能包含一些泪如岛洞的外多边形,类如嵌套。这样致使了嵌套多边形的树形结构。根节点是最外围的外多边形,子节点则是包含在当前最外多边形内部的内多边形。每个孙子节点,则是构成直接被最外围多边形包含的内多边形的子树,每一个多边形树能够按照宽度优先去遍历。

图6.1展现了一个嵌套多边形构成的树结构,能够分割使用耳切法

 

树形结构展现以下:

 

存储当前树节点的数据结构能够定义以下:

 

解析当前树形结构的流程算法大体以下:

 

 

函数MakeSimple封装了获取一个内多边形和其外多边形中间相互可见边的算法,经过复用他们,产生两个新边,能够生成一个新的简单多边形,这个过程要对没个内多边形不停的重复

 

完成最终的三角划分,以便获取最终的索引顺序,来代替最初的多边形顶点定义顺序。相比原始的值,这里可能须要复制一些顶点,以便被多个三角形使用

相关文章
相关标签/搜索