使用耳切法将多边形三角化

效果图:

 

做法及原理可参考此链接:http://www.cnblogs.com/xignzou/p/3721494.html

 

代码:

using System;
using System.Collections.Generic;
using UnityEngine;

namespace PolygonTool
{

    #region 耳切法对简单多边形进行三角形化

    /// <summary>
    /// 判断凹点,凸点,耳朵的比较轴
    /// </summary>
    public enum CompareAxle
    {
        X,
        Y,
        Z
    }

    /// <summary>
    /// 对多边形处理
    /// </summary>
    public class Triangulation
    {

        /// <summary>
        /// 判断凹凸的时候的比对轴
        /// </summary>
        private CompareAxle _compareAxle = CompareAxle.Y;

        /// <summary>
        /// 多边形顶点
        /// </summary>
        private List<Vector3> _polygonVertexs = new List<Vector3>();

        /// <summary>
        /// 顶点序列
        /// </summary>
        private List<int> _vertexsSequence = new List<int>();

        /// <summary>
        /// 节点管理器
        /// </summary>
        private NodeManager _nodeManager = new NodeManager();

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="polygonVertexs">多边形顶点</param>
        public Triangulation(List<Vector3> polygonVertexs)
        {
            this._polygonVertexs = polygonVertexs;
            _nodeManager.Init(polygonVertexs);
        }

        /// <summary>
        /// 设置比较轴
        /// </summary>
        /// <param name="compareAxle"></param>
        public void SetCompareAxle(CompareAxle compareAxle)
        {
            this._compareAxle = compareAxle;
        }

        /// <summary>
        /// 获取三角形的顶点序列
        /// </summary>
        public int[] GetTriangles()
        {
            while (_nodeManager.LinkedListLength >= 3)
            {
                SplitResult sr = SplitPolygon();
                //
                if (sr == null)
                {
                    Debug.Log("null");
                    return null;
                }
            }

            return _vertexsSequence.ToArray();
        }

        /// <summary>
        /// 计算凹顶点,凸顶点,耳朵
        /// </summary>
        private SplitResult SplitPolygon()
        {
            //凹点
            List<Node> _concaveVertexs = new List<Node>();
            //凸点
            List<Node> _raisedVertexs = new List<Node>();
            //耳朵
            List<Node> _polygonEars = new List<Node>();
            //起始节点
            Node currentNode = _nodeManager.FirstNode;

            #region 计算凹顶点,凸顶点

            for (int i = 0; i < _nodeManager.LinkedListLength; i++)
            {
                Vector3 one = currentNode.vertex - currentNode.lastNode.vertex;
                Vector3 two = currentNode.nextNode.vertex - currentNode.vertex;
                Vector3 crossRes = Vector3.Cross(one, two);

                if (_compareAxle == CompareAxle.Y)
                {
                    if (crossRes.y > 0)
                    {
                        _concaveVertexs.Add(currentNode);
                    }
                    else
                    {
                        _raisedVertexs.Add(currentNode);
                    }
                }

                if (_compareAxle == CompareAxle.X)
                {
                    if (crossRes.x > 0)
                    {
                        _concaveVertexs.Add(currentNode);
                    }
                    else
                    {
                        _raisedVertexs.Add(currentNode);
                    }
                }

                if (_compareAxle == CompareAxle.Z)
                {
                    if (crossRes.z > 0)
                    {
                        _concaveVertexs.Add(currentNode);
                    }
                    else
                    {
                        _raisedVertexs.Add(currentNode);
                    }
                }

                _polygonEars.Add(currentNode);
                currentNode = currentNode.nextNode;
            }

            for (int i = 0; i < _concaveVertexs.Count; i++)
            {
                _polygonEars.Remove(_concaveVertexs[i]);
            }
            #endregion

            #region 计算耳朵
            List<int> needRemoveIdList = new List<int>();
            for (int i = 0; i < _polygonEars.Count; i++)
            {
                Node earNode = _polygonEars[i];
                Node compareNode = earNode.nextNode.nextNode;

                while (compareNode != earNode.lastNode)
                {
                    bool isIn = IsIn(compareNode.vertex, earNode.lastNode.vertex, earNode.vertex,
                        earNode.nextNode.vertex);

                    if (isIn == true)
                    {
                        if (_polygonEars.Contains(_polygonEars[i]))
                        {
                            needRemoveIdList.Add(_polygonEars[i].id);
                        }
                        break;
                    }
                    compareNode = compareNode.nextNode;
                }
            }

            for (int j = 0; j < needRemoveIdList.Count; j++)
            {
                for (int i = 0; i < _polygonEars.Count; i++)
                {
                    if (_polygonEars[i].id == needRemoveIdList[j])
                    {
                        _polygonEars.RemoveAt(i);
                    }
                }
            }

            #endregion

            #region 打印初始化结果

            Debug.Log("凸点");
            for (int i = 0; i < _raisedVertexs.Count; i++)
            {
                Debug.Log(_raisedVertexs[i].id);
            }

            Debug.Log("凹点");
            for (int i = 0; i < _concaveVertexs.Count; i++)
            {
                Debug.Log(_concaveVertexs[i].id);
            }

            Debug.Log("耳朵");
            for (int i = 0; i < _polygonEars.Count; i++)
            {
                Debug.Log(_polygonEars[i].id);
            }

            Debug.Log("-----------------------------------------------");
            #endregion

            //耳朵为空说明不是简单多边形 多边形三角化失败
            if (_polygonEars.Count == 0)
            {
                return null;
            }

            _vertexsSequence.Add(_polygonEars[0].lastNode.id);
            _vertexsSequence.Add(_polygonEars[0].id);
            _vertexsSequence.Add(_polygonEars[0].nextNode.id);
            _nodeManager.RemoveNode(_polygonEars[0]);


            return new SplitResult(_raisedVertexs, _concaveVertexs, _polygonEars);
        }

        /// <summary>
        /// 判断一点是否在三角形内
        /// </summary>
        /// <param name="p">一点</param>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="c"></param>
        /// <returns></returns>
        public bool IsIn(Vector3 p,Vector3 a,Vector3 b,Vector3 c)
        {
            Vector3 pa = p - a;
            Vector3 pb = p - b;
            Vector3 pc = p - c;

            Vector3 t1 = Vector3.Cross(pa, pb);
            Vector3 t2 = Vector3.Cross(pb, pc);
            Vector3 t3 = Vector3.Cross(pc, pa);

            bool isIn2 = t1.y >= 0 && t2.y >= 0 && t3.y >= 0 || t1.y <= 0 && t2.y <= 0 && t3.y <= 0;

            return isIn2;
        }

        /// <summary>
        /// 管理多边形 构成一个双向链表
        /// </summary>
        public class NodeManager
        {

            private List<Node> _nodeList = new List<Node>();

            public int LinkedListLength
            {
                get { return _nodeList.Count; }
            }

            public Node FirstNode
            {
                get { return _nodeList[0]; }
            }

            public void Init(List<Vector3> vertexs)
            {

                for (int i = 0; i < vertexs.Count; i++)
                {
                    Node node = new Node(i, vertexs[i]);
                    _nodeList.Add(node);
                }

                for (int i = 0; i < LinkedListLength; i++)
                {
                    if (i == 0)
                    {
                        _nodeList[i].lastNode = _nodeList[LinkedListLength - 1];
                        _nodeList[i].nextNode = _nodeList[1];
                    }
                    else if (i == LinkedListLength - 1)
                    {
                        _nodeList[i].lastNode = _nodeList[LinkedListLength - 2];
                        _nodeList[i].nextNode = _nodeList[0];
                    }
                    else
                    {
                        _nodeList[i].lastNode = _nodeList[i - 1];
                        _nodeList[i].nextNode = _nodeList[i + 1];
                    }
                }
            }

            public void RemoveNode(Node node)
            {
                _nodeList.Remove(node);
                node.lastNode.nextNode = node.nextNode;
                node.nextNode.lastNode = node.lastNode;
            }
        }

        public class Node
        {

            public int id;
            public Vector3 vertex;
            public Node lastNode;
            public Node nextNode;

            public Node(int id, Vector3 vertex)
            {
                this.id = id;
                this.vertex = vertex;
            }

            public Node(int id, Vector3 vertex, Node lastNode, Node nextNode)
            {
                this.id = id;
                this.vertex = vertex;
                this.lastNode = lastNode;
                this.nextNode = nextNode;
            }
        }

        public class SplitResult
        {
            /// <summary>
            /// 凸顶点
            /// </summary>
            public List<Node> raisedVertexs;

            /// <summary>
            /// 凹顶点
            /// </summary>
            public List<Node> concaveVertexs;

            /// <summary>
            /// 耳朵
            /// </summary>
            public List<Node> polygonEars;

            public SplitResult(List<Node> raisedVertexs, List<Node> concaveVertexs, List<Node> polygonEars)
            {
                this.raisedVertexs = raisedVertexs;
                this.concaveVertexs = concaveVertexs;
                this.polygonEars = polygonEars;
            }
        }
    }

    #endregion
}

接下来是测试代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using PolygonTool;

public class T : MonoBehaviour
{
    //用物体的坐标来代替点
    public List<Transform> tList;
    //计算得到的三角形序列下标
    private List<int> resultList = new List<int>();

    private Triangulation triangulation;

    private void Start()
    {
        tList.Reverse();

        List<Vector3> posList = new List<Vector3>();
        for (int i = 0; i < tList.Count; i++)
        {
            posList.Add(tList[i].position);
        }

        triangulation = new Triangulation(posList);

        triangulation.SetCompareAxle(CompareAxle.Y);

        int[] a = triangulation.GetTriangles();

        if (a != null)
        {
            for (int i = 0; i < a.Length; i++)
            {
                Debug.Log("===:" + a[i]);
                resultList.Add(a[i]);
            }
        }


        GameObject go = new GameObject();

        MeshFilter mf = go.AddComponent<MeshFilter>();

        go.AddComponent<MeshRenderer>();

        Mesh m = new Mesh();

        Vector3[] vertexs = new Vector3[a.Length];

        for (int i = 0; i < vertexs.Length; i++)
        {
            Vector3 v = tList[a[i]].position;
            vertexs[i] = v;
        }

        m.vertices = vertexs;
        
        int[] tri = new int[a.Length];

        for (int i = 0; i < tri.Length; i += 3)
        {
            tri[i] = i;
            tri[i + 1] = i + 2;
            tri[i + 2] = i + 1;
        }

        m.triangles = tri;

        mf.mesh = m;
    }

    private void OnDrawGizmos()
    {
        for (int i = 0; i < tList.Count; i++)
        {
            if (i < tList.Count - 1)
            {
                Gizmos.DrawLine(tList[i].position, tList[i + 1].position);
            }
            else
            {
                Gizmos.DrawLine(tList[i].position, tList[0].position);
            }

        }

        Gizmos.color = Color.black;

        for (int i = 0; i < resultList.Count; i+=3)
        {
            int startIndex = resultList[i];
            int endIndex = resultList[i + 2];
            Gizmos.DrawLine(tList[startIndex].position, tList[endIndex].position);
        }
    }

}

github地址:https://github.com/yiwei151/PolygonTriangulation

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
可以参考以下的 C 代码实现: ``` #include <stdio.h> #include <stdlib.h> #define MAX_POLYGON_VERTICES 1000 typedef struct { double x, y; } Point; typedef struct { int a, b, c; } Triangle; int earClipping(Point* polygon, int numVertices, Triangle* triangles) { // 初始三角剖分结果为空 int numTriangles = 0; // 每次剪掉一个“耳朵”后多边形的边数都会减少,最后只剩下 3 个点 while (numVertices > 3) { // 遍历所有顶点,找到一个“耳朵” for (int i = 0; i < numVertices; i++) { // 确定当前顶点 i 对应的边所在的两个顶点 int iPrev = (i + numVertices - 1) % numVertices; int iNext = (i + 1) % numVertices; // 判断顶点 i 是否是凸顶点 Point pi = polygon[i]; Point piPrev = polygon[iPrev]; Point piNext = polygon[iNext]; double crossProduct = (pi.x - piPrev.x) * (piNext.y - pi.y) - (piNext.x - pi.x) * (pi.y - piPrev.y); if (crossProduct >= 0) { continue; } // 判断顶点 i 是否成为凸耳朵 // 如果顶点 i 成为凸耳朵,需要确定凸耳朵的另外两个顶点 int isEar = 1; for (int j = 0; j < numVertices; j++) { if (j == i || j == iPrev || j == iNext) { continue; } if ((polygon[j].x - pi.x) * (piPrev.y - pi.y) + (piPrev.x - pi.x) * (pi.y - polygon[j].y) >= 0 && (piPrev.x - pi.x) * (piNext.y - pi.y) + (piNext.x - pi.x) * (pi.y - piPrev.y) >= 0 && (piNext.x - pi.x) * (polygon[j].y - pi.y) + (polygon[j].x - pi.x) * (pi.y - piNext.y) >= 0) { isEar = 0; break; } } if (!isEar) { continue; } // 找到一个凸耳朵,将它对应的三角形加入到三角剖分结果中 Triangle triangle = { iPrev, i, iNext }; triangles[numTriangles++] = triangle; // 从多边形中剪掉凸耳朵 i for (int j = i + 1; j < numVertices; j++) { polygon[j - 1] = polygon[j]; } numVertices--; i--; } } // 将多边形剩余的三个顶点构成最后一个三角形 Triangle triangle = { 0, 1, 2 }; triangles[numTriangles++] = triangle; return numTriangles; } int main() { Point polygon[MAX_POLYGON_VERTICES]; int numVertices; // 读取多边形顶点坐标 scanf("%d", &numVertices); for (int i = 0; i < numVertices; i++) { scanf("%lf%lf", &polygon[i].x, &polygon[i].y); } // 进行三角剖分 Triangle triangles[MAX_POLYGON_VERTICES]; int numTriangles = earClipping(polygon, numVertices, triangles); // 输出三角剖分结果 printf("%d\n", numTriangles); for (int i = 0; i < numTriangles; i++) { printf("%d %d %d\n", triangles[i].a, triangles[i].b, triangles[i].c); } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值