本文算法见:http://www.faqs.org/faqs/graphics/algorithms-faq/ 中的Subject 1.03
很明显,线段的端点由两个SPoint来定义,SPoint定义如下,坐标系中的x,y坐标可以决定一个点:
class SPoint(object): def __init__(self, a=0.0, b=0.0): self._x = a self._y = b所以一条线段AB的定义可以为(SPoint A, SPointB).很明显,这个线段用A,B来表示的话就是:
AB = A + r(B-A), r=[0,1]r=0的时候,线段AB其实就是A点,r=1的时候,线段AB就是B点。r的取值在0-1之间就描述了AB之间的的线段。
给定两个线段AB,CD,根据上面的定义,这两个线段的程序表达式如下:
AB = A + r(B-A), r=[0,1] CD = C + s(D-C), s=[0,1]如果AB和CD有交叉,那么必然有对应的 r 和 s 满足下面的等式:
A + r(B-A) = C + s(D-C)转换成坐标x, y也就是:
A._x+r(B._x-A._x)=C._x+s(D._x-C._x) A._y+r(B._y-A._y)=C._y+s(D._y-C._y)根据上面的两个等式可以得到 r 和 s 的值为:
(A._y-C._y)(D._x-C._x)-(A._x-C._x)(D._y-C._y) r = --------------------------------------------- (B._x-A._x)(D._y-C._y)-(B._y-A._y)(D._x-C._x) (A._y-C._y)(B._x-A._x)-(A._x-C._x)(B._y-A._y) s = --------------------------------------------- (B._x-A._x)(D._y-C._y)-(B._y-A._y)(D._x-C._x)很明显,当 0<= r <=1 并且 0<= s <=1的时候,两个线段是有相交的。
当 r 或者 s 的分母为0时,两条线段是平行的。 当 r 或者 s 的分母为0并且分子也为0时,两条线段是重合的。
如果r>1, 线段的交点位于AB的延长线上; 如果r<0, 线段的交点位于BA的延长线上; 如果s>1, 线段的交点位于CD的延长线上; 如果s<0, 线段的交点位于DC的延长线上;
实现的代码如下,注意在测试用例中有一个特殊的case:[(1,1), (2,2), (2,2), (3,3)], 线段AB和CD交于(2,2),但是线段AB和CD其实是一条直线,具体判断这种情况为相交线还是不相交就看应用场景了.
# -*- coding: utf-8 -*- class SPoint(object): def __init__(self, a=0.0, b=0.0): self._x = a self._y = b def LineIntersection2D(A, B, C, D): rDenominator = (B._x-A._x)*(D._y-C._y)-(B._y-A._y)*(D._x-C._x) rNumerator = (A._y-C._y)*(D._x-C._x)-(A._x-C._x)*(D._y-C._y) sDenominator = (B._x-A._x)*(D._y-C._y)-(B._y-A._y)*(D._x-C._x) sNumerator = (A._y-C._y)*(B._x-A._x)-(A._x-C._x)*(B._y-A._y) if rDenominator == 0: # rDenominator == sDenominator # lines are parallel return False r = (1.0) * rNumerator / rDenominator s = (1.0) * sNumerator / sDenominator if 0<=r<=1.0 and 0<=s<=1.0: return True else: return False if __name__ == '__main__': TestLines = [[(1,1), (2,2), (1,2), (2,1)], # intersected: YES [(1,1), (2,2), (1,1), (2,2)], # NO [(1,1), (2,2), (1,1), (2,1)], # YES [(1,1), (2,2), (1,2), (2,3)], # NO [(1,1), (2,2), (2,2), (3,3)], # YES or NO??? [(1,1), (2,2), (2,2), (3,4)]] # YES for points in TestLines: A = SPoint(points[0][0], points[0][1]) B = SPoint(points[1][0], points[1][1]) C = SPoint(points[2][0], points[2][1]) D = SPoint(points[3][0], points[3][1]) intersected = "Yes" if LineIntersection2D(A,B,C,D) else "No" print "AB and CD is intersected?", intersected