CG2017 PA2-2 Find Dancing Partners (寻找舞伴) · 中
1.思路解析(Voronoi Diagrams)
2.1.3 连接到Bounding Box
将无限半边(Infinite Half-edge)连接到Bounding Box的思路也不难,首先,我们先将Bounding Box也看成一个方形的DCEL结构,然后计算出无限半边与边界边的节点,然后进行连接即可:
但在进行连接的时候,需要注意四点:
- 如何确定哪些Voronoi Edge为无限半边;
- 如何确定无限半边和Bounding Box的交点;
- 如何连接无限半边和Bounding Box;
- Bounding Box的范围确定;
首先,在结束计算Voronoi Diagrams之后,Status Tree中存储的中间节点里面就包含了所需连接到R上的Infinite Half-edges,比如之前的例子:
大家可以看到右侧Status Tree的中间节点刚好存储了需要进行连接的无限半边:e10, e7, e1 和 e4,且没有半边为其他半边的twin edge的情况存在。但是这是一般情况下,在特殊情况下这个条件是不成立的:有数个Site刚好落在同一条水平线上,且它们位于所有Site的最上方,比如下面这个示例,会存在无限半边最后不在Status Tree中的情况,需要进行特殊处理:
上面这个例子,Status Tree中最后只有e10 和 e5,没有e1 和 e3,但是e1和e3确实是无限半边,是需要连接到R上面的。但为什么会出现这样的问题呢?原因在于当两条抛物线的基点处于同一水平线上,且基线相同,那么它们的交点至多只有一个,一般情况下交点会有两个。所以当<1,2>和<2,4>汇集到左边的Voronoi Vertex,我们就失去了<1,2>所表示的无限半边信息,所以针对这种情况需要进行代码的单独处理,单独存储这些“消失”的无限半边。
其次,对于如何确定无限半边和Bounding Box的交点,我们按照无限半边延长的方向(也即是抛物线交点的前进方向),求解它所在射线与R的交点,这个思路我们在 CG2017 PA1-2 Crossroad (十字路口) 中有给大家介绍过,因此,我们把Half-edge看成一种特殊的射线(Ray),应用当前场景的思路见下图:
接下来就是如何连接无限半边到R上面,涉及的DCEL操作不难,但是需要注意:当前无限半边连接的是哪一条R的半边,因为大家可以注意到,进行一次或多次连接后,原来整条R的边界半边就会被分割成若干份。我们需要进行边界半边查询操作,或者先记录下一边边界半边所需要连接的无限半边,之后按照一定的顺序依次进行连接,但两种思路都需要用到半边排序的操作。
最后在确定R范围的时候,大家一定要紧跟伪代码的描述:
计算出一个包围框,其尺寸之大,应足以容下Voronoi 图中的所有顶点,通过对双向链接边表的适当调整,将这些单向无穷边都联接到这个包围框上
千万不要仅使用Site计算R的范围,因为某些情况下,Voronoi Vertex会落在离Site非常远的位置,这点需要额外注意。
2.1.4 Degenerate Case
根据教材上面的描述,Voronoi Diagrams涉及的Degenerate Case一共有4种:
- 数个Site处于同一水平线上;
- 数个SIte共圆;
- 某个Site刚好处于两个Arc交点的下方,且与上方两个Site形成一个空心圆;
- 三条连续同高度的Arcs,它们是由三个处于同一水平线上的SIte所确定;
对于1),我们只需用到之前的“假想斜切”思路进行处理即可,这里不再赘述。对于2)每三个Site会形成一个空心圆,而且这些空心圆的圆心和半径都是相同的,既空心圆重合,这里我们需要使用Zero-length Edge(虚边)的概念来进行处理,这个我们单独留到下一节再进行讲解。对于3),我们只需在加入该Site的时候,检查是否会生成相应的圆事件即可。对于4),这三个Site不会定义任何圆事件,所以直接忽略即可,不做单独处理。所以接下来我们把重点放在多Site共圆的讲解上面来。最后我们给到教材上面关于3)的图例1,方便大家理解:
2.1.5 Zero-length Edge(虚边)
当至少4个Site共圆的时候,按照我们之前的算法处理思路,会生成多个等价的空心圆(既圆心和半径都相同),且每个空心圆对应的Voronoi Vertex也是重合的:
那么在构造Voronoi Diagrams的过程中,我们是否需要对这种情况进行单独处理呢?答案是不需要的,我们就保留这些等价的空心圆,和Voronoi Vertex,等到最后对生成的DCEL结构进行单独处理即可。为了更好的描绘这些重合的空心圆和Voronio Vertex,我们需要引入Zero-length Edge(虚边)的概念,既重合Voronoi Vertex之间有长度为0的半边相连:
在结束构造Voronoi Diagrams之后,我们遍历整个表示Voronoi Diagrams的DCEL结构,移除这些不需要的虚边。移除的规则也很简单:
虚边(Zero-length Edge)必定存在于两个重合的Voronoi Vertex之间。在重合在同一点的Voronoi Vertex之中,第一个生成和最后一个生成的Vertex至多只有一个虚边,其余Vertex至多有两条虚边,都不会超过三条虚边。
那么这又是为什么呢?首先大家先来观察下面5个Site共圆的情况:
大家应该能注意到这么一个事实,在形成第一个Voronoi Vertex的时候,e1和e3汇集形成v1,且这两条边都不为虚边,它们汇集之后形成e5,且e5为虚边,e5和e7汇集形成新的虚边e9,e9和e11汇集形成e12。所以我们似乎能观察到这么一个现象:
1)第一个重合Voronoi Vertex是由两条实边汇集而成,并且它们会形成新的一条虚边;
2)中间Vertex是由一条虚边和一条实边汇集而成,并且它们也会形成一条新的虚边;
3)最后一个Vertex也是由一条虚边和一条实边汇集而成,但它们最后不会形成一条虚边,而是一条实边;
为了严谨的说明上述结论,我们可以简单使用反证法证明一下:现在有5个Site共圆,按照顺时针方向它们分别是i,k,n,m,j,而且我们可以观察到每相邻的两个Site必定有一条Voronoi Edge分割两边的Site,那么现在假设有三条边:ei,ek,em,且ei所在的垂直平分线两边的Site分别为i和m,ek两边的Site分别为i和k,em两边的Site分别为m和k。ei和ek汇集形成一个中间Vertex,vi+1,且它们汇集之后形成em,同时它们三条边都是虚边。我们知道后续所有的虚边都会被移除,也表示虚边两侧的Site不会有相应的Voronoi Edge,那么m 和 i ,m 和 k,i 和 k之间都不会有Voronoi Edge,但是根据刚才的观察,i 和 k 之间至少有一条Voronoi Edge将它们平分分割,从而和我们的假设产生矛盾。
最后,关于移除Zero-length Edge操作非常的简单哦。首先找到需要移除的虚边,然后只需要把虚边的next和prev相连即可,它们相连的半边都指向同一个Voronoi Face(Cell):
到此我们就把所有Voronoi Diagrams实现相关的知识都给大家讲解完毕了,接下来我们将进入到Voronoi Diagrams结合Point Location的实现讲解当中,因为要高效解决题目,我们还需要“借力”Point Location算法才行。
上一节:CG2017 PA2-2 Find Dancing Partners (寻找舞伴) · 上
下一节:CG2017 PA2-2 Find Dancing Partners (寻找舞伴) · 下
系列汇总:清华计算几何大作业思路分析和代码实现
2. 参考资料
- Computational Geometry: Algorithms and Applications
- 计算几何 ⎯⎯ 算法与应用, 邓俊辉译,清华大学出版社
- 计算几何 | Computational Geometry
3. 免责声明
※ 本文之中如有错误和不准确的地方,欢迎大家指正哒~
※ 此项目仅用于学习交流,请不要用于任何形式的商用用途,谢谢呢;