逐点插入法-delaunay三角剖分

参考资料:

三角剖分
:https://baike.baidu.com/item/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86%E7%AE%97%E6%B3%95?tp=2_11
TIN: https://wenku.baidu.com/view/42d4a49580c758f5f61fb7360b4c2e3f572725d0.html

逐点插入法三角剖分: http://paulbourke.net/papers/triangulate/(英文文章)
和 http://blog.sina.com.cn/s/blog_15ff6002b0102xxxf.html
和 https://blog.csdn.net/qq_34719188/article/details/83216527

三角剖分:

三角剖分是一种研究方法。三角剖分≠TIN

三角剖分是代数拓扑学里最基本的研究方法。 以曲面为例, 我们把曲面剖开成一块块碎片,要求满足下面条件: (1)每块碎片都是曲边三角形; (2)曲面上任何两个这样的曲边三角形,要么不相交,要么恰好相交于一条公共边(不能同时交两条或两条以上的边)。

而**TIN**是:不规则三角网,当在建立TIN的时候,用到三角剖分的方法。


假设V是二维实数域上的有限点集,边e是由点集中的点作为端点构成的封闭线段, E为e的集合。那么该点集V的一个三角剖分T=(V,E)是一个平面图G,该平面图满足条件:

1.除了端点,平面图中的边不包含点集中的任何点。

2.没有相交边。

3.平面图中所有的面都是三角面,且所有三角面的合集是散点集V的凸包。

Delaunay三角剖分

在实际中运用的最多的三角剖分是Delaunay三角剖分,它是一种特殊的三角剖分。先从Delaunay边说起:

【定义】Delaunay边:假设E中的一条边e(两个端点为a,b),e若满足下列条件,则称之为Delaunay边:存在一个圆经过a,b两点,圆内(注意是圆内,圆上最多三点共圆)不含点集V中任何其他的点,这一特性又称空圆特性

【定义】Delaunay三角剖分:如果点集V的一个三角剖分T只包含Delaunay边,那么该三角剖分称为Delaunay三角剖分。

.1)Delaunay三角剖分的准则:

要满足Delaunay三角剖分的定义,必须符合两个重要的准则:

1、空圆特性:Delaunay三角网是唯一的(任意四点不能共圆),在Delaunay三角形网中任一三角形的外接圆范围内不会有其它点存在。如下图所示:
逐点插入法-delaunay三角剖分_第1张图片
2、最大化最小角特性:在散点集可能形成的三角剖分中,Delaunay三角剖分所形成的三角形的最小角最大。从这个意义上讲,Delaunay三角网是“最接近于规则化的“的三角网。具体的说是指在两个相邻的三角形构成凸四边形的对角线,在相互交换后,六个内角的最小角不再增大。如下图所示:
在这里插入图片描述

.2)Delaunay三角剖分的特性:

以下是Delaunay剖分所具备的优异特性:

  1. 最接近以最近邻的三点形成三角形,且各线段(三角形的边)皆不相交。
  2. 唯一性:不论从区域何处开始构建,最终构造出的结果只有一种(前提是不存在四点共圆的情况)。
  3. 最优性:任意两个相邻三角形形成的凸四边形的对角线如果可以互换的话,那么两个三角形六个内角中最小的角度不会变大
  4. 最规则:如果将三角网中的每个三角形的最小角进行升序排列,则Delaunay三角网的排列得到的数值最大。
  5. 区域性:新增、删除、移动某一个顶点时只会影响临近的三角形。
  6. 具有凸多边形的外壳:三角网最外层的边界形成一个凸多边形的外壳。

.3)局部优化处理:

理论上为了构造Delaunay三角网,Lawson提出的局部优化过程LOP(Local Optimization Procedure),一般三角网经过LOP处理,即可确保成为Delaunay三角网,其基本做法如下所示:

  1. 将两个具有共同边的三角形合成一个多边形。
  2. 以最大空圆准则作检查,看其第四个顶点是否在三角形的外接圆之内。
  3. 如果在,修正对角线即将对角线对调,即完成局部优化过程的处理。

LOP处理过程如下图所示:【下图左的△BCD的外接圆内包含A点,则执行3,执行后,下图右】
逐点插入法-delaunay三角剖分_第2张图片

.4)实现Delaunay三角剖分的算法:

Delaunay剖分是一种三角剖分的标准,实现它有多种算法。

逐点插入法-delaunay三角剖分_第3张图片

实现Delaunay三角剖分的算法

由表可以看出,三角网生成法的时间效率最低,分治算法的时间效率最高,逐点插入法效率居中。由于区域生长法本质的缺陷,导致其效率受限,这种方法在80年代中期以后已经很少使用。分治算法时间效率相对较高,但是由于其递归执行,所以需要较大的内存空间,导致其空间效率较低。此外,分治法的数据处理及结果的优化需要的工作量也比较大。逐点插入算法实现简单,时间效率比较高,而运行占用的空间也较小,从时间效率和空间效率综合考虑,性价比最高,因而应用广泛。

1977年,Lawson提出了逐点插入法建立Delaunay三角网的算法思想。之后Lee和Schachlter, Bowyer, Watson, Sloan,先后进行了发展和完善。他们的算法在初始化三角网的建立方法、定位点所在三角形的过程、以及插入的过程方面各具特点。

如:Lawson算法Bowyer-Watson算法

1、Lawson算法

逐点插入的Lawson算法是Lawson在1977年提出的,该算法思路简单,易于编程实现。基本原理为:1️⃣首先建立一个大的三角形或多边形,把所有数据点包围起来,向其中插入一点,该点与包含它的三角形三个顶点相连,形成三个新的三角形;2️⃣然后逐个对它们进行空外接圆检测,同时用Lawson设计的局部优化过程LOP进行优化,即通过交换对角线的方法来保证所形成的三角网为Delaunay三角网。

  • 优点:上述基于散点的构网算法理论严密、唯一性好,网格满足空圆特性,较为理想。由其逐点插入的构网过程可知,遇到非Delaunay边时,通过删除调整,可以构造形成新的Delaunay边。在完成构网后,增加新点时,无需对所有的点进行重新构网,只需对新点的影响三角形范围进行局部联网,且局部联网的方法简单易行。同样,点的删除、移动也可快速动态地进行。

  • 缺点:但在实际应用当中,这种构网算法当点集较大时构网速度也较慢,如果点集范围是非凸区域或者存在内环,则会产生非法三角形。

    ​ 如下图所示:当离散点集构成圆环时,Lawson算法产生的非法三角形
    逐点插入法-delaunay三角剖分_第4张图片

​ 生成的非法三角形:
逐点插入法-delaunay三角剖分_第5张图片

​ 正确的生成为:
逐点插入法-delaunay三角剖分_第6张图片

2、Bowyer-Watson算法

逐点插入算法中的Bowyer-Watson算法的基本步骤是:

  1. 构造一个超级三角形,包含所有散点,放入三角形链表。

  2. 将点集中的散点依次插入,在三角形链表中找出其外接圆包含插入点的三角形(称为该点的影响三角形),删除影响三角形的公共边,将插入点同影响三角形的全部顶点连接起来,从而完成一个点在Delaunay三角形链表中的插入。

  3. 根据优化准则对局部新形成的三角形进行优化。将形成的三角形放入Delaunay三角形链表。

  4. 循环执行上述第2步,直到所有散点插入完毕。

    ​ 这一算法的关键的第2步图示如下:
    逐点插入法-delaunay三角剖分_第7张图片

#-----------------------------------------------------------#

TIN

TIN的概念

不规则三角网(Triangulated Irregular Network ,TIN):是用一系列互不交叉、互不重叠的连接在一 起的三角形来表示地形表面。TIN既是矢量结构又有栅格的空间铺盖特征,能很好地描述和维护空间关系。
逐点插入法-delaunay三角剖分_第8张图片

T:三角化( Triangulated ) 是离散数据的三角剖分过程,也是TIN的建立过程。位于三角形内的任意一点的高程值均可以通过三角形平面方程唯一确定。

I:不规则性( Irregular ) , 指用来构建TIN的采样点的分布形式。TIN具有可变分辨率,比格网DEM能更好反映地形起伏。

N:网 ( Network ) , 表达整个区域的三角形分布形态,即三角形之间不能交叉和重叠。三角形之间的拓扑关系隐含其中。

TIN的基本元素

  • 节点 (Node): 是相邻三角形的公共顶点, 也是用来构建TIN的釆样数据;
  • 边 (Edge): 指两个三角形的公共边界,是TIN不光滑性的具体反映。边同时还包含特征线、断裂线以及区域边界。
  • 面 (Face): 由最近的三个节点所组成的三角形面,是TIN描述地形表面的基本单元。TIN中的每一个三角形都描述了局部地形倾斜状态,具有唯一的坡度值。三角形在公共节点和边上是无缝的,或者说三角形不能交叉和重叠。

构建TIN的原始数据类型

用来进行TIN构建的原始数据 根据数据点之间的约束条件可分为无约束数据域约束数据域两种类型。

  • 无约束数据域:是指数据点之间不存在任何关系,数据分布完全呈离散状态,数据点之间在物理上相互独立。
  • 约束数据域:则是部分数据点之间存在着某种联系,这种联系一般通过线性特征来维护,如地形数据中的山脊线、山谷线上的点等。

约束数据域 的例子:

​ 【狭窄的海岸线】:就是约束数据,因为不能把海岸两侧的点进行连接。
逐点插入法-delaunay三角剖分_第9张图片

TIN的三角形的形状的要求

  1. 三角形的格网唯一;
  2. 最佳三角形形状,尽量接近正三角形;
  3. 三角形边长之和最小,保证最近的点形成三角形。

TIN的三角剖分准则

TIN的三角剖分准则:是指TIN中三角形的形成法则,它决定着三角形的几何形状和
TIN的质量。

目前,在GIS、计算机和图形学领域常用 的三角剖分准则有6种。

  • 空外接圆准则:在TIN中,过每个三角形的外接圆均不包含点集的
    其余任何点。
  • 最大最小角准则:在TIN中的两相邻三角形形成的凸四边形中,这两三角形中的最小内角一定大于交换凸四边形对角线后所形成的两三
    角形的最小内角;
  • 最短距离和准则:指一点到基边的两端的距离和为最小。
  • 张角最大准则:一点到基边的张角为最大。
  • 面积比准则:三角形内切圆面积与三角形面积或三角形面积与周长平方之比最小。
  • 对角线准则:两三角形组成的凸四边形的两条对角线之比。这一准则的比值限定值,须给定,即当计算值超过限定值才进行优化。

逐点插入法-delaunay三角剖分_第10张图片
逐点插入法-delaunay三角剖分_第11张图片

说明:

逐点插入法-delaunay三角剖分_第12张图片

关于Delaunay三角网

逐点插入法-delaunay三角剖分_第13张图片

TIN的建立方法

  1. 无约束散点域三角剖分算法与实现
  2. 约束散点数据域三角剖分算法与实现
  3. 基于等高线数据的TIN的建立
  4. 基于栅格数据的三角网建立

无约束散点域的三角剖分算法与实现:

1)三角网生长算法:递归生长算法、凸闭包收缩法

基于等高线数据的TIN的建立:

  • 1)等高线离散点直接生成TIN
  • 2)将等高线作为特征线的方法;
  • 3)自动增加特征点及优化TIN的方法。

#---------------------------------#

基于逐点插入算法进行三角剖分

逐点插入法算法思想:

1、首先,对于样本中的点集进行排序,在这里以x坐标从小到大进行排序(也可以按照y坐标)。放入数组_vertices中。

2、然后,需要构造出一个超级三角形,超级三角形要能够将样本中的点全都包含在其内(不能再其边上)。并将超级三角形存入 三角形列表_triangles中。并将超级三角形的三边存入polygon(是用来存储临时新产生的边)中。

3、然后开始对_vertices中的点进行遍历,如果该点在_triangles中三角形的外接圆内(在圆上也相当于在圆内)时,则需要将这些三角形从列表中删除,然后将当前点连接刚刚删除的三角形的三个顶点,从而形成三个新的三角形,并将这三个新三角形加入列表_triangles中。

4、当对样本点集中的点遍历完之后,还需要将第二步中所构造的超级三角形删除(因为超级三角形的三个顶点不属于样本点集中的点)。最终形成的列表_triangles就是三角剖分的三角网了。

伪代码:

这一份伪代码是参考如下图所示的文章所写的:
逐点插入法-delaunay三角剖分_第14张图片

subroutine triangulate
input : vertex list   输入:顶点链表
output : triangle list  输出:三角形链表
initialize the triangle list  初始化三角形链表
determine the supertriangle  确定超级三角形
add supertriangle vertices to the end of the vertex list   将超级三角形的顶点加入到顶点链表中
add the supertriangle to the triangle list  将超级三角形加入到三角形链表中
for each sample point in the vertex list  对顶点链表中的每一个点
initialize the edge buffer  初始化边数组
for each triangle currently in the triangle list  对于三角形链表中的每一个三角形
calculate the triangle circumcircle center and radius  计算出外接圆圆心和半径
if the point lies in the triangle circumcircle then  如果这个点在三角形的外接圆内
add the three triangle edges to the edge buffer  把这个三角形的三条边加入到边数组中
remove the triangle from the triangle list  从三角形链表中将这个三角形删除
endif
endfor
delete all doubly specified edges from the edge buffer  把边数组中所有重复的边都删除,注意这里是把所有的重复边都删除,比如有边e1,e2,e2,e3,删除后应该只剩e1和e3
this leaves the edges of the enclosing polygon only  这步会得到一个闭合的多边形
add to the triangle list all triangles formed between the point 用这个点和边数组中的每一条边都组合成一个三角形,加入到三角形链表中
and the edges of the enclosing polygon
endfor
remove any triangles from the triangle list that use the supertriangle vertices从三角形链表中删除使用超级三角形顶点的三角形
remove the supertriangle vertices from the vertex list将超级三角形的顶点从顶点链表中删除
end

作者:earthwjl
链接:https://www.jianshu.com/p/172749e6116a
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

源码部分:

源码参考: https://blog.csdn.net/qq_34719188/article/details/83216527#font_colorff4a45_C_font_28

首先按照TIN的基本元素,可知分为三类:顶点、边、面(三角形)。其结构可简单示意为:

  • 顶点(vertex):P(x,y)
  • 边(edge): e1: [ P1(x1,y1) ,P2(x2,y2) ]
  • 面(三角, triangle): P1 、 P2 、 P3 ;e1、e2、e3

代码框架:

  • 定义点类

点类型构造函数:具有(x,y)坐标值

  • 定义变类

边类型构造函数有:边的两端的端点(顶点)的坐标、标记该边是否是好的的标记isbad.

  • < triangle.h> 定义三角形类

三角形类构造函数有:三个顶点的坐标信息、标记该三角形是否是好的的标记isbad

  • 定义三角剖分法类
  • < numeric.h> 定义比较类(用于三角退化)
  • 定义主函数

main.cpp实现流程:

1、首先,定义 RandomFloat()函数,利用rand()函数生成随机的二维点,存放在points[]数组中;

2、然后,将这些点带入triangulate()函数中,该函数是Deluanay 三角剖分核心算法 — 逐点插入法

1、先将[带有(x,y)坐标的点points]拷贝一份;

2、然后开始计算三角形的上下左右边界,目的是为了建立一个超级三角形。建立完之后,将超级三角形放入 _triangles【存储三角形(当整个三角剖分核心算法执行完毕之后)它存储的才是最终生成的三角网,在此之前有可能还会删除或增加】

3、接下来开始依次遍历每个点,建立三角形:

​ (1)先定义一个临时变量polygon,来存储临时新产生的边;

​ (2)用circumCircleContains()函数来检测点p是否在该三角形外接圆的内部;如果有三角形内部包含就需要将该三角形从_triangles中剔除,然后就形成三个单独的边(其实边一直都在,只是在 _triangles中不再是一个三角形了。)

​ (3)此时需要将重复的边删除,并更新一下边polygon的存储。

至此,这就是该算法的流程

3、三角剖分法-----逐点插入法 将随机生成的点建立三角网之后,还需要将超级三角形删除(因为超级三角形的三个顶点不是随机生成的点,这个超级三角形是人为的一个三角形,它不属于三角网)。

4、注意:该程序还是用了【SFML

SFML 是多媒体库,它为PC的各个组件提供简单的界面,用来简化游戏和多媒体应用程序的开发。 主要由五个模块组成,分别是:系统,窗口,图形,音频和网络。

SFML在vs 2013中的配置环境: https://blog.csdn.net/github_35735591/article/details/73485598

用该库中的函数 sf::RenderWindow window() 创建主窗口;

实现细节:

1、构造超级三角形?

首先,计算出点集的坐标范围的边界,即:xmax 、 xminymax 、 yminxmid、ymid,继续计算出坐标范围的dmax = (dx > dy) ? dx : dy;

然后计算超级三角形的三个顶点:

//最左边的顶点
super_triangle.x = xmid - 20 * dmax; 
super_triangle.y = ymid - dmax;
//最上面的顶点
super_triangle.x = xmid;
super_triangle.y = ymid + 20 * dmax;
//最右边的顶点
super_triangle.x = xmid + 20 * dmax;
super_triangle.y = ymid - dmax;

从而,超级三角形就计算出来了。

2、如何求三角形的外接圆的圆心、半径?

求三角形外接圆圆心坐标: https://blog.csdn.net/ztf312/article/details/89197684

这得看已知条件是什么.第一种是如果已知条件是三角形三个顶点的坐标,那么最直接的方法是根据三角形外接圆的定义来求:其圆心是三角形各边垂直平分线交点(外心),其半径是该点到任一顶点的距离.先写出任意两条边的直线方程,再换算出该两条边垂直平分线的方程,然后计算交点即为圆心坐标,最后计算圆心与某一顶点的距离,就可以直接写出圆方程.
第二种方法是,设圆心为(x,y),那么写出它与三个顶点距离的三条表达式,组成一个二元二次方程组,求解出x和y.

用行列式值法求解二元一次方程:
逐点插入法-delaunay三角剖分_第15张图片

所以,可以这样求:
逐点插入法-delaunay三角剖分_第16张图片
【红色部分是笔者不小心写错了的】

其实写的更直观一点就是这样:
逐点插入法-delaunay三角剖分_第17张图片

const T circum_x = (ab * (p3.y - p2.y) + cd * (p1.y - p3.y) + ef * (p2.y - p1.y)) / 2*(p1.x * (p3.y - p2.y) + p2.x * (p1.y - p3.y) + p3.x * (p2.y - p1.y));//第一句
const T circum_y = (ab * (p3.x - p2.x) + cd * (p1.x - p3.x) + ef * (p2.x - p1.x)) / (p1.y * (p3.x - p2.x) + p2.y * (p1.x - p3.x) + p3.y * (p2.x - p1.x)); //第二句

const VertexType circum(half(circum_x), half(circum_y));//half()是除以2 的功能。
//该段代码写的可读性不强,其实该段代码用数学表达就是下面这样子的:

第一句:逐点插入法-delaunay三角剖分_第18张图片

第二句:
逐点插入法-delaunay三角剖分_第19张图片

其他方法:

  • 当三角形的三个顶点中,有两个顶点的x坐标或者y坐标一样时,计算三角形外接圆的圆心:
    逐点插入法-delaunay三角剖分_第20张图片

  • 一般情况的,另一种求外接圆圆心的方法:

逐点插入法-delaunay三角剖分_第21张图片

######################################################

源码中,main.cpp中开始随机生成点集时,所用到的概念:

随机数种子: 随机数就是就随机数种子中取出的数。种子就是个序号,这个序号交给一个数列管理器,通过这个序号,你从管理器中取出一个数列,这个数列就是你通过那个序号得到的随机数。 也就是说种子和随机数列是一一对应的。{An}=f(x), x 就是种子,F()是算法,{An}是数列,这个数列看上去是随机的,这是因为An的通项很复杂。

你可能感兴趣的:(计算几何)