编程语言
首页 > 编程语言> > Unity动态构建mesh绘制多边形算法流程分析和实践

Unity动态构建mesh绘制多边形算法流程分析和实践

作者:互联网

前言

先说一下,写这篇博文的动机,原文的博主代码写的十分潇洒,以至于代码说明和注释都没有,最近恰逢看到,所以以此博文来分析其中的算法和流程
参考博文:https://blog.csdn.net/linxinfa/article/details/78816362

github网址:https://github.com/linxinfa/Unity-ArbitraryPolygonMesh

先复习一下线代
向量的混合积的数学意义是:两个向量叉乘的结果是一个新向量,这个新向量垂直于原向量组成的平面,并且新向量的长度等于原向量合成的平行四边形的面积

向量的混合积是三个向量组成的平行六面体的体积。叉乘可以看成高是单位长度的平行六面体的体积,也就是其平行四边形的面积

操作步骤

image.png
image.png

image.png

代码步骤

    // 计算多边形的面积,按照同一顺序进行叉乘
    private float Area()
    {
        // n = 5
        int n = m_points.Count;
        float A = 0.0f;
        Vector2 pval = Vector2.zero;
        Vector2 qval = Vector2.zero;
        for (int p = 0; p < n; p++)
        {
            pval = m_points[p];
            qval = m_points[(p + 1) % n];
            A += pval.x * qval.y - qval.x * pval.y;
        }
        return (A * 0.5f);
    }
        if (Area() > 0)
        {
            // 0 1 2 3 4
            for (int v = 0; v < n; ++v)
                V[v] = v;
        }
        else
        {
            // 将顶点顺序逆过来
            // 4 3 2 1 0
            for (int v = 0; v < n; ++v)
                V[v] = (n - 1) - v;
        }
        int nv = n;
        int count = nv;
        int u, w;
        // n边形的一个顶点出发只能引出(n-2)条对角线
        for (int v = nv - 1; nv >= 3;) 
        {
            // 形成不了三角形时会走这个return
            if ((count--) <= 0)
                return indices.ToArray();
                
            // 连续三个顶点 不超过nv
            u = v;
            v = u + 1;
            w = u + 2;

            u %= nv;
            v %= nv;
            w %= nv;
            
            // u v w连续三个顶点能组成三角形,且保证顺序是合理的
            if (Snip(u, v, w, nv, V))
            {
                int a, b, c;
                a = V[u];
                b = V[v];
                c = V[w];

                // 将顶点按照顺序放入list中
                indices.Add(a);
                indices.Add(b);
                indices.Add(c);

                // 做了代码修改,原文写法比较麻烦,将V数组中的值从后往前挪一位
                for (int s = v; s + 1 < nv; ++s)
                    V[s] = V[s + 1];

                nv--;
                // 原博文是 count = 2 * nv 但其实没必要
                count = nv;
            }
        }
    private bool Snip(int u, int v, int w, int n, int[] V)
    {
        Vector2 A = m_points[V[u]];
        Vector2 B = m_points[V[v]];
        Vector2 C = m_points[V[w]];

        // 面积如果小于Mathf.Epsilon(接近0的最小正浮点数),就为不能切分成三角形
        // AB向量叉乘AC向量结果为ABC三顶点形成封闭图形的面积的两倍,判断是否能形成三角形
        if (Mathf.Epsilon > ((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x))) 
            return false;
        for (int p = 0; p < n; ++p)
        {
            // 直到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;
    }