在作一個Low Poly的課題,而這種低多邊形的成像效果在如今設計中愈來愈被喜歡,其中的低多邊形都是由三角形組成的。git
而如何自動生成這些看起來很特殊的三角形,就是本章要討論的內容。github
項目地址: https://github.com/zhiyishou/polyer算法
Demo:https://zhiyishou.github.io/Polyer數組
其是最早是由不少離散的點組成,基於這個肯定的點集,將點集鏈接成必定大小的三角形,且分配要相對合理,才能呈現出漂亮的三角化。緩存
這時則要求使用三角剖分算法(Delaunay),引於百度百科《Delaunay三角剖分算法》對Delaunay三角形的定義爲:優化
【定義】三角剖分:假設V是二維實數域上的有限點集,邊e是由點集中的點做爲端點構成的封閉線段, E爲e的集合。那麼該點集V的一個三角剖分T=(V,E)是一個平面圖G,該平面圖知足條件:1.除了端點,平面圖中的邊不包含點集中的任何點。2.沒有相交邊。3.平面圖中全部的面都是三角面,且全部三角面的合集是散點集V的凸包。在實際中運用的最多的三角剖分是Delaunay三角剖分,它是一種特殊的三角剖分。先從Delaunay邊提及:【定義】Delaunay邊:假設E中的一條邊e(兩個端點爲a,b),e若知足下列條件,則稱之爲Delaunay邊:存在一個圓通過a,b兩點,圓內(注意是圓內,圓上最多三點共圓)不含點集V中任何其餘的點,這一特性又稱空圓特性。【定義】Delaunay三角剖分:若是點集V的一個三角剖分T只包含Delaunay邊,那麼該三角剖分稱爲Delaunay三角剖分。【定義】假設T爲V的任一三角剖分,則T是V的一個Delaunay三角剖分,當前僅當T中的每一個三角形的外接圓的內部不包含V中任何的點。
其方法雖然可實現三角化,可是效率仍是不過高subroutine triangulate input : vertex list output : triangle list initialize the triangle list determine the supertriangle add supertriangle vertices to the end of the vertex list add the supertriangle to the triangle list for each sample point in the vertex list initialize the edge buffer for each triangle currently in the triangle list calculate the triangle circumcircle center and radius if the point lies in the triangle circumcircle then add the three triangle edges to the edge buffer remove the triangle from the triangle list endif endfor delete all doubly specified edges from the edge buffer this leaves the edges of the enclosing polygon only add to the triangle list all triangles formed between the point and the edges of the enclosing polygon endfor remove any triangles from the triangle list that use the supertriangle vertices remove the supertriangle vertices from the vertex list end
input: 頂點列表(vertices) //vertices爲外部生成的隨機或亂序頂點列表 output:已肯定的三角形列表(triangles) 初始化頂點列表 建立索引列表(indices = new Array(vertices.length)) //indices數組中的值爲0,1,2,3,......,vertices.length-1 基於vertices中的頂點x座標對indices進行sort //sort後的indices值順序爲頂點座標x從小到大排序(也可對y座標,本例中針對x座標) 肯定超級三角形 將超級三角形保存至未肯定三角形列表(temp triangles) 將超級三角形push到triangles列表 遍歷基於indices順序的vertices中每個點 //基於indices後,則頂點則是由x從小到大出現 初始化邊緩存數組(edge buffer) 遍歷temp triangles中的每個三角形 計算該三角形的圓心和半徑 若是該點在外接圓的右側 則該三角形爲Delaunay三角形,保存到triangles 並在temp裏去除掉 跳過 若是該點在外接圓外(即也不是外接圓右側) 則該三角形爲不肯定 //後面會在問題中討論 跳過 若是該點在外接圓內 則該三角形不爲Delaunay三角形 將三邊保存至edge buffer 在temp中去除掉該三角形 對edge buffer進行去重 將edge buffer中的邊與當前的點進行組合成若干三角形並保存至temp triangles中 將triangles與temp triangles進行合併 除去與超級三角形有關的三角形 end
大多數同窗看過僞代碼後仍是一頭霧水,因此用圖來解釋這個過程,咱們先用三點來作實例:this
如圖,隨機的三個點spa
根據離散點的最大分佈來求得隨機一個超級三角形(超級三角形意味着該三角形包含了點集中全部的點).net
個人方法是根據類似三角形定理求得與矩形一半的小矩形的對角三角形,擴大一倍後則擴大後的直角三角形斜邊通過點(Xmax,Ymin)設計
可是爲了將全部的點包含在超級三角形內,在右下角對該三角形的頂點進行了橫和高的擴展,並要保證這個擴展三角形底大於高,才能實現包含
這樣求得的超級三角形不會特別大使得計算複雜,並且過程也簡單,並將超級三角形放入temp triangles中
接下來就像是僞代碼中描述的那樣,對temp triangle中的的三角形遍歷畫外接圓,這時先對左邊的第一個點進行判斷,其在圓內
因此該三角形不爲Delaunay三角形,將其三邊保存至edge buffer中,temp triangle中刪除該三角形
將該點與edge buffer中的每個邊相連,組成三個三角形,加入到temp triangles中
再將重複對temp triangles的遍歷並畫外接圓,這時使用的是第二個點來進行判斷
再次對temp triangles進行遍歷,這裏該數組裏則含有四個三角形,一個是上次檢查跳過的含有第一個點的三角形和新根據第二個點生成的三個三角形
這時,temp buffer 中有六條邊,triangles中有兩個三角形,temp triangles中有1個三角形
對temp buffer中的六條邊進行去重,獲得五條邊,將該點與這五條邊組合成五個三角形並加入到temp triagnles 中,這時temp triangles中有6個三角形
因爲三個點已經遍歷結束,到了不會再對第三個點造成的三角形作外接圓,這時則將triangles與temp trianlges合併,合併後的數組表示包含已經肯定的Delaunay三角形和剩下的三角形
這時除去合併後數組中的和超級三角形三個點有關的全部三角形,即進行數組座標的限定,則獲得了最後的結果:
在用點對三角形外接圓位置關係進行判斷的時候,爲何點在外接圓的右側的話能夠肯定該三角形是Delaunay三角形
而當點外接圓的外側且非右側時,爲何要路過三角形,不把該三角形肯定爲Delaunay三角形呢?
首先,咱們在開始的時候對原始方法進行優化時,咱們增長了一個indices數組來操做vertices,並對indices依據vertices的x座標進行了從小到大的排序
則咱們在後面遍歷點時是從點集的最左側開始的,如圖: