序
这是我在逛街时捡到的一篇外国朋友的文章,他用十分简短的伪代码解释了Delaunay Triangle的实现过程,这是我见过讲解Delaunay Triangle最棒的,最通俗易懂的文章。
文章
Delaunay三角剖分是1934年由俄国数学家BorisNikolaevich Delaunay(1890-1980)发明的,并以他的名字命名。它在科学和计算机图形学领域有着广泛的应用。它经常用于几何不规则分布的数据的图形表示--想想天气图或海拔图。它的3D变体在为电子游戏创造虚拟世界方面非常重要,还有许多其他的东西。
虽然乍一看,获得Delaunay三角剖分看起来几乎是微不足道的,但实际上这是一项相当复杂的任务,如果您想要对更多的点数进行有效的操作,那么情况就越复杂。许多巧妙的算法已经被设计出来,并且这个领域是正在进行的研究的主题。
我在互联网上搜索Delaunay三角剖分(以及密切相关的Voronoi或Direchlet图),发现了大量的算法。然而,他们中的大多数都不满意。有些是由数百行甚至数千行不完整的意大利面代码组成的。有些人太重了,他们有很多选择来创建带有特殊约束的三角。另一些则是以一种完全混淆底层方法的方式进行优化的。
因此,我最终创建了自己的实现。我在这里介绍它是因为,也许有一天,会有其他人需要Delaunay三角剖分。但也因为我认为这本身就是一个很好的有趣的问题。此外,它还展示了标准模板库(STL)的一些不错的技巧。
算法
整个算法在很大程度上依赖于Delaunay三角剖分的一个重要属性,即:除顶点外,任何三角形的外接圆上或内部都没有其他点。。换句话说,所有被限制的圆圈都是空的。
知道这一点,并认真思考,您可以设计以下方法,以添加一个顶点,一个已经存在的三角剖分(在伪代码)。
添加顶点(新顶点)
{
for(var v in 已有的三角形集合)
{
if(新顶点 是否在 v的外接圆内)
{
记录v的所有边到边集合
移除v
}
}
从边集合中移除所有双边
for(var 边 in 单边集合)
{
新三角形 = 每条边 + 新顶点 //生成一个新的三角形
已有的三角形集合.add(新三角形)
}
}
在图片中,它看起来如下:
1.插入一个新的顶点。 2.首先找到那些三角形的外接圆包含了这个顶点
3.移除这些三角形,但要记住他们的边缘。 4.删除双边,只保留唯一的边。
5.再剩余的边和顶点之间形成新的三角形。 6.最后的样子。
现在,你只需要找到一种开始整个过程的方法。在某种程度上,您应该首先创建一个有效的Delaunay三角剖分。然后,依次添加所有顶点。
这种有效的初始三角剖分很容易找到。你只是做了一个大的“超级三角形”,它包含了你所有的顶点。当然,这意味着会形成多余的三角形,但这些三角形很容易被移除。
因此,在伪码中,完整的算法相当于以下内容。
生成三角形()
{
创建一个超级三角形,其范围包含所有的顶点
把超级三角形,放入已有三角形集合
for (var v in 待插入顶点集合)
{
添加顶点(v)
}
for (var v in 已有的三角形集合)
{
if (v 包含了 超级三角形顶点任意顶点)
{
remove v
}
}
}
优化
通过沿水平轴对顶点进行预排序,可以对算法进行优化.然后,当它们的限定圆完全位于当前顶点的左侧时,就可以连续地呈现三角形“完成”。我实现了这个优化。对于少量的顶点,它的效果几乎不明显,但对于较大的数目,它是戏剧性的。我使用STD:设置容器,使其对象自动排序。
我还预先计算了圆周的中心和半径.这些优化不太重要,因为它们没有增强基本算法。然而,他们确实大大加快了速度。
不完美
算法并不完美。特别是,它不能很好地处理三个以上顶点位于一个圆上的情况。在调查了罗杰·拉布的评论之后,我发现了一条艰难的路,下面是。诚然,我在第一个版本中犯了一个错误,错误地假设没有两个三角形可以有相同的圆周。我纠正了这一点,但在不太可能的情况下,仍然存在一个问题,那就是许多顶点恰好位于同一个圆圈上。