假设线段的两个端点分别为:A、B,另外一点为 P。
问题:判断 P 点是否位于线段 AB 上。
方法1:通过线段确定的直线方程判断。
(1) 在二维空间中,三点坐标表示为:A(xa,ya), B(xb,yb), P(xp,yp)。
AB确定的直线方程(点斜式)为:
k = (yb - ya) * 1 / (xb - xa)
y = k * (x - xa) + ya
P点若位于直线上,首先应该满足直线的方程:
yp = k * (xp - xa) + ya。
若上式成立,说明A、B、P共线,接下来只需判断 P 位于 A 和 B 中间,即
(xa <= xb && xa <= xp <= xb) || (xa > xb && xa >= xp >= xb)
(ya <= yb && ya <= yp <= yb) || (ya > yb && ya >= yp >= yb)
同时成立。否则,P 不在线段AB上。
注意到直线方程中有除法运算,需做一步AB是否重合的判断,否则除数为零。上式稍作变形,便可去掉除法运算,即:
(y - ya) / (yb - ya) = (x - xa) / (xb - xa)
进而
(y - ya) * (xb - xa) - (yb - ya) * (x - xa) = 0。
上式成立的条件,(x,y)与A或B点重合,或者(x,y)、A、B三点共线。接下来再做 P 是否位于AB中间的判断即可。
y = k * (x - xa) + ya
P点若位于直线上,首先应该满足直线的方程:
yp = k * (xp - xa) + ya。
若上式成立,说明A、B、P共线,接下来只需判断 P 位于 A 和 B 中间,即
(xa <= xb && xa <= xp <= xb) || (xa > xb && xa >= xp >= xb)
(ya <= yb && ya <= yp <= yb) || (ya > yb && ya >= yp >= yb)
同时成立。否则,P 不在线段AB上。
(y - ya) / (yb - ya) = (x - xa) / (xb - xa)
进而
(y - ya) * (xb - xa) - (yb - ya) * (x - xa) = 0。
上式成立的条件,(x,y)与A或B点重合,或者(x,y)、A、B三点共线。接下来再做 P 是否位于AB中间的判断即可。
上述变形后的等式,其实表示的是二维平面中向量的一种运算:叉积(cross product),
A x B = xa*yb - ya*xb
由直线方程的启示,不难理解叉积为零,则可判定两向量共线或者一个向量为零。
AB确定的直线方程相应变为:
(y - ya) / (yb - ya) = (x - xa) / (xb - xa) = (z - za) / (zb - za)。
按(1)中所述,将P点代入直线方程成立后,只需再多做一步 z 轴坐标的判断:
(za <= zb && za <= zp <= zb) || (za > zb && za >= zp >= zb)。
所有条件同时成立,则 P 点位于线段 AB 上。
二维空间中的叉积定义已经介绍,三维空间的描述可以参考「4」,叉积可以作为判定两个向量是否共线的一个工具,三维空间中的叉积结果会得到一个新的向量。
先求得两个向量:v1 = B - A, v2 = P - A。然后 v1 和 v2 做叉积:v = v1 x v2,如果 v 是零向量,即 v 的x、y、z分量都为 0,则v1 和 v2 至少有一个为零或者两个向量共线。
if v2 == 0,then P == A;
else if v1 == 0 then A == B AND P doesn't lie in AB ; // v2 != 0
else P,A,B are colinear;
end
如果判定共线后,接下来判断 P 是否位于 AB 内的方式同方法1.
方法3:面积法。
如果A、B、P三点组成的三角形的面积为零,那么可以得出三点共线的结论。
三角形面积可以使用海伦公式直接得出,另外上述v1和v2叉积得到的向量的长度的一半,也是三点组成的三角形的面积(参考叉积的定义)。
不过这两种方式都涉及到开根号的运算,代价较高。
另外在判定了三点共线后,v1 和 v2 的夹角要么0度,要么180度,点积的符号可以用来确定P点的位置,如:v1 ・ v2 < 0。那么,v1 和 v2 夹角为180,反向,P点位于AB之外。
接下来再以 B 点为基准做两个向量v1’ = A - B, v2’ = P - B,若v1’ ・ v2’ > 0,即可断定 P 位于线段AB上。
如果不以 B 为基准做两个向量,可以通过长度来判断,由内积的定义可以知道:
v1 ・ v2 = |v1| |v2| cos<v1, v2> = |v1| |v2| cos(0) = |v1| |v2|。
如果v1 ・ v2 > |v1|*|v1| = v1 ・ v1 => |v2| > |v1|,即PA的长度大于BA,又PA和BA同向,那么P点一定位于AB之外。
不过上述的点积判定符号的方法,包含了许多乘法的运算,进行坐标比较的方法反而更加直接简单。综上所述,方法2更值得推荐。
[1] http://stackoverflow.com/questions/328107/how-can-you-determine-a-point-is-between-two-other-points-on-a-line-segment
[2] http://stackoverflow.com/questions/7050186/find-if-point-lay-on-line-segment