点与点之间, 线与线之间,点与线之间的位置关系是1类非常重要的问题。它不但是平面几何学的基石,也常常利用于LBS(Location Based Service),社交网络,和数据库查询等领域。
本文中,我将给出判断这些关系的相干算法,作为参考。需要说明的是,我给出的这些问题的解法,都是建立在2维平面空间之上。有关多维空间的位置关系,大家可以仿照2维空间中问题的思路,做相应的拓展。
语言上,我用确当然还是Python.
点与点之间的距离
先从最简单的点与点的位置关系说起。1般情况下,我们只关心点与点之间的距离。
1. 点类的定义
为使算法思路更加清晰,先定义点类Point,既然是在2维空间上,那末每一个点都应当有两个属性:x, y分别代表点的横纵坐标。
class Point(object): """Point are two-dimension""" def __init__(self, x, y): self.x = x
self.y = y
接下来就看看如何计算两点之间距离:固然可以用初中学的欧氏距离最基本的计算方法。但是斟酌到代码编写的效力,和方便以后向高维空间拓展。我在本文中将尽可能使用向量计算。
而为了简化代码,我们使用对向量运算已相当做熟的库numpy
2. 两点之间距离的计算
明显,两点可以构成向量,而向量的长度则是其内积的开方。空间中,点A与点B的距离可以用向量AB−→−的模|AB−→−|表示。所以,现在需要做的,就是写1个函数,以两点为参数,计算由这两点构成的向量的模。
为了和本文以后的问题保持编码风格上1致,同时简化代码编写。我使用对向量运算已极其成熟的库numpy帮助计算。并且定义了1个新的类Vector,类Vector以向量的出发点和终点作为输入,生成1个只具有属性x和y的向量对象。
最后,和前面定义的类放在1起,代码以下:
import numpy as np class Point(object): """Point are two-dimension""" def __init__(self, x, y): self.x = x
self.y = y class Vector(object): """start and end are two points""" def __init__(self, start, end): self.x = end.x - start.x
self.y = end.y - start.y def pointDistance(p1, p2): """calculate the distance between point p1 and p2""" v = Vector(p1, p2) t = np.array([v.x, v.y]) return float(np.sqrt(t @ t))
说明1下,在Python3.5以后的版本中,使用numpy库时,ndarray对象之间的乘法可以用@,代替之前的v1.dot(v2)这样的情势。
点与线之间的位置关系
1. 线的分类
点与线之间的位置关系就要略微复杂1些了,复杂的地方在于线分线段和直线两种情况。但是,在定义类的时候我都用两点来代表线段(直线)的两个属性。因而,最少代码看上去是没甚么分别的。
不同的地方在于,线段的两个点事两个端点,而直线的两个点是直线上任意两点。
class Segment(object): """the 2 points p1 and p2 are unordered""" def __init__(self, p1, p2): self.p1 = p1
self.p2 = p2 class Line(object): """p1 and p2 are 2 points in straight line""" def __init__(self, p1, p2): self.p1 = p1
self.p2 = p2
需要注意的是,这里并没有说线段的两个点是甚么顺序(不1定说左侧的点就是p1,右侧就是p2)
2. 点与线的位置关系
(1) 计算点到直线的距离
如Fig.1(a)所示,现要求点C到直到直线AB的距离。还是向量法,据向量知识可知:
cos∠CAB=AC−→−⋅AB−→−|AC−→−|⋅|AB−→−|
再由3角形知识可知,线段AD的长度为:
|AC−→−|⋅cos∠CAB
所以,AD−→−可以这样计算:
AD−→−=AB−→−|AB−→−|⋅|AD−→−|=AB−→−|AB−→−|⋅|AC−→−|⋅cos∠CAB=AB−→−⋅AC−→−|AB−→−|2AB−→−
当AD−→−计算完成以后,可以根据AD−→−相应的坐标值得到点D的坐标,再由上面点和点之间的距离,便可得到线段CD的长度。
给出完全的代码以下:
import numpy as np class Point(object): """Point are two-dimension""" def __init__(self, x, y): self.x = x
self.y = y class Segment(object): """the 2 points p1 and p2 are unordered""" def __init__(self, p1, p2): self.p1 = p1
self.p2 = p2 class Line(object): """p1 and p2 are 2 points in straight line""" def __init__(self, p1, p2): self.p1 = p1
self.p2 = p2 class Vector(object): """start and end are two points""" def __init__(self, start, end): self.x = end.x - start.x
self.y = end.y - start.y def pointDistance(p1, p2): """calculate the distance between point p1 and p2""" v = Vector(p1, p2) t = np.array([v.x, v.y]) return float(np.sqrt(t @ t)) def pointToLine(C, AB): """calculate the shortest distance between point C and straight line AB, return: a float value""" vector_AB = Vector(AB.p1, AB.p2)
vector_AC = Vector(AB.p1, C) tAB = np.array([vector_AB.x, vector_AB.y])
tAC = np.array([vector_AC.x, vector_AC.y]) tAD = ((tAB @ tAC) / (tAB @ tAB)) * tAB Dx, Dy = tAD[0] + AB.p1.x, tAD[1] + AB.p1.y
D = Point(Dx, Dy) return pointDistance(D, C)
(2) 判断点是不是在直线上
既然已能够计算点到直线的距离了,那末,只需要看点到直线的距离是不是为0便可知道这个点在不在直线上。
接着上面的代码,可以写出以下函数:
def pointInLine(C, AB): """determine whether a point is in a straight line""" return pointToLine(C, AB) <