在平面坐标系中,判断点 P 是否在线段 AB 上,可以通过以下 5种常用算法 实现,涵盖数学原理、实现步骤和代码示例:
1. 参数化方程法(推荐)
原理
将点 P 表示为线段 AB 的线性插值,检查参数 t 是否在 [0,1] 范围内且满足共线性。
步骤
-
共线性检验 :
计算向量 AP 和 AB 的叉积(二维叉积为标量):
Cross=(xP−xA)(yB−yA)−(yP−yA)(xB−xA)
若 ∣Cross∣>ϵ(微小阈值,如 10−6),则点不共线。
-
参数 t 检查 :
若共线,计算参数:
t=∥AB∥2(xP−xA)(xB−xA)+(yP−yA)(yB−yA)
若 t∈[0,1],则 P 在线段上。
代码实现
python
复制
def is_point_on_segment(A, B, P, epsilon=1e-6):
cross = (P[0] - A[0]) * (B[1] - A[1]) - (P[1] - A[1]) * (B[0] - A[0])
if abs(cross) > epsilon: # 不共线
return False
dot = (P[0] - A[0]) * (B[0] - A[0]) + (P[1] - A[1]) * (B[1] - A[1])
t = dot / ((B[0] - A[0])**2 + (B[1] - A[1])**2)
return 0 <= t <= 1
2. 距离和检验法
原理
若点 P 在线段 AB 上,则 AP+PB=AB。
步骤
- 计算距离 AP、PB、AB。
- 检查 ∣AP+PB−AB∣<ϵ。
代码实现
python
复制
def is_point_on_segment_distance(A, B, P, epsilon=1e-6):
d_AB = ((B[0] - A[0])**2 + (B[1] - A[1])**2)**0.5
d_AP = ((P[0] - A[0])**2 + (P[1] - A[1])**2)**0.5
d_PB = ((B[0] - P[0])**2 + (B[1] - P[1])**2)**0.5
return abs(d_AP + d_PB - d_AB) < epsilon
3. 直线方程代入法
原理
将 P 的坐标代入线段 AB 的直线方程,检查是否满足且位于端点之间。
步骤
- 若线段非垂直(xA=xB),检查斜率一致性:xP−xAyP−yA=xB−xAyB−yA
- 若线段垂直(xA=xB),检查 xP=xA 且 yP 在 [yA,yB] 内。
- 最后检查 P 的坐标是否在 A 和 B 的范围内。
代码实现
python
复制
def is_point_on_segment_equation(A, B, P, epsilon=1e-6):
if abs(B[0] - A[0]) < epsilon: # 垂直线段
return abs(P[0] - A[0]) < epsilon and min(A[1], B[1]) <= P[1] <= max(A[1], B[1])
else:
slope = (B[1] - A[1]) / (B[0] - A[0])
on_line = abs((P[1] - A[1]) - slope * (P[0] - A[0])) < epsilon
in_range = min(A[0], B[0]) <= P[0] <= max(A[0], B[0])
return on_line and in_range
4. 边界框快速排除法
原理
先检查 P 是否在线段 AB 的轴对齐包围盒(AABB)内,再结合共线性检验。
步骤
- 检查 P 的坐标是否满足:min(xA,xB)≤xP≤max(xA,xB)min(yA,yB)≤yP≤max(yA,yB)
- 若在包围盒内,再进行共线性检验(如方法1)。
代码实现
python
复制
def is_point_on_segment_bbox(A, B, P, epsilon=1e-6):
in_bbox = (min(A[0], B[0]) <= P[0] <= max(A[0], B[0])) and \
(min(A[1], B[1]) <= P[1] <= max(A[1], B[1]))
if not in_bbox:
return False
cross = (P[0] - A[0]) * (B[1] - A[1]) - (P[1] - A[1]) * (B[0] - A[0])
return abs(cross) < epsilon
5. 面积法(三角形面积为零)
原理
若 P 在线段 AB 上,则三角形 ABP 的面积为 0。
步骤
- 计算叉积的绝对值(即平行四边形面积):Area=∣(xA(yB−yP)+xB(yP−yA)+xP(yA−yB))∣
- 若 Area<ϵ,则共线,再检查坐标范围。
代码实现
python
复制
def is_point_on_segment_area(A, B, P, epsilon=1e-6):
area = abs(A[0]*(B[1] - P[1]) + B[0]*(P[1] - A[1]) + P[0]*(A[1] - B[1]))
if area > epsilon:
return False
return min(A[0], B[0]) <= P[0] <= max(A[0], B[0]) and \
min(A[1], B[1]) <= P[1] <= max(A[1], B[1])
算法对比
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
参数化方程法 | 高精度,直接计算参数 t | 需计算叉积和点积 | 通用场景(推荐) |
距离和检验法 | 直观易理解 | 浮点误差敏感 | 快速近似判断 |
直线方程法 | 显式处理垂直/水平线段 | 需分情况处理斜率 | 已知线段方向时 |
边界框法 | 快速排除明显不在的点 | 需额外共线性检验 | 预处理优化 |
面积法 | 数学简洁 | 计算面积后仍需范围检查 | 教学演示 |
总结
- 推荐方法1(参数化方程法):综合精度和效率,适合大多数场景。
- 性能优化:先用方法4(边界框法)快速排除,再使用方法1精确判断。
- 特殊处理:垂直线段或水平线段可简化计算(如直接比较坐标)。
通过上述方法,可灵活应对不同需求下的点与线段位置关系判断问题。