清华计算几何大作业(八):CG2017 PA2-2 Find Dancing Partners (寻找舞伴) · 下 | Voronoi Diagrams 构造实现讲解

1. 思路分析(Voronoi Diagrams结合Point Location)

2.2.1 生成输入线段

在生成Voronoi Diagrams之后,我们需要查找某个点位于哪个Voronoi Cell之中,这个操作如果不借助Point Location,我们需要遍历所有Voronoi Cell,查询时间复杂度将会Unacceptable!那么我们可以借助Point Location算法将Voronoi Diagrams分割成一个个Trapezoid。我们在Trapezoidal Map之中进行Point Location,之后查找当前Trapzoid隶属于哪一个Voronoi Cell,即可完成题目要求的高效查询。那么现在的问题就是:如何根据生成的Voronoi Diagrams生成对应的Point Location查询结构呢?或者说Point Location的输入线段从何而来呢?

其实答案很简单,输入线段其实就是Voronoi Edge,但是有一点需要注意:

因为Voronoi Diagrams最后是连接到Bounding Box上面的,所以我们需要区分Voronoi Edge和Bounding Box的Edge。

我们以上一节的例子为例,生成对应的输入线段以及构造的Trapezoidal Map:

在这里插入图片描述

2.2.2 Space Query(空间查找)

在构造好Trapezoidal Map之后,我们就可以进行Point Location查询,查询结构也理所当然的有三种:

  • 查询点落在Trapezoid里面;
  • 查询点落在输入线段上;
  • 查询点落在输入线段的端点上;

我们再根据Voronoi Diagrams和Trapezoidal Map之间的对应关系,可以把上面的查询结构转化成在Voronoi Diagrams的查询结果:

  • 查询点落在Voronoi Cell里面 <= 查询点落在Trapezoid里面;
  • 查询点落在Voronoi Edge上 <= 查询点落在输入线段上;
  • 查询点落在Voronoi Vertex上 <= 查询点落在输入线段的端点上;

最后我们根据上面三种查询结果,通过DCEL结构,遍历出查询点最近的Site有哪些,既查询点分别落在Cell,Edge和Vertex上,有哪些最近Cell:

在这里插入图片描述

如果有多个最近Site,我们只需要按照题意报告ID最小的那个即可。到此,我们也讲解完了Voronoi Diagrams结合点定位的知识点。一共用了三篇文章才大功告成,不容易不容易哒~

2. 伪代码

※ 吐槽:这里大家可以看到整个系列课程里面最难实现的伪代码,一行伪代码背后都可以涉及一个小算法才能实现… 这点在两个事件处理伪代码里面体现得淋漓尽致

// 主入口算法
Algorithm VORONOIDIAGRAM(P)
算法 VORONOIDIAGRAM(P)
Input. A set P := {p1, . . . , pn} of point sites in the plane.
输入:平面点集P := {p1,, pn)
Output. The Voronoi diagram Vor(P) given inside a bounding box in a doubly connected edge list D.
输出:以双向链接边表D 表示的(限制在一个足够大的包围框之内的)VoronoiVor(P)
1. Initialize the event queue Q with all site events, initialize an empty status structure T and an empty doubly-connected edge list D.
1. 初始化事件队列Q:将所有的基点事件插入其中,初始化状态结构T:将其置空,初始化双向链接边表D:将其置空
2. while Q is not empty
2. while (Q 非空)
    3. do Remove the event with largest y-coordinate from Q.
    3. do 将y-坐标最大的事件从Q 中取出
        4. if the event is a site event, occurring at site pi
        4. if (这是一个发生于基点pi 处的基点事件)
            5. then HANDLESITEEVENT(pi)
            5. then HANDLESITEEVENT(pi)
        6. else HANDLECIRCLEEVENT(γ), where γ is the leaf of T representing the arc that will disappear
        6. else HANDLECIRCLEEVENT(γ)(* 这里的γ是T 的一匹叶子,它对应于那段即将消失的弧 *)
7. The internal nodes still present in T correspond to the half-infinite edges of the Voronoi diagram. Compute a bounding box that contains all vertices of the Voronoi diagram in its interior, and attach the half-infinite edges to the bounding box by updating the doubly-connected edge list appropriately.
7. (* 仍然存在于T 中的那些内部节点,对应于Voronoi 图的单向无穷边 *) 计算出一个包围框,其尺寸之大,应足以容下Voronoi 图中的所有顶点,通过对双向链接边表的适当调整,将这些单向无穷边都联接到这个包围框上
8. Traverse the half-edges of the doubly-connected edge list to add the cell records and the pointers to and from them.
8. 遍历双向链接边表中的所有半边, 增加相应的单元记录,设置好指向这些单元的指针,以及由这些单元发出的(指向对应各边的)指针
// 基点事件处理算法
HANDLESITEEVENT(pi)
算法 HANDLESITEEVENT(pi)
1. If T is empty, insert pi into it (so that T consists of a single leaf storing pi) and return. Otherwise, continue with steps 25.
1.T 为空,则将pi 插入其中 (* 这样,T 只由单匹叶子组成,这匹叶子记录了pi 的信息 *),然后返回否则继续执行第2~52. Search in T for the arc α vertically above pi. If the leaf representing α has a pointer to a circle event in Q, then this circle event is a false alarm and it must be deleted from Q.
2.T 中查找出位于pi 垂直上方的那段弧α,若对应于α的那匹叶子有一个指针,指向Q 中某个圆事件,则(* 说明该圆事件是一次误警 *)Q 中剔除该事件
3. Replace the leaf of T that represents α with a subtree having three leaves. The middle leaf stores the new site pi and the other two leaves store the site pj that was originally stored with α. Store the tuples <pj, pi> and <pi, pj> representing the new breakpoints at the two new internal nodes. Perform rebalancing operations on T if necessary.
3. 将原来对应于α的那匹叶子,替换为一棵由三匹叶子构成的子树:居中的那匹叶子记录下新基点pi,两侧的两匹叶子分别记录下原先记录于α的基点pj,两个内部节点分别对应于新生出的断点,它们分别记录下基点对<pj, pi><pi, pj>,若有必要,须对T 做调整,以使之重新平衡
4. Create new half-edge records in the Voronoi diagram structure for the edge separating V(pi) and V(pj), which will be traced out by the two new breakpoints.
4. 对应于将V(pi)V(pj)分割开来的那条边,在Voronoi 图结构中创建一个半边记录(* 这条边将由两个新断点逐渐勾勒出来 *)
5. Check the triple of consecutive arcs where the new arc for pi is the left arc to see if the breakpoints converge. If so, insert the circle event into Q and add pointers between the node in T and the node in Q. Do the same for the triple where the new arc is the right arc.
5. 找出pi 在其中居于左侧的那个邻接弧三元组,检查是否有断点汇聚到一点,果真如此,则将对应的圆事件插入事件队列Q,并在Q 中该节点和T 中与之对应的节点之间设置指针,使它们相互指向对方,找出pi 在其中居于右侧的那个邻接弧三元组,做类似的处理
// 圆事件处理算法
HANDLECIRCLEEVENT(γ)
算法 HANDLECIRCLEEVENT(γ)
1. Delete the leaf γ that represents the disappearing arc α from T. Update the tuples representing the breakpoints at the internal nodes. Perform rebalancing operations on T if necessary. Delete all circle events involving α from Q; these can be found using the pointers from the predecessor and the successor of γ in T. (The circle event where α is the middle arc is currently being handled, and has already been deleted from Q.)
1. 将(对应于即将消失的弧α的那匹)叶子γ,从T 删除掉,检查相关的内部节点,更新其中表示有关断点的基点对信息,若有必要,须对T 做调整,以使之重新平衡,在Q 中,删除所有与α相关的圆事件(*T 中,γ的前驱与后继节点配有相应的指针 *)(* 借助这些指针,就可以找出这些事件 *)(α在其中居中的那个圆事件,此刻正在接受处理,并已经从Q 被删除掉了)
2. Add the center of the circle causing the event as a vertex record to the doubly-connected edge list D storing the Voronoi diagram under construction. Create two half-edge records corresponding to the new breakpoint of the beach line. Set the pointers between them appropriately. Attach the three new records to the half-edge records that end at the vertex.
2. 更新存储当前Voronoi图的双向链接边表D:对应于该事件的圆心生成一个Voronoi顶点记录,并将该记录插入双向链接边表对应于海滩线上新生出的断点生成两个半边记录,并正确地设置好它们相互之间的指针,将这三个新记录,与同样终止于该Voronoi顶点的其它半边链接起来
3. Check the new triple of consecutive arcs that has the former left neighbor of α as its middle arc to see if the two breakpoints of the triple converge. If so, insert the corresponding circle event into Q. and set pointers between the new circle event in Q and the corresponding leaf of T. Do the same for the triple where the former right neighbor is the middle arc.
3. (* 此前与α紧邻于左侧的那段弧,现可能在某个新的邻接弧三元组中居中 *) 检查该邻接弧三元组所对应的两个断点是否汇合为一点,果真如此,则将对应的圆事件插入到事件队列Q中,并在Q中该节点和T中与之对应的节点之间设置指针,使它们相互指向对方(* 此前与α紧邻于右侧的那段弧,现也可能在某个新的邻接弧三元组中居中 *)对该弧,做类似的处理。

3. 可视化结果示例

首先给到构造Voronoi Diagrams的两个可视化结果:1)构造需要链接到Bounding Box上面的Voriono Edge;2)最后生成的Voronoi Diagrams:

在这里插入图片描述

其次,给到由之前Voronoi Diagrams所有Voronoi Edge生成的Segment,用于Point Location算法的构建,以及最后生成的Trapezoidal Map:

在这里插入图片描述

最后这里给到以塞尔达野炊所有传送点为基点(Site)构造出来的的Voronoi Diagrams,以及相应的Point Location结构,以及用它们进行完整的Voronoi Diagrams点定位计算:

在这里插入图片描述

红色点为查询点,红色边框的图形l为查询点所在的Trapeze或Voronoi Cell,通过这个可视化结果,大家也应该能理解我们如何运用 Voriono Diagrams 和 Point Location 两个算法来解决本题。

4. 项目代码

个人作业项目代码:Algorithm

1.1.6 Voronoi Diagrams

DescriptionEntry method\File
Build Voronoi DiagramsBoundingBox voronoiDiagrams( List<Face> sites )
Find on which cell ( Voronoi Face ) the query point is.List<Face> findCell( SearchVertex v, Vector p )
Generate segments from Voronoi edges to compute the trapezoidal Map of the Voronoi Diagrams.List<Line> getSegments( BoundingBox b )
Program ( including visualization )CG2017 PA2-2 Find Dancing Partners

1.1.7 Point Location

DescriptionEntry method\File
Build trapezoidal Map and search structureBoundingBox trapezoidalMap( List<Line> S, , List<Vector> Q )
Point LocatoinSearchVertex get( Vector p )
Program ( including visualization )CG2017 PA3-2 Which wall are you looking at

5. 拓展(Follow-ups)

  • 使连接无限半边到Bounding Box操作的时间复杂度为最优:O( nlogn ),笔者当前实现使用的暴力解查询需要连接的边界半边。
  • Voronoi Diagrams和Delaunay Triangulation的相互转化,两者互为对方的对偶图。
  • 使用线段计算Voronoi Diagrams,详见教材 7.3 线段集Voronoi 图( 7.3 Voronoi Diagrams of Line Segments )。

上一节:CG2017 PA2-2 Find Dancing Partners (寻找舞伴) · 下

下一节:CG2017 PA2-1 Shortest Path in The Room (房间中的最短路径)

系列汇总:清华计算几何大作业思路分析和代码实现

6. 参考资料

  1. Computational Geometry: Algorithms and Applications
  2. 计算几何 ⎯⎯ 算法与应用, 邓俊辉译,清华大学出版社
  3. 计算几何 | Computational Geometry

7. 免责声明

※ 本文之中如有错误和不准确的地方,欢迎大家指正哒~
※ 此项目仅用于学习交流,请不要用于任何形式的商用用途,谢谢呢;


在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值