CG2017 PA1-2 Crossroad (十字路口)
1. 前置知识
- 计算几何算法:Line Segment Intersection(Plane Sweep),详见教材 线段求交:专题图叠合 (2 Line Segment Intersection: Thematic Map Overlay)
- 平衡二叉查找树(BBST),本系列特指红黑树
这里给到必要观看的视频课程章节,这些内容对理解和实现 线段求交 算法至关重要,标记有绿色√为必看章节
2. 思路分析
问题描述:CG2017 PA1-2 Crossroad (十字路口)
在解析处理直线、射线和圆的处理方式之前,我们先来看看线段求交算法的一个核心思路:求交几何物体的单调性,这个性质非常的重要,因为之后圆需要使用相同的思路进行处理。其实简单来说,满足线段求交算法要求的几何图形需要满足以下性质:
- 图形为单调图形;
- 图形定义有左起始点(Left EndPoint)和右终止点(Right EndPoint);
这两个性质我们结合线段就能很清楚的理解了。从下图可知,一条线段相对x轴单调递增,并且有左起始点和右终止点:
这些性质之所以重要,是因为求交算法就是依赖它们来避免不要的检查(即Brute Froce两两检查是否相交)。以线段为例,通过待检查线段进行空间位置的排序,我们只需要检查那些足够相邻的线段,只有它们才有可能相交,也就是说只有当遇见Left EndPoint、Right EndPoint以及交点,线段之间的位置关系才会发生改变,也即与它们在空间位置上相邻的线段才需要检查是否相交。我们以x轴为扫描线,y轴为空间排序方向:
我们可以看到,只有当扫描线经过 l2 和 l3 的交点A之后,它们之间的空间位置关系才会发生变化,并且其余线段的位置关系均不受影响,这点就是线段求交算法的核心思路。
那么我们如何应用这个思路来处理圆呢?其实很简单,我们只需要把圆处理成具有相同性质的图形即可,即四条单调圆弧(Arc):
接下来的问题就是如何处理直线和射线了,它们与线段不同,直线和射线都有无限延伸的部分,而我们的算法是不太好处理无限延展的图形的。为了解决这个问题,我们可以利用一下计算几何当中一个非常重要的概念:Bounding Box(限定区域),这样的处理会把无限的问题有限化,即把无限的直线或射线全部处理成线段来计算,以达到“无限化有限,有限化一“的目的,比如下面这个例子:
但是这样会有一个问题,如果某些直线或射线相交于无限远(即相交于Bounding Box之外),那么这个思路是无法报告出所有的交点的。虽然这个思路有一定局限性,但在实际应用中这种限制是非常常见的,比如显示屏的显示区域就是一个天然的Bounding Box,我们不可能也不需要显示一条无限延伸的直线,我们只需要显示它在屏幕限定的区域内即可,其实这样的处理也是变相把直线看成线段了。
当然也不排除有其他可行的办法能报告出所有可能的交点,大家可以把这个问题当成拓展延伸进行思考,如果有啥新想法也请随时告诉我呢~
3. 伪代码
// 1. 主入口算法
Algorithm FINDINTERSECTIONS(S)
算法 FINDINTERSECTIONS(S)
Input. A set S of line segments in the plane.
输入:平面线段集S
Output. The set of intersection points among the segments in S, with for each intersection point the segments that contain it.
输出:S 中各线段之间的所有交点(以及穿过各交点的线段的信息)
1. Initialize an empty event queue Q. Next, insert the segment endpoints into Q; when an upper endpoint is inserted, the corresponding segment should be stored with it.
1. 初始化一个空的事件队列Q。然后,将所有线段的(上、下)端点插入Q 中;对于上端点,还要记录其对应的线段
2. Initialize an empty status structure T.
2. 初始化一个空的状态结构T
3. while Q is not empty
3. while (Q 非空)
4. do Determine the next event point p in Q and delete it.
4. do 找出Q 中的下一事件点p,将其删除
5. HANDLEEVENTPOINT(p)
5. HANDLEEVENTPOINT(p)
// 1. 主处理算法
HANDLEEVENTPOINT(p)
算法 HANDLEEVENTPOINT(p)
1. LetU(p) be the set of segments whose upper endpoint is p; these segments are stored with the event point p. (For horizontal segments, the upper endpoint is by definition the left endpoint.)
1. 令U(p)为所有以p 为上端点的线段构成的集合;这些线段都与事件点p 存放在一起(若是水平线段,则以其左端点做为上端点)
2. Find all segments stored in T that contain p; they are adjacent in T. Let L(p) denote the subset of segments found whose lower endpoint is p, and let C(p) denote the subset of segments found that contain p in their interior.
2. 在T中找出包含p的所有线段① (* 在T中,这些线段是(依次)相邻的 *) 在所找出的线段中将那些以p为下端点的线段组成集合L(p),将那些在内部包含p的线段组成集合C(p)
3. if L(p)∪U(p)∪C(p) contains more than one segment
3. if (L(p)∪U(p)∪C(p)包含不止一条线段)
4. then Report p as an intersection, together with L(p), U(p), and C(p).
4. then 报告“发现交点p”;同时返回L(p)、U(p)和C(p)
5. Delete the segments in L(p)∪C(p) from T.
5. 将L(p)∪C(p)中的线段从T 中删除
6. Insert the segments in U(p)∪C(p) into T. The order of the segments in T should correspond to the order in which they are intersected by a sweep line just below p. If there is a horizontal segment, it comes last among all segments containing p.
6. 将U(p)∪C(p)中的线段插入到T中 (* T中各线段的次序,必须与它们和扫描线刚离开p之后的相交次序一致 *) (* 若存在水平的线段,则将它排在包含p的所有线段②的最后 *)
7. (* Deleting and re-inserting the segments of C(p) reverses their order. *)
7. (* 将C(p)中的线段删除,然后按照逆序重新插入 *)
8. if U(p)∪C(p) = null
8. if U(p)∪C(p) = null
9. then Let sl and sr be the left and right neighbors of p in T.
9. then 在T 中,找出p 的左右邻居sl 和sr
10. FINDNEWEVENT(sl , sr, p)
10. FINDNEWEVENT(sl , sr, p)
11. else Let s' be the leftmost segment of U(p)∪C(p) in T.
11. else 在T 中,找出U(p)∪C(p)里最左边的线段s'
12. Let sl be the left neighbor of s' in T.
12. 在T 中,找出与s'紧邻于左侧的线段sl
13. FINDNEWEVENT(sl , s', p)
13. FINDNEWEVENT(sl , s', p)
14. Let s'' be the rightmost segment of U(p)∪C(p) in T.
14. 在T 中,找出U(p)∪C(p)里最右边的线段s''
15. Let sr be the right neighbor of s'' in T.
15. 在T中,找出与s''紧邻于右侧的线段sr
16. FINDNEWEVENT(s'', sr, p)
16. FINDNEWEVENT(s'', sr, p)
// 3. 查找新事件
FINDNEWEVENT(sl , sr, p)
算法 FINDNEWEVENT(sl, sr, p)
1. if sl and sr intersect below the sweep line, or on it and to the right of the current event point p, and the intersection is not yet present as an event in Q
1. if (sl 和sr 相交于当前扫描线的下方(或者交点正好落在当前扫描线上并且在当前事件点的右侧),而且该交点尚未做为一个事件出现在Q 中)
2. then Insert the intersection point as an event into Q.
2. then 将这个交点做为一个事件,插入到Q 中
4. 可视化结果示例
5. 项目代码
个人作业项目代码:Algorithm
1.1.3 Geometric Intersection
Description | Entry method\File |
---|---|
Line and line | Vector lineIntersect( Line l ) |
Segment and segment | Vector segmentIntersect( Line l ) |
Segment and Circle | Vector[] segmentCircle( Segment s, Circle c ) |
Line and Circle | Line lineCircleIntersect( Line line, Circle circle ) |
Brute Force | List<Vector> bruteForce( List<E> S ) |
Bentley Ottmann’s algrithom( Intersection Of segment, ray, line and Circle ) | List<EventPoint2D> findIntersection( List<IntersectionShape> S ) |
Program ( including visualization ) | CG2017 PA1-2 Crossroad |
6. 拓展(Follow-ups)
- 追加圆和圆求交
- 报告交点和交点所涉及的几何图形(比如有一个交点P,报告P的坐标,以及由哪些图形相交形成P) - 笔者代码已实现
- 追加交点筛选接口 - 笔者代码已实现
- 在保证原算法的时间复杂度的条件下,是否有方法能报告出所有交点(主要难点是由直线或射线产生的交点,在某些情况下这些交点会相交于无限远)
上一节:CG2017 PA5-1 Dynamic Convex Hull(动态凸包)
下一节:CG2017 PA3-1 Delaunay Triangulation (Delaunay三角剖分)
系列汇总:清华计算几何大作业思路分析和代码实现
7. 参考资料
- Computational Geometry: Algorithms and Applications
- 计算几何 ⎯⎯ 算法与应用, 邓俊辉译,清华大学出版社
- 计算几何 | Computational Geometry
8. 免责声明
※ 本文之中如有错误和不准确的地方,欢迎大家指正哒~
※ 此项目仅用于学习交流,请不要用于任何形式的商用用途,谢谢呢;