三角剖分
: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边:假设E中的一条边e(两个端点为a,b),e若满足下列条件,则称之为Delaunay边:存在一个圆经过a,b两点,圆内(注意是圆内,圆上最多三点共圆)不含点集V中任何其他的点,这一特性又称空圆特性。
【定义】Delaunay三角剖分:如果点集V的一个三角剖分T只包含Delaunay边,那么该三角剖分称为Delaunay三角剖分。
要满足Delaunay三角剖分的定义,必须符合两个重要的准则:
1、空圆特性:Delaunay三角网是唯一的(任意四点不能共圆),在Delaunay三角形网中任一三角形的外接圆范围内不会有其它点存在。如下图所示:
2、最大化最小角特性:在散点集可能形成的三角剖分中,Delaunay三角剖分所形成的三角形的最小角最大。从这个意义上讲,Delaunay三角网是“最接近于规则化的“的三角网。具体的说是指在两个相邻的三角形构成凸四边形的对角线,在相互交换后,六个内角的最小角不再增大。如下图所示:
以下是Delaunay剖分所具备的优异特性:
理论上为了构造Delaunay三角网,Lawson提出的局部优化过程LOP(Local Optimization Procedure),一般三角网经过LOP处理,即可确保成为Delaunay三角网,其基本做法如下所示:
LOP处理过程如下图所示:【下图左的△BCD的外接圆内包含A点,则执行3,执行后,下图右】
Delaunay剖分是一种三角剖分的标准,实现它有多种算法。
由表可以看出,三角网生成法的时间效率最低,分治算法的时间效率最高,逐点插入法效率居中。由于区域生长法本质的缺陷,导致其效率受限,这种方法在80年代中期以后已经很少使用。分治算法时间效率相对较高,但是由于其递归执行,所以需要较大的内存空间,导致其空间效率较低。此外,分治法的数据处理及结果的优化需要的工作量也比较大。逐点插入算法实现简单,时间效率比较高,而运行占用的空间也较小,从时间效率和空间效率综合考虑,性价比最高,因而应用广泛。
1977年,Lawson提出了逐点插入法建立Delaunay三角网的算法思想。之后Lee和Schachlter, Bowyer, Watson, Sloan,先后进行了发展和完善。他们的算法在初始化三角网的建立方法、定位点所在三角形的过程、以及插入的过程方面各具特点。
如:Lawson算法、Bowyer-Watson算法。
逐点插入的Lawson算法是Lawson在1977年提出的,该算法思路简单,易于编程实现。基本原理为:1️⃣首先建立一个大的三角形或多边形,把所有数据点包围起来,向其中插入一点,该点与包含它的三角形三个顶点相连,形成三个新的三角形;2️⃣然后逐个对它们进行空外接圆检测,同时用Lawson设计的局部优化过程LOP进行优化,即通过交换对角线的方法来保证所形成的三角网为Delaunay三角网。
优点:上述基于散点的构网算法理论严密、唯一性好,网格满足空圆特性,较为理想。由其逐点插入的构网过程可知,遇到非Delaunay边时,通过删除调整,可以构造形成新的Delaunay边。在完成构网后,增加新点时,无需对所有的点进行重新构网,只需对新点的影响三角形范围进行局部联网,且局部联网的方法简单易行。同样,点的删除、移动也可快速动态地进行。
缺点:但在实际应用当中,这种构网算法当点集较大时构网速度也较慢,如果点集范围是非凸区域或者存在内环,则会产生非法三角形。
逐点插入算法中的Bowyer-Watson算法的基本步骤是:
构造一个超级三角形,包含所有散点,放入三角形链表。
将点集中的散点依次插入,在三角形链表中找出其外接圆包含插入点的三角形(称为该点的影响三角形),删除影响三角形的公共边,将插入点同影响三角形的全部顶点连接起来,从而完成一个点在Delaunay三角形链表中的插入。
根据优化准则对局部新形成的三角形进行优化。将形成的三角形放入Delaunay三角形链表。
循环执行上述第2步,直到所有散点插入完毕。
不规则三角网(Triangulated Irregular Network ,TIN):是用一系列互不交叉、互不重叠的连接在一 起的三角形来表示地形表面。TIN既是矢量结构又有栅格的空间铺盖特征,能很好地描述和维护空间关系。
T:三角化( Triangulated ) 是离散数据的三角剖分过程,也是TIN的建立过程。位于三角形内的任意一点的高程值均可以通过三角形平面方程唯一确定。
I:不规则性( Irregular ) , 指用来构建TIN的采样点的分布形式。TIN具有可变分辨率,比格网DEM能更好反映地形起伏。
N:网 ( Network ) , 表达整个区域的三角形分布形态,即三角形之间不能交叉和重叠。三角形之间的拓扑关系隐含其中。
用来进行TIN构建的原始数据 根据数据点之间的约束条件可分为无约束数据域和约束数据域两种类型。
约束数据域 的例子:
TIN的三角剖分准则:是指TIN中三角形的形成法则,它决定着三角形的几何形状和
TIN的质量。
目前,在GIS、计算机和图形学领域常用 的三角剖分准则有6种。
说明:
Delaunay
三角网1)三角网生长算法:递归生长算法、凸闭包收缩法
1、首先,对于样本中的点集进行排序,在这里以x
坐标从小到大进行排序(也可以按照y
坐标)。放入数组_vertices
中。
2、然后,需要构造出一个超级三角形,超级三角形要能够将样本中的点全都包含在其内(不能再其边上)。并将超级三角形存入 三角形列表_triangles
中。并将超级三角形的三边存入polygon
(是用来存储临时新产生的边)中。
3、然后开始对_vertices
中的点进行遍历,如果该点在_triangles
中三角形的外接圆内(在圆上也相当于在圆内)时,则需要将这些三角形从列表中删除,然后将当前点连接刚刚删除的三角形的三个顶点,从而形成三个新的三角形,并将这三个新三角形加入列表_triangles
中。
4、当对样本点集中的点遍历完之后,还需要将第二步中所构造的超级三角形删除(因为超级三角形的三个顶点不属于样本点集中的点)。最终形成的列表_triangles
就是三角剖分的三角网了。
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的基本元素,可知分为三类:顶点、边、面(三角形)。其结构可简单示意为:
点类型构造函数:具有(x,y)坐标值
边类型构造函数有:边的两端的端点(顶点)的坐标、标记该边是否是好的的标记isbad.
三角形类构造函数有:三个顶点的坐标信息、标记该三角形是否是好的的标记isbad
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 、 xmin
、ymax 、 ymin
、xmid、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.
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 的功能。
//该段代码写的可读性不强,其实该段代码用数学表达就是下面这样子的:
其他方法:
源码中,main.cpp中开始随机生成点集时,所用到的概念:
随机数种子: 随机数就是就随机数种子中取出的数。种子就是个序号,这个序号交给一个数列管理器,通过这个序号,你从管理器中取出一个数列,这个数列就是你通过那个序号得到的随机数。 也就是说种子和随机数列是一一对应的。{An}=f(x), x 就是种子,F()是算法,{An}是数列,这个数列看上去是随机的,这是因为An的通项很复杂。