1、快速排斥
判断线段是否相交,如果需要判断很多次,最好能有一种没什么计算量的判别方法先粗略的进行一次判断。具体来说,如果线段ab和线段cd不相交,那么以他们为斜线画出的矩形一定不相交,如图所示:
可以看出,只要满足ab的最大x坐标小于cd的最小x坐标(第三竖排),或者cd的最大x坐标小于ab的最小x坐标(第一竖排),或者ab的最大y坐标小于cd的最小y坐标(以左下角为坐标原点的话第一横排),或者cd的最大y坐标小于ab的最小y坐标(以左下角为坐标原点的话第三横排),ab与cd就一定不相交,因此可以写出python代码如下:
#快速排斥实验,判断线段ab和线段cd是否不相交,不相交返回False,不能判断不相交返回True
def quick_judge(a,b,c,d):
if (max(a.x,b.x) < min(c.x,d.x) or
max(c.x,d.x) < min(a.x,b.x) or
max(a.y,b.y) < min(c.y,d.y) or
max(c.y,d.y) < min(a.y,b.y)) :
return False
else:
return True
其中a,b,c,d都是自己定义的Point类型。
class Point():
def __init__(self,x,y):
self.x = x
self.y = y
2、叉乘
有a(a.x,a.y),b(b.x,b.y),c(c.x,c.y),d(d.x,d.y)四个点,
向量ab可以表示为vectorA(b.x-a.x,b.y-b.x)
向量cd可以表示为vectorB(d.x-c.x,d.y-c.y)
vectorA叉乘vectorB的结果是(vectorA.x * vectorB.y - vectorA.y * vectorB.x)
用python代码表达:
#求向量ab和向量cd的叉乘
def xmult(a,b,c,d):
vectorAx = b.x - a.x
vectorAy = b.y - a.y
vectorBx = d.x - c.x
vectorBy = d.y - c.y
return (vectorAx * vectorBy - vectorAy * vectorBx)
利用叉乘,我们可以判断两个点是否在一条直线或者线段两端,进而判断两条线段是否相交,如果点a和点b在直线cd的两端,同时,点c和点d在直线ab的两端,那么线段ab和线段cd就相交,如图所示:
如何判断ab在cd两侧呢?向量cd叉乘向量ca,结果的符号可以表示将cd旋转到ca所在的直线的最短移动路径的方向,图中也就是逆时针,如果ab不在cd两侧,向量cd旋转到ca和cb应当是相同方向(即都是顺时针或都是逆时针),只有在如图所示的情况,ab分布在cd两侧时,cd旋转到ca和旋转到cb的方向才会不同。
如果方向相同,cd叉乘ca的结果xmult1,乘上cd叉乘cb的结果xmult2,结果应该为正。方向不同这个结果则为负,也就是说如果xmult1 * xmult2 <0,就可以认为ab在cd的两侧。同理,认为ab叉乘ac = xmult3,ab叉乘ad=xmult4,那么如果cd在ab两侧,xmult3*xmult4<0。
代码如下:先进行快速排斥,如果快速排斥无法得出ab和cd不相交,再利用叉乘进行判断
#判断线段ab和线段cd是否相交,相交返回True,不相交返回False
def cross(a,b,c,d):
#先进行快速排斥,以减小计算量
if not quick_judge(a,b,c,d):
return False
#若快速排斥无法判断,使用叉乘判断是否相交
#以c为公共点,分别判断向量cd到向量ca与到向量cb的方向,记为xmult1和xmult2。
#若ab分布于cd两侧,xmult1 * xmult2应小于0。
#同理若cd分布于ab两侧,xmult3 * xmult4应小于0。
xmult1 = xmult(c,d,c,a)
xmult2 = xmult(c,d,c,b)
xmult3 = xmult(a,b,a,c)
xmult4 = xmult(a,b,a,d)
if xmult1 * xmult2 < 0 and xmult3 * xmult4 < 0:
return True
else:
return False
此方法不考虑ab和cd在一条直线上的情况,因为我的应用场景不会出现这种情况。如果会出现,可自行写一个特殊情况的判断代码。
另外,多段线段和多段线段的相交判别,可以逐段进行判断,转换成单段线段和单段线段的判别问题。