三维空间的三角剖分( 3D Delaunay Triangulated graph)第一部分:从二维空间的三角剖分做起

二维区域中Delaunay三角剖分:

1.将一个有限的二维平面空间划分为若干个三角形单元,每个三角形相邻且互不重叠。

2.每个三角形单元的顶点只能是相邻单元的顶点,每个三角形的外接圆内不包含其他三角形单元的顶点。

先上效果图(图为Unity中用Gizmos将每个三角形单元顶点连线画出来的预览图):

目录

一、要求

二、所需要自定义的类:

1.Edge类:存放三角形每条边的数据

2.Triangle类:存放每个三角形单元的数据

三、Delaunay 二维三角剖分过程

1.变量

2.剖分过程:


一、要求

输入:

    /// <summary>
    /// 二维空间区域中的点集
    /// </summary>
    public List<Vector3> _vertices = new List<Vector3>();

输出:

    /// <summary>
    /// 三角形列表
    /// </summary>
    List<Triangle> _triangle = new List<Triangle>();

二、所需要自定义的类:

1.Edge类:存放三角形每条边的数据

Edge类的结构很简单,只需要两个Vector3来定义一条线段。

    public class Edge
    {
        public Vector3 P1;
        public Vector3 P2;

        public Edge(Vector3 P1, Vector3 P2)
        {
            this.P1 = P1;
            this.P2 = P2;
        }

    }

2.Triangle类:存放每个三角形单元的数据

Triangle类的结构的参数较多:

首先是三个顶点(P1,P2,P3)和三条边(E,E2,E3),外接圆的圆心Center和半径R,和一个bool值isSame(用来判断该三角形是否多余)。

    public class Triangle
    {
        public Vector3 P1;
        public Vector3 P2;
        public Vector3 P3;
        public Edge E1;
        public Edge E2;
        public Edge E3;

        public Vector3 Center;
        public double R;

        public bool isSame;
    }

其次是一些函数:

(1).计算该三角形的外接圆:

        /// <summary>
        /// 计算三角形的外接圆
        /// </summary>
        public void GetTriangleExcenterRadius()
        {
            //same point
            if (P1 == P2 && P1 == P3)
            {
                R = 0;
                Center = P1;
                return;
            }
            float x1 = P1.x, x2 = P2.x, x3 = P3.x, y1 = P1.z, y2 = P2.z, y3 = P3.z;
            float C1 = (float)(Math.Pow(x1, 2) + Math.Pow(y1, 2) - Math.Pow(x2, 2) - Math.Pow(y2, 2));
            float C2 = (float)(Math.Pow(x2, 2) + Math.Pow(y2, 2) - Math.Pow(x3, 2) - Math.Pow(y3, 2));
            float centery = (C1 * (x2 - x3) - C2 * (x1 - x2)) / (2 * (y1 - y2) * (x2 - x3) - 2 * (y2 - y3) * (x1 - x2));
            float centerx = (C1 - 2 * centery * (y1 - y2)) / (2 * (x1 - x2));
            Center = new Vector3(centerx, 0, centery);
            R = GetDistance(P1, Center);
        }

(2).判断该三角形与点之间的空间关系:

        public bool isComtain(Vector3 node)
        {
            GetTriangleExcenterRadius();
            if ((node - Center).sqrMagnitude <= R*R)
                return true;
            else
                return false;
        }

 (3).判断是否存在包含超级三角形顶点的三角形:

        public void isSame(Triangle s)
        {
            Vector3[] super = new Vector3[3];
            super [0] = s.P1;
            super [1] = s.P2;
            super [2] = s.P3;
            if (super .Contains(P1) || super .Contains(P2) || super .Contains(P3))
                isBad = true;

        }

三、Delaunay 二维三角剖分过程

1.变量

    /// <summary>
    /// 输入点集
    /// </summary>
    public List<Vector3> _vertices = new List<Vector3>();

    /// <summary>
    /// 超级三角形
    /// </summary>
    Triangle SuperTriangle;

    /// <summary>
    /// 边列表
    /// </summary>
    List<Edge> _edges = new List<Edge>();

    /// <summary>
    /// 输出三角形列表
    /// </summary>
    List<Triangle> _triangle = new List<Triangle>();

2.剖分过程:

(1)第一步:对输入的点集进行排序(由于是二维,y均为0,只根据x,z进行排序):

_vertices = _vertices.OrderBy(o => o.x).ThenBy(o => o.z).ToList();
排序后的点集按照x值、z值从小到大排序(x权重大于z,这个权重调换也可以不影响),

(2)第二步:根据排序好的点集构建超级三角形(即包含点集中所有节点的三角形):

    /// <summary>
    /// 找到超级三角形
    /// </summary>
    /// <returns></returns>
    public Triangle FindSuper_Triangle(List<Vector3> _vertices)
    {
        Triangle super_Triangle=new Triangle(new Vector3(0,0,0),new Edge(new Vector3(1, 0, 1), new Vector3(-1, 0, 1)));
        Vector3 Circle;
        float Radius;
        float xmin = _vertices[0].x;
        float xmax = _vertices[_vertices.Count - 1].x;
        float zmin = _vertices[0].z;
        float zmax = _vertices[_vertices.Count - 1].z;
        foreach (var dot in _vertices)
        {
            if (zmin > dot.z)
                zmin = dot.z;
            if (zmax <= dot.z)
                zmax = dot.z;
            //zmin = Math.Min(zmin, dot.z);
            //zmax = Math.Max(zmax, dot.z);
        }
        Vector3 P1 = new Vector3(xmin, 0, zmin);
        Vector3 P2 = new Vector3(xmin, 0, zmax);
        Vector3 P3 = new Vector3(xmax, 0, zmax);
        Vector3 P4 = new Vector3(xmax, 0, zmin);

        Circle = (P1 + P3) / 2;
        Radius = Mathf.Sqrt((xmax - xmin) * (xmax - xmin) + (zmax - zmin) * (zmax - zmin));

        Vector3 sP1 = new Vector3(Circle.x, 0, Circle.y + Radius * 2);
        Vector3 sP2 = new Vector3(Circle.x - Radius * 2, 0, Circle.y - Radius);
        Vector3 sP3 = new Vector3(Circle.x + Radius * 2, 0, Circle.y - Radius);

        super_Triangle = new Triangle(sP1, new Edge(sP2, sP3));


        return super_Triangle;
    }

这里用到的方法比较笨,构造出的超级三角形非常大,但准确率能有一定的保证。

(3)第三步:终于要开始剖分啦!

剖分的过程就是从超级三角形开始,根据三角形内的点对三角形进行解构(这部分原理在网上已经有许多详细的图文讲解,这里就不多赘述了,上代码!)

先将超级三角形加入三角形列表,其三个顶点加入点集:

        _vertices.Add(SuperTriangle.P1);
        _vertices.Add(SuperTriangle.P2);
        _vertices.Add(SuperTriangle.P3);
        _triangle.Add(SuperTriangle);

开始遍历点集,对于每一个节点:

     判断三角形列表中的每个三角形是否包含该点,若包含,将该三角形的三条边加入边列表(AddEdge函数),然后从三角形列表中移除该三角形:

            int index = 0;
            while (index<_triangle.Count)
            {
                if (_triangle[index].isComtain(_vertices[i]))
                {
                    AddEdge(_edges, _triangle[index].E1);
                    AddEdge(_edges, _triangle[index].E2);
                    AddEdge(_edges, _triangle[index].E3);
                    _triangle.Remove(_triangle[index]);
                }
                else
                {
                    index++;
                }
            }

     (关于AddEdge函数:加入列表中不存在的边,特别要注意的一点就是若是要加入的边已经存在时,需将已存在的这条边移除,并在移除之后也不能加入该边:

    /// <summary>
    /// 添加边
    /// </summary>
    /// <param name="_edges"></param>
    /// <param name="E"></param>
    public void AddEdge(List<Edge> _edges,Edge E)
    {
        bool isAdd = true;
        int index = 0;
        while (index<_edges.Count)
        {
            if (_edges[index].P1 == E.P1 && _edges[index].P2 == E.P2 || _edges[index].P1 == E.P2 && _edges[index].P2 == E.P1)
            {
                _edges.Remove(_edges[index]);
                isAdd = false;
            }
            else
            {
                index++;
            }
        }
        if(isAdd)
        {
            _edges.Add(E);
        }
    }

      接着遍历边列表,对于边列表中的每条边,都与该点构成新的三角形单元存放入三角形列表中:

            foreach (var e in _edges)
            {
                Triangle Ttemp = new Triangle(_vertices[i], e);
                _triangle.Add(Ttemp);
            }

按以上过程遍历完点集之后,开始处理三角形列表和点集——记得我们一开始把超级三角形的三个顶点加入这两个列表里了吧,把它移除!

移除三角形列表中包含超级三角形顶点的三角形:

        int Tindex = 0;
        while (Tindex < _triangle.Count)
        {
            _triangle[Tindex].Bad(SuperTriangle);
            if (_triangle[Tindex].isBad)
                _triangle.Remove(_triangle[Tindex]);
            else
                Tindex++;
        }

最后,移除点集中的三个超级三角形顶点:

        _vertices.Remove(SuperTriangle.P1);
        _vertices.Remove(SuperTriangle.P2);
        _vertices.Remove(SuperTriangle.P3);

大功告成!遍历输出的三角形列表,就可以根据每个三角形的顶点将其画出来啦:

  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: Delaunay三角剖分是计算机图形学中常用的方法,它是将点云转化为无重叠的三角形集合的过程。对于三维点云而言,我们可以利用C++语言编写Delaunay三角剖分的源代码。 具体而言,我们需要借助第三方库来完成这个过程。例如,我们可以使用CGAL库中的Delaunay_triangulation_3类来实现三维点云的Delaunay三角剖分。在使用该类之前,我们需要将点云转化为一系列顶点,将顶点作为参数传入Delaunay_triangulation_3类的对象中。 在通过Delaunay_triangulation_3类计算Delaunay三角剖分后,我们可以通过遍历三角形集合,计算每个三角形的顶点坐标和法向量,从而得到三维点云的表面重建结果。 需要注意的是,Delaunay三角剖分的结果可能会产生“拟合问题”,即存在一些三角形的边缘与点云的表面重建结果不完全吻合。为了解决这个问题,我们可以使用一些优化方法,例如对三角形的边缘进行局部调整,以提高重建结果的精度。 总之,通过编写三维点云Delaunay三角剖分的源代码,我们可以将点云转化为一系列无重叠的三角形,从而实现三维模型的重建。 ### 回答2: 三维点云 delaunay 三角剖分是一种将无序的三维点云数据转化为三角形面片的方法,可以在三维建模、地质勘探等领域中应用。其源代码一般采用 C++ 编写,下面简单介绍其实现。 三维点云 delaunay 三角剖分主要分为以下几步: 1. 构建超级三角形。为了保证所有点都在三角剖分内部,需要在点云的边界之外添加一个超级三角形(一般为一个比点云面积大的等边三角形),保证所有点都在其内部。 2. 将点逐一插入。从点云中随机选取一个点开始,将其插入到当前三角剖分中。插入过程中会检查新插入点与其它三角形的关系,同时进行三角形翻转和边的反转等操作以维护 delaunay 三角剖分的特性。 3. 剖分收敛。当所有点都插入完成后,需要对剩余的三角形进行处理,将所有与超级三角形相交的三角形删除,以得到最终的三角剖分结果。 其源代码主要包括点的数据结构定义、超级三角形的构建、插入点和剖分收敛等函数的实现。在实现中需要注意,对于边界点或重复点等特殊情况需要进行处理,同时可根据具体应用场景做出一些优化。 ### 回答3: 三维点云的Delaunay三角剖分源代码是一个算法实现,可以将一个三维点云数据集转化为一组无重叠三角形的连接。这个算法通常由C++实现,并且主要包含以下步骤: 首先,需要定义一个三维点云数据结构,用于存储所有的点。然后,通过半边数据结构来表示三角形的连接关系,并创建一个起始三角形,该三角形的外接圆可以囊括所有的点。 接下来,使用一个扫描线算法来生成三角剖分。该算法主要通过在扫描线上移动,并利用拐角点的概念来不断更新Delaunay三角形网格。在每个点上,都会查找在当前点的左侧和右侧的最高顶点,并通过一个旋转操作来更新三角形的连接关系。 通过以上步骤,就可以生成一个Delaunay三角剖分,其中每个三角形都与它的外接圆不包含任何点。这个算法在计算几何和计算机图形学领域十分流行,并且有多种优化和扩展,可以更好地满足具体应用的需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值