二維空間內的三角剖分2 -- (給出邊緣頂點的例子)

  以前的三角剖分, 竟然弄的如此複雜, 不再相信百度了......ide

  換一種思路, 首先給出的頂點是連續的頂點, 那麼能夠以一種農村包圍城市的方法來劃分, 咱們只須要沿着順序不斷獲取Pa, Pb, Pc三點, 而後檢測他們是否構成三角形(不在一條直線上)而且沒有其它頂點在三角形內, 而後經過剔除頂點的方式繼續劃分, 就能把三角形一個個分解出來, 雖然都是簡單劃分, 對於複雜的好比自相交它仍是不能正確計算, 但是相比以前的簡直又快又簡單, 看下面圖解: 函數

  沿着給出的邊無限循環取點, 好比先取[0, 1, 2] 而後測試到 Point 3 在它們構成的三角形內, 就跳過繼續取點[1, 2, 3] 而後檢測沒有點在其中, 這就獲得第一個三角形了:測試

  而後將位於中間的點剔除出頂點列表, 這樣等於獲得最外圍的某個三角形了, 好像農村包圍城市同樣, 剔除掉 Point 2 以後繼續進行測試, 獲取 [3, 4, 5] 構成的三角形, 一次類推...this

  頂點反向也能夠正常計算出來.spa

代碼:code

    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 邏輯, 以後再研究...blog

相關文章
相關標籤/搜索