使用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封裝了獲取一個內多邊形和其外多邊形中間相互可見邊的算法,經過複用他們,產生兩個新邊,能夠生成一個新的簡單多邊形,這個過程要對沒個內多邊形不停的重複
完成最終的三角劃分,以便獲取最終的索引順序,來代替最初的多邊形頂點定義順序。相比原始的值,這裏可能須要複製一些頂點,以便被多個三角形使用