0. 引言
在计算几何中,经常要求判断线段与线段或直线是否有交点。如果简单地求它们的交点再进行判断容易出错(误差之类的,而且比较麻烦),所以我们需要稳定的方法来判断!
- 注:后文中所有的表示未标注的话均为向量表示法。
1. 法一(基本法曰.. 曰)
定义
- 一条直线由一个点 O和一个向量 V组成,过点 O,P的直线可以表示为:O+kV,其中 V=→OP。
- O=(x,y),P=(x1,y1),→OP=(x1−x,y1−y)
- 一条线段由两个点构成
1. 判断线段与直线相交
正文
点 p2 对于线段的关系可以分为 5 种,如图所示:
- 1.→p0p2在 →p0p1逆时针方向 (Counter Clock Wise)
- 2.→p0p2在 →p0p1顺时针方向 (Clock Wise)
- 3.p2在直线 p0p1上且 →p0p1与 →p0p2反向 (On Line Back)
- 4.p2在直线 p0p1上且 →p0p1与 →p0p2同向且 p2不在线段 p0p1上 (On Line Front)
- 5.p2在直线 p0p1上且 →p0p1与 →p0p2同向且 p2在线段 p0p1上 (On segment)
那么我们就可以分类讨论他们的特点啦。
首先,如果两条线段(p0p1与 p2p3)相交,则对于 p0,p1来说有如下三种情况:
- p0,p1分别在 →p2p3两端,一个在顺时针方向,一个在逆时针方向。
- p0,p1中至少有 1 个在线段 p2p3上
- p0,p1,p2,p3在同一直线上,且线段 p2,p3在线段 p0,p1上
对于 p2,p3同理。
于是我们需要判断一个点与一条线段之间的关系。
然后我们就写了这样一个函数(下面的情况指的是上面五张图中的各种情况):
代码也不长,可以准确判断点与线段关系。
注意一下函数的返回值。
为什么要一正一负呢?仔细观察:
- p0,p1分别在 →p2p3两端,一个在顺时针方向,一个在逆时针方向。此时 ccw(p2,p3,p0)×ccw(p2,p3,p1)=−1
- p0,p1中至少有 1 个在线段 p2p3上,此时 ccw(p2,p3,p0)×ccw(p2,p3,p1)=0
- p0,p1,p2,p3在同一直线上,且线段 p2,p3在线段 p0,p1上,此时 ccw(p2,p3,p0)×ccw(p2,p3,p1)=−4
发现没有?我们利用了异号相乘为负和任何数乘以 0均为 0的性质,如果 ccw(p2,p3,p0)×ccw(p2,p3,p1)≤0则说明对于 p0,p1满足上面的三个条件。
而这时候还不足以说明 p0p1与 p2p3相交,还需要判断对于 p2,p3是否满足上面的三个条件。
所以判断两条线段 p0p1与 p2p3是否相交的代码如下(intersect,相交,若相交返回 true(1)):
例题
You can Solve a Geometry Problem too
HDU – 1086: 传送门= ̄ω ̄=
题意:给出 n条线段,判断有几对线段相交。
代码:
2. 判断线段与直线相交
正文
这个问题比判断线段与线段相交简单些。
比如判断直线 S+kV和线段 p0p1是否相交,只需要判断 p0p1与 S+kV的位置关系。
思路与上面一样,只不过是 ccw函数不需要判断第 3,4 种情况(指的是上面的那 5 张图所示的情况),即我们把这两种情况均视为第 5 种情况——点在直线上。
所以 ccw函数这么写:
而且其实我们的 intersect函数也要改:
于是就愉快地判断了线段与直线关系啦!
例题
Segments
POJ – 3304: 传送门= ̄ω ̄=
题解:枚举任意两线段的不同端点,过这两点作一条直线。若存在一条直线与所有线段都相交则存在答案,输出 Yes!。否则说明不存在答案,输出 No!
注意特判只有一条线段的情况。
(我之前把特判写读入前面了WA了好久!
代码:
法二 (二次判断法)
对于两个线段是否相交,我们可以用一种较为快速的方法。利用一下几个结论。
- 如果包裹两条线段的矩形没有相交,那么两条线段一定没有相交,反之不一定。(排斥实验)
- 如果线段彼此跨过对方所在的直线或端点在直线上,则两线段一定相交。(跨立实验)
对于线段是否跨过一条直线,我们可以利用向量叉乘判断。叉乘是两向量的一种乘积,其结果 (2 维空间中) 是一个标量。如果我们在 2 维空间中,将两个向量的屁股接在一起,伸出右手,右手食指伸出指向向量 a,再转一个小于 π的角度指向 b,那么大拇指的方向代表了 a×b的符号。如果大拇指朝上,则符号为正,反之为负。如果大拇指既不朝上也不朝下,那么请去医院检查一下,或者检查一下你是否理解了上面这段话。
所以代码大概是这样的:
4 条评论
litble · 2018年1月19日 8:24 下午
表示蒟蒻是求个交点然后判断是否在线段上的 QAQ
konnyakuxzy · 2018年1月19日 9:36 下午
你这样会有误差啊
还是图样图森破 QvQ
litble · 2018年1月20日 9:37 下午
Orz 这种方法就是不管是两条什么线相交都可以用这种做法,不用动脑子。
还是您比较强啊
konnyakuxzy · 2018年1月20日 9:38 下午
这说明您的方法就是强啊 Orz
(另外您再改下我的评论试试看?)