之前的三角剖分, 居然弄的如此復雜, 再也不相信百度了......
換一種思路, 首先給出的頂點是連續的頂點, 那么可以以一種農村包圍城市的方法來劃分, 我們只需要沿著順序不斷獲取Pa, Pb, Pc三點, 然后檢測他們是否構成三角形(不在一條直線上)并且沒有其它頂點在三角形內, 然后通過剔除頂點的方式繼續劃分, 就能把三角形一個個分解出來, 雖然都是簡單劃分, 對于復雜的比如自相交它還是不能正確計算, 可是相比之前的簡直又快又簡單, 看下面圖解:

沿著給出的邊無限回圈取點, 比如先取[0, 1, 2] 然后測驗到 Point 3 在它們構成的三角形內, 就跳過繼續取點[1, 2, 3] 然后檢測沒有點在其中, 這就得到第一個三角形了:

然后將位于中間的點剔除出頂點串列, 這樣等于得到最外圍的某個三角形了, 好像農村包圍城市一樣, 剔除掉 Point 2 之后繼續進行測驗, 獲取 [3, 4, 5] 構成的三角形, 一次類推...

頂點反向也可以正常計算出來.
代碼:
public class Triangulator { private List<Vector2> m_points = new List<Vector2>(); public Triangulator(List<Vector2> points) { m_points = points; } public int[] Triangulate() { List<int> indices = new List<int>(); int n = m_points.Count; if(n < 3) { return indices.ToArray(); } int[] V = new int[n]; if(Area() > 0) { for(int v = 0; v < n; v++) { V[v] = v; } } else { for(int v = 0; v < n; v++) { V[v] = (n - 1) - v; } } int nv = n; int count = 2 * nv; for(int v = nv - 1; nv > 2;) { if((count--) <= 0) { return indices.ToArray(); } int u = v; if(nv <= u) { u = 0; } v = u + 1; if(nv <= v) { v = 0; } int w = v + 1; if(nv <= w) { w = 0; } if(Snip(u, v, w, nv, V)) { int a, b, c, s, t; a = V[u]; b = V[v]; c = V[w]; indices.Add(a); indices.Add(b); indices.Add(c); for(s = v, t = v + 1; t < nv; s++, t++) { V[s] = V[t]; } nv--; count = 2 * nv; } } indices.Reverse(); return indices.ToArray(); } private float Area() { int n = m_points.Count; float A = 0.0f; for(int p = n - 1, q = 0; q < n; p = q++) { Vector2 pval = m_points[p]; Vector2 qval = m_points[q]; A += pval.x * qval.y - qval.x * pval.y; //Cross } return (A * 0.5f); // Triangle Size } // can this triangle be clipped? private bool Snip(int u, int v, int w, int n, int[] V) { int p; Vector2 A = m_points[V[u]]; Vector2 B = m_points[V[v]]; Vector2 C = m_points[V[w]]; if(Mathf.Epsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x)))) { return false; // 三邊重合以及方向檢測 } for(p = 0; p < n; p++) { if((p == u) || (p == v) || (p == w)) { continue; } Vector2 P = m_points[V[p]]; if(InsideTriangle(A, B, C, P)) { return false; } } return true; } // P inside triangle[A,B,C] public static bool InsideTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P) { var pa = (A - P); var pb = (B - P); var pc = (C - P); var crossA = CrossVec2(pa, pb); var crossB = CrossVec2(pb, pc); var crossC = CrossVec2(pc, pa); bool inside = (crossA >= 0 && crossB >= 0 && crossC >= 0) || (crossA <= 0 && crossB <= 0 && crossC <= 0); return inside; } public static float CrossVec2(Vector2 a, Vector2 b) { return (a.x * b.y) - (a.y * b.x); } }
它最重要的邏輯是 Area() 這個函式, 它通過叉乘計算了一個面積, 而影響了原始點的排列順序, 然后影響到 Snip 邏輯, 之后再研究...
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/2221.html
標籤:其他
