线段之间的距离
· 阅读需 9 分钟
计算点和线段之间的距离,线段和线段之间的距离
空间中点和线段之间的距离
已知点 ,线段 , 假设线段 延长线上距离 最近的点是 , 设P点坐标为
是位置标量。
由于 点到 点是 到线段 延长线上距离最近的点, 所以 垂直于 。
将 带入
得
从而
如果 ,且为端点,则最小距离为
如果 ,且为端点,则最小距离为
如果没有端点,则最小距离为,
空间线段和线段之间的最短距离
已知线段 和线段 , 假设直线 和直线 之间的最短连线为 , 设 和 点坐标为:
和是位置标量。
由于直线和直线之间的最短连线为,则
于是
即
从上面两个式子分别得到
设
带入得到
将带入得到
求得
将带入到的表达式,就可以得到
从而得到和
最短距离就是
当最近的点在线段上时
上面的计算是正确的,如果最近点不在线段上,则需要考虑line1和line2是不是直线或者射线
先假设,,则最小距离
当是端点,且时,
计算P_1和的最短距离,最近点为, 如果则
当是端点,且时,
计算P_2和的最短距离,最近点为,如果则
当是端点,且时,
计算P_3和的最短距离,最近点为,如果则
当是端点,且时,
计算P_4和的最短距离,最近点为,如果则
以上是在两条直线不平行和不重叠的情况下进行的
当两条直线平行或者共线时
如果两条直线平行或者共线,则需要进行特殊讨论
-
在的后侧,且和较近, 即,如果和为端点,则
-
在的后侧,且和较近,即,如果和为端点,则
-
在的前侧,且和较近,即,如果和为端点,则
-
在的前侧,且和较近,即,如果和为端点,则
以上四种情况的距离为
-
如果上述四种情况均不符合,则说明有无数多最近点
Python代码实现
import numpy as np
class distance():
def __init__(self):
return
def points(point1, point2):
'''
Calculate distance between two points or
minimum distance between two point sets
'''
# First adjust points coordinates data shape
# point date type is like: np.array([[x1,y1,z1],[x2,y2,z2],...)
# Caution: double '['in point1 and point2
point1 = np.array(point1).reshape(-1, 3)
point2 = np.array(point2).reshape(-1, 3)
if point1.shape[0]*point2.shape[0] == 0: # 如果两个数组中有一个为空
# print("at least one points set in pointsDistance is void")
return 0
nrows, ncols = point1.shape
dtype = {'names': ['f{}'.format(i) for i in range(ncols)],
'formats': ncols * [point1.dtype]}
inter12 = np.intersect1d(point1.view(dtype), point2.view(dtype))
if inter12.shape[0] != 0:
# print("Intersected")
return 0
disArray = np.array([])
for pointa in point1:
for pointb in point2:
disArray = np.append(disArray, np.linalg.norm(pointa-pointb))
disArray = np.sort(np.unique(disArray))
return disArray
def pointLine(point, line, end1=1, end2=2, dimension=3):
'''
pointLine(point,line,end1=0,end2=0)
point: the point coordinate [x0,y0,z0] or for 2D: [x0,y0]
line: two points on line. p1 and p2.[x1,y1,z1,x2,y2,z2]
for 2D:[[x1,y1,x2,y2]]
end1: if end1=1, then p1 is one end of the line segment
if end1=0, then p1 is not end of the line segment
end2: the other end of line segment
'''
p0 = np.array(point).reshape(-1, dimension)[0]
# np.array([[[x0,y0,z0]]]) only one point
p1 = np.array(line).reshape(-1, dimension)[0]
p2 = np.array(line).reshape(-1, dimension)[1]
# np.array([[[x1,y1,z1],[x2,y2,z2]]]) only two points
u = (p0-p1).dot(p2-p1)/np.linalg.norm(p2-p1)**2.
if end1 == 1 and u <= 0:
p = p1
distance = np.linalg.norm(p-p0)
elif end2 == 1 and u >= 1:
p = p2
distance = np.linalg.norm(p-p0)
else:
p = p1 + u*(p2-p1)
distance = np.linalg.norm(p - p0)
return p, distance
def lines(line1, line2, end1=1, end2=1, end3=1, end4=1, dimension=3):
'''
Calculate the distance between two line segments
Reference:
http://paulbourke.net/geometry/pointlineplane/
https://stackoverflow.com/questions/2824478/shortest-distance-between-two-line-segments
'''
''' Given two lines segments defined by numpy.array
line: two points on line. p1 and p2.[x1,y1,z1,x2,y2,z2]
for 2D:[[x1,y1,x2,y2]]
There are four points for these two lines
end1: if end1=1, then p1 is one end of the line1 segment.
if end1=0, then p1 is not end of the line1 segment.
end2: the other end of line1 segment.
end3 and end4: ends of line2 segment.
'''
p1 = np.array(line1).reshape(-1, dimension)[0]
p2 = np.array(line1).reshape(-1, dimension)[1]
# np.array([[[x1,y1,z1],[x2,y2,z2]]]) two points for first line
p3 = np.array(line2).reshape(-1, dimension)[0]
p4 = np.array(line2).reshape(-1, dimension)[1]
# np.array([[[x3,y3,z3],[x4,y4,z4]]]) two points for second line
# 先判断是不是平行和共线
line1 = p2 - p1
line2 = p4 - p3
line1Norm = np.linalg.norm(line1)
line12cross = np.cross(line1, line2)
line12crossNorm = np.linalg.norm(line12cross)
if line12crossNorm != 0: # 不平行且不共线
# calculate mid variables
k1 = (p3-p1).dot(line1)
k2 = (line2).dot(line1)
k3 = np.linalg.norm(line1)**2.
k4 = (p3-p1).dot(line2)
k5 = np.linalg.norm(line2)**2.
ub = (k1 * k2 - k3 * k4)/(k3 * k5 - k2**2.)
ua = (k1 + ub*k2)/k3
pa = p1 + ua*(line1)
pb = p3 + ub*(line2)
if 0 <= ua <= 1 and 0 <= ub <= 1: # 当最近点都在线段上时
distance = np.linalg.norm(pb-pa)
return pa, pb, distance
# print("ua,ub,pa,pb:", ua, ub, pa, pb)
# 两个直线最近点不在线段上
pa = p1
pb = p3
distance = np.linalg.norm(pb-pa) # 假设最近距离为p3-p1之间的距离
if end1 == 1 and ua <= 0:
pT, distanceT = pointLine(
p1,
[p3, p4],
end1=end3,
end2=end4,
dimension=dimension)
if distanceT <= distance:
distance = distanceT
pa = p1
pb = pT
if end2 == 1 and ua >= 1:
pT, distanceT = pointLine(
p2,
[p3, p4],
end1=end3,
end2=end4,
dimension=dimension)
if distanceT <= distance:
distance = distanceT
pa = p2
pb = pT
if end3 == 1 and ub <= 0:
pT, distanceT = pointLine(
p3,
[p1, p2],
end1=end1,
end2=end2,
dimension=dimension)
if distanceT <= distance:
distance = distanceT
pa = pT
pb = p3
if end4 == 1 and ub >= 1:
pT, distanceT = pointLine(
p4,
[p1, p2],
end1=end1,
end2=end2,
dimension=dimension)
if distanceT <= distance:
distance = distanceT
pa = pT
pb = p4
else:
# print("平行或者共线")
if ((p3-p1).dot(line1) <= (p4-p1).dot(line1) <= 0 and
end4 == 1 and end1 == 1): # line2 在line1后侧且p4距离p1近
# print("#line2 在line1后侧且p4距离p1近")
pa = p1
pb = p4
distance = np.linalg.norm(pb-pa)
elif ((p4-p1).dot(line1) <= (p3-p1).dot(line1) <= 0 and
end3 == 1 and end1 == 1): # line2 在line1后侧且p3距离p1近
# print("#line2 在line1后侧且p3距离p1近")
pa = p1
pb = p3
distance = np.linalg.norm(pb-pa)
elif (0 <= (p3-p2).dot(line1) <= (p4-p2).dot(line1) and
end3 == 1 and end2 == 1): # line2 在line1前侧且p3距离p2近
# print("#line2 在line1前侧且p3距离p2近")
pa = p2
pb = p3
distance = np.linalg.norm(pb-pa)
elif (0 <= (p4-p2).dot(line1) <= (p3-p2).dot(line1) and
end4 == 1 and end2 == 1): # line2 在line1前侧且p4距离p2近
# print("#line2 在line1前侧且p4距离p2近")
pa = p2
pb = p4
distance = np.linalg.norm(pb-pa)
else:
# print("无穷多最近点")
pa = None
pb = None
distance = np.linalg.norm(np.cross((p3-p1), line1)/line1Norm)
# print("pa,pb,distance:", pa, pb, distance)
return pa, pb, distance