Voronoi图和Delaunay三角形和凸包


 写一下最近写的一点东西...

最近在上算法课,上课老师讲到了维诺图,自己觉得很有意思就研究了一下,并用java写了一个简单的se程序用来显示维诺图和Delaunay三角形还有凸包。

首先介绍一下凸包,凸包在数学里是很常见的,给定一些点,然后找出包含这些的最小凸包,一般是这么做的。至于凸包的定义维基百科或者百度都行,自己查一查就知道,通俗的讲就是延长每一条边,剩下的图形总在边的一边。

(界面有点丑)

这边给出一下我程序运行出来的凸包

然后是Delaunay三角形,这个三角形是这样的,可以在凸包里面进行插入点,然后连接各个节点和插入点,然后会形成三角形,再插入还会再次分割三角形,并且形成的三角形是不包括第四个点的。

Voronoi图在上述三角形的基础上求得每一个三角形外接圆的圆心,连接各个这样的圆心可以得到这么一个voronoi图。

 

好了这是我的效果展示图,具体代码我上传到Github上供大家学习参考,有些一点注释,但因为写的比较匆忙不是写的很详细,大家有问题可以一起讨论讨论。

Github地址:https://github.com/monster5475/JAVACore/tree/master/

接下来说一下我做这个过程中遇到的一些问题和一些事情,首先要感谢一下给我帮助蛮多的几篇博客。

https://www.cnblogs.com/zhiyishou/p/4430017.html 这篇主要讲三角形的切割形成

https://blog.csdn.net/u012328159/article/details/50808360 这篇主要讲用graphScan的方法形成的凸包

https://blog.csdn.net/gdut2015go/article/details/48208983 这是一个参考博客。

https://blog.csdn.net/k346k346/article/details/52244123 这是我主要参考的,按照实现的博客,这个博客写的挺好的,我基本参考这个思路走的。

忘了补充一下Bowyer-Watson算法,我还查看《基于Bowyer_Watson三角网生成算法的研究_周雪梅》这篇文章中的一些伪代码。

真的是前人栽树,后人乘阴,感谢前辈!


现在来介绍一下主要思路

(1)首先是java窗体设计,以及一些按钮的绑定什么的,画图,大概一天完成的,窗体画图有点忘了。

(2)然后是第一步随机生成点函数,这个没什么难度,主要要注意不要生成重复点,然后不要超过边界。(一开始以为会存在画图x y和现实中x y的差距,后来发现不用考虑)

(3)得到一个point的arraylist,先找到y最低点,得到一个扇形图类似如此

然后p0就一定是凸包上的一个点,p1点也必定是凸包上的一点,然后将这两个点进栈,开始判断其他点,这边首先要了解一下叉乘的概念,向量a叉乘向量b,得到的结果向量是垂直于向量a和b组成的平面,方向是根据a和b的叉乘的顺序根据右手法则判断的,那么我们就可以通过叉乘的结果的正负来确定点的位置比如在向量p0p1 和向量p1p2的叉乘中(注意叉乘首先要让两个向量是同一个起点),根据右手法则得到的就是一个垂直纸面向外的向量,那么就可以说明点p2在向量p0p1的逆时针一边。如果p2在p0p1在的顺时针一边,则叉乘结果应该是垂直纸面向内的向量。根据这一点,我们就可以一个一个判断,比如p0p1判断完p2是我们要的点,那么进栈,这个时候栈中元素为(p0,p1,p2)p2位栈顶元素,这时候我们再连接p2p3,用向量p1p2(栈顶的两个点)叉乘p2p3,发现结果向量垂直纸面向内,那么点p3是在p1p2的顺时针的方向,那么就跟我们凸包的定义出错,因为我们在之前说了凸包上的所有点会在边的同一侧,现在p3在顺时针一边,p0在逆时针一边,所以p2不是凸包上的一点,将p2出栈,p3入栈,如此如此 直到最后。。这个是Graham扫描法,因为我们一开始选定是从逆时针方向,所以判断的时候相关,当然我们也可以选择顺时针方向,那时候我们就是从p12 p11那边算起,那时候我们就要判断点是否在当前栈的两个点组成的边的顺时针方向。

(4)然后我们得到了这些点组成的凸包,现在我们将要进行Delaunay三角形的构造,我一开始是确定用Bowyer-Watson算法进行生成,但是在其中第二步的时候我有些疑惑,后来自己想了一种办法解决,不知道是否有漏洞,但是画图画出来倒是都ok的,我这边拷贝一下上面链接中一个博主发的话

Delaunay剖分是一种三角剖分的标准,实现它有多种算法。本次采用Bowyer-Watson算法,算法的基本步骤是:
(1)构造一个超级三角形,包含所有散点,放入三角形链表。
(2)将点集中的散点依次插入,在三角形链表中找出其外接圆包含
插入点的三角形(称为该点的影响三角形),删除影响三角形的公共边,将插入点同影响三角形的全部顶点连接起来,从而完成一个点在Delaunay三角形链表中的插入。
(3)根据优化准则对局部新形成的三角形进行优化。将形成的三角形放入Delaunay三角形链表。
(4)循环执行上述第2步,直到所有散点插入完毕。

关键步骤2如下图所示:
这里写图片描述

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

LOP (Local Optimization Procedure)处理过程如下图所示:
这里写图片描述

--------------------- 本文来自 Dablelv 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/k346k346/article/details/52244123?utm_source=copy 

我这边疑惑的是第二步中怎么去“删除影响三角形的公共边,将插入点同影响三角形的全部顶点连接起来”,后来在写代码中想到了一种方法,我现在在这里进行一下说明,首先我们得到了凸包,现在我们在凸包中插入一个点,那么怎么进行三角形的划分呢?是不是直接拿各个凸包的点和插入点连接就可以完成了三角形的划分,得到一个Triangles,然后我们现在进行第二个点的插入,我们检测Triangle的外接圆发现这个点在三个三角形内,我们称这三个三角形是受影响的三角形,我们可以判断这三个三星形是连在一起的,我们将这三个三角形从Triangles中删除,并用这三个三角形的边形成了一个多边形,然后这个插入点就在这个多边形中间,这是不是就是变成我们插入第一个点的情况。。我们只需连接各个点与插入点就可以得到全新的三星形集合,加入Triangles即可。这边还值得注意的是局部优化修正过程,一开始不是很理解这个情况,后来自己想了一下这种情况应该是要避免外接圆中的刚好两个点是直径的情况然后第四个点在园内,要修改对角线,形成小的外接圆,将其中一个点分叉出去。

等以上工作全部做完以后你就可以得到一个Triangles的列表,画出来就可以

(5)最后一步是维诺图的建立,其实维诺图的建立在Delaunay三角形上就已经很方便了,只要找出上一步的各个Triangle的外接圆圆心,然后将具有共同边的外接圆圆心连接起来就可以得到我们需要的初步维诺图,还有的是值得注意的是维诺图中存在一些射线。我就是在画图过程中被这些射线搞得烦死了。只有真正有画图的需求才知道射线好难决定。。我也说不清楚,如果按我写的代码中我处理很复杂,虽然是画出来了,但是还有一点点问题。可能有时候会失效。。

上面就是我写这个小项目的过程,其中用了一些java8的stream特性,觉得java8还挺好用的。。加油加油。


 

下面放几个类的小截图,给也想要试一下你的一些感觉,当然你可以去看看我的代码。如果觉得我还可以的,麻烦给我Github的项目小星星~~~

这是主要算法类:Algorithm

这是Edge的类 主要重写了equals和tostring

这是窗体类

 这是画板类

 稍微贴几张图,具体可以去看代码~~~

这是自己实现的一个代码,发现网上资料有是有,但是没看到一个真正可以跑起来的,自己做一个例子happy一下,额~这个大概用了我五天的时间去写完。当然是断断续续,因为最近也在上课了。

猜你喜欢

转载自www.cnblogs.com/monster5475/p/9726543.html