在计算机图形学、几何计算和工程领域中,经常需要计算两条线段的交点。本文将详细介绍线段交点计算的原理、公式推导过程以及代码实现。
原理
要计算两条线段的交点,我们首先需要确定这两条线段是否相交。线段可以由两个端点定义,设线段 A B AB AB的端点为 A ( x 1 , y 1 ) A(x_1, y_1) A(x1,y1)和 B ( x 2 , y 2 ) B(x_2, y_2) B(x2,y2),线段 C D CD CD的端点为 C ( x 3 , y 3 ) C(x_3, y_3) C(x3,y3)和 D ( x 4 , y 4 ) D(x_4, y_4) D(x4,y4)。
推导过程
-
确定线段的方程:线段(AB)和(CD)可以表示为参数方程:
A B : { x = x 1 + t ( x 2 − x 1 ) y = y 1 + t ( y 2 − y 1 ) ( 0 ≤ t ≤ 1 ) AB: \begin{cases} x = x_1 + t(x_2 - x_1) \\ y = y_1 + t(y_2 - y_1) \end{cases} \quad (0 \leq t \leq 1) AB:{x=x1+t(x2−x1)y=y1+t(y2−y1)(0≤t≤1)C D : { x = x 3 + s ( x 4 − x 3 ) y = y 3 + s ( y 4 − y 3 ) ( 0 ≤ s ≤ 1 ) CD: \begin{cases} x = x_3 + s(x_4 - x_3) \\ y = y_3 + s(y_4 - y_3) \end{cases} \quad (0 \leq s \leq 1) CD:{x=x3+s(x4−x3)y=y3+s(y4−y3)(0≤s≤1)
-
设置交点条件:如果线段相交,那么存在 t t t和 s s s使得:
x 1 + t ( x 2 − x 1 ) = x 3 + s ( x 4 − x 3 ) x_1 + t(x_2 - x_1) = x_3 + s(x_4 - x_3) x1+t(x2−x1)=x3+s(x4−x3)
y 1 + t ( y 2 − y 1 ) = y 3 + s ( y 4 − y 3 ) y_1 + t(y_2 - y_1) = y_3 + s(y_4 - y_3) y1+t(y2−y1)=y3+s(y4−y3) -
解方程组:解这个方程组得到 t t t和 s s s。
t = ( x 3 − x 1 ) ( y 4 − y 3 ) − ( y 3 − y 1 ) ( x 4 − x 3 ) ( x 2 − x 1 ) ( y 4 − y 3 ) − ( y 2 − y 1 ) ( x 4 − x 3 ) t = \frac{(x3 - x1)(y4 - y3) - (y3 - y1)(x4 - x3)}{(x2 - x1)(y4 - y3) - (y2 - y1)(x4 - x3)} t=(x2−x1)(y4−y3)−(y2−y1)(x4−x3)(x3−x1)(y4−y3)−(y3−y1)(x4−x3)
s = ( x 3 − x 1 ) ( y 2 − y 1 ) − ( y 3 − y 1 ) ( x 2 − x 1 ) ( x 2 − x 1 ) ( y 4 − y 3 ) − ( y 2 − y 1 ) ( x 4 − x 3 ) s = \frac{(x3 - x1)(y2 - y1) - (y3 - y1)(x2 - x1)}{(x2 - x1)(y4 - y3) - (y2 - y1)(x4 - x3)} s=(x2−x1)(y4−y3)−(y2−y1)(x4−x3)(x3−x1)(y2−y1)−(y3−y1)(x2−x1)
如果 t t t和 s s s都在 [ 0 , 1 ] [0, 1] [0,1]区间内,那么线段相交。 -
计算交点:交点的坐标为:
P ( x 1 + t ( x 2 − x 1 ) , y 1 + t ( y 2 − y 1 ) ) P\left(x_1 + t(x_2 - x_1), y_1 + t(y_2 - y_1)\right) P(x1+t(x2−x1),y1+t(y2−y1))
代码实现
Python代码
def line_intersection(A, B, C, D):
x1, y1 = A
x2, y2 = B
x3, y3 = C
x4, y4 = D
# 计算分母
denominator = (x2 - x1) * (y4 - y3) - (y2 - y1) * (x4 - x3)
if denominator == 0:
return None # 线段平行或重合
# 计算t和s
t = ((x3 - x1) * (y4 - y3) - (y3 - y1) * (x4 - x3)) / denominator
s = ((x3 - x1) * (y2 - y1) - (y3 - y1) * (x2 - x1)) / denominator
# 检查t和s是否在[0, 1]区间内
if 0 <= t <= 1 and 0 <= s <= 1:
return (x1 + t * (x2 - x1), y1 + t * (y2 - y1))
else:
return None # 线段不相交
C++代码
#include <iostream>
struct Point {
double x, y;
};
Point line_intersection(Point A, Point B, Point C, Point D) {
double x1 = A.x, y1 = A.y;
double x2 = B.x, y2 = B.y;
double x3 = C.x, y3 = C.y;
double x4 = D.x, y4 = D.y;
// 计算分母
double denominator = (x2 - x1) * (y4 - y3) - (y2 - y1) * (x4 - x3);
if (denominator == 0) {
return {0, 0}; // 线段平行或重合
}
// 计算t和s
double t = ((x3 - x1) * (y4 - y3) - (y3 - y1) * (x4 - x3)) / denominator;
double s = ((x3 - x1) * (y2 - y1) - (y3 - y1) * (x2 - x1)) / denominator;
// 检查t和s是否在[0, 1]区间内
if (0 <= t && t <= 1 && 0 <= s && s <= 1) {
return {x1 + t * (x2 - x1), y1 + t * (y2 - y1)};
} else {
return {0, 0}; // 线段不相交
}
}
应用示例
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
def line_intersection(A, B, C, D):
x1, y1 = A
x2, y2 = B
x3, y3 = C
x4, y4 = D
# 计算分母
denominator = (x2 - x1) * (y4 - y3) - (y2 - y1) * (x4 - x3)
if denominator == 0:
return None # 线段平行或重合
# 计算t和s
t = ((x3 - x1) * (y4 - y3) - (y3 - y1) * (x4 - x3)) / denominator
s = ((x3 - x1) * (y2 - y1) - (y3 - y1) * (x2 - x1)) / denominator
# 检查t和s是否在[0, 1]区间内
if 0 <= t <= 1 and 0 <= s <= 1:
return (x1 + t * (x2 - x1), y1 + t * (y2 - y1))
else:
return None # 线段不相交
# 手动定义线段
segments = [
((0.3, 0.3), (0.8, 0.8), (0.2, 0.8), (0.8, 0.2)),
((0.2, 0.2), (0.8, 0.2), (0.5, 0.5), (0.8, 0.5)),
((0.1, 0.1), (0.7, 0.7), (0.6, 0.22), (0.8, 0.2)),
((0.3, 0.3), (0.7, 0.7), (0.4, 0.84), (0.8, 0.2)),
((0.2, 0.5), (0.8, 0.4), (0.6, 0.8), (0.7, 0.2)),
((0.2, 0.3), (0.8, 0.8), (0.5, 0.2), (0.8, 0.2))
]
fig, ax = plt.subplots()
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.set_aspect('equal')
# 初始化绘图
line1, = ax.plot([], [], 'b-', animated=True)
line2, = ax.plot([], [], 'b-', animated=True)
point, = ax.plot([], [], 'ro', animated=True)
text = ax.text(0.5, 0.9, '', transform=ax.transAxes, ha='center')
def init():
line1.set_data([], [])
line2.set_data([], [])
point.set_data([], [])
text.set_text('')
return line1, line2, point, text
def animate(i):
A, B, C, D = segments[i]
intersection = line_intersection(A, B, C, D)
line1.set_data([A[0], B[0]], [A[1], B[1]])
line2.set_data([C[0], D[0]], [C[1], D[1]])
if intersection:
line1.set_color('r')
line2.set_color('r')
point.set_data([intersection[0]], [intersection[1]])
text.set_text(f'Intersection: ({intersection[0]:.2f}, {intersection[1]:.2f})')
else:
line1.set_color('b')
line2.set_color('b')
point.set_data([], []) # 修改这里
text.set_text('No Intersection')
return line1, line2, point, text
ani = FuncAnimation(fig, animate, init_func=init, frames=len(segments), interval=500, blit=True)
# 保存动图
ani.save('line_segments.gif', writer='pillow', fps=1)
plt.show()
运行效果如下