判断线段是否相交并求交点
公式推导
设两条线段AB
和CD
,其端点分别为
(
x
a
,
y
a
)
,
(
x
b
,
y
b
)
(
x
c
,
y
c
)
,
(
x
d
,
y
d
)
(x_a,y_a),(x_b,y_b) (x_c,y_c),(x_d,y_d)
(xa,ya),(xb,yb)(xc,yc),(xd,yd).需要判断2条线段是否相交,若相交求交点。
两条直线所在的参数方程为:
{ x = x a + α ( x b − x a ) y = y a + α ( y b − y a ) { x = x c + β ( x d − x c ) y = y c + β ( y d − y c ) (1) \left\{ \begin{aligned} x = x_a + \alpha(x_b-x_a) \\ y = y_a + \alpha(y_b-y_a) \\ \end{aligned} \right. \left\{ \begin{aligned} x = x_c + \beta(x_d-x_c) \\ y = y_c + \beta(y_d-y_c) \end{aligned} \right. \tag{1} {x=xa+α(xb−xa)y=ya+α(yb−ya){x=xc+β(xd−xc)y=yc+β(yd−yc)(1)
若两线段相交,则交点坐标满足以下方程:
{
x
=
x
a
+
α
(
x
b
−
x
a
)
=
x
c
+
β
(
x
d
−
x
c
)
y
=
y
a
+
α
(
y
b
−
y
a
)
=
y
=
y
c
+
β
(
y
d
−
y
c
)
(2)
\left\{ \begin{aligned} x = x_a + \alpha(x_b-x_a)=x_c + \beta(x_d-x_c) \\ y = y_a + \alpha(y_b-y_a)=y = y_c + \beta(y_d-y_c) \end{aligned} \right. \tag{2}
{x=xa+α(xb−xa)=xc+β(xd−xc)y=ya+α(yb−ya)=y=yc+β(yd−yc)(2)
即:
{
α
(
x
b
−
x
a
)
−
β
(
x
d
−
x
c
)
=
x
c
−
x
a
α
(
y
b
−
y
a
)
−
β
(
y
d
−
y
c
)
=
y
c
−
y
a
(3)
\left\{ \begin{aligned} \alpha(x_b-x_a) - \beta(x_d-x_c) = x_c - x_a \\ \alpha(y_b-y_a) - \beta(y_d-y_c) = y_c - y_a \end{aligned} \right. \tag{3}
{α(xb−xa)−β(xd−xc)=xc−xaα(yb−ya)−β(yd−yc)=yc−ya(3)
求行列式,上面方程存在解的条件是:
Δ
=
∣
x
b
−
x
a
−
(
x
d
−
x
c
)
y
b
−
y
a
−
(
y
d
−
y
c
)
∣
(4)
\Delta = \begin{vmatrix} x_b-x_a & -(x_d-x_c) \\ y_b-y_a & -(y_d-y_c) \end{vmatrix} \tag{4}
Δ=∣∣∣∣xb−xayb−ya−(xd−xc)−(yd−yc)∣∣∣∣(4)
若
Δ
=
0
\Delta=0
Δ=0,则方程无解。意味着线段重合或平行。如果
Δ
≠
0
\Delta\neq0
Δ=0,则可求出交点对应的两个参数值:
{
α
=
1
Δ
∣
x
c
−
x
a
−
(
x
d
−
x
c
)
y
c
−
y
a
−
(
y
d
−
y
c
)
∣
β
=
1
Δ
∣
x
b
−
x
a
x
c
−
x
a
y
b
−
y
a
y
c
−
y
a
∣
(5)
\left\{ \begin{aligned} \alpha = \frac{1}{\Delta} \begin{vmatrix} x_c-x_a & -(x_d-x_c) \\ y_c-y_a & -(y_d-y_c) \end{vmatrix} \\ \beta = \frac{1}{\Delta} \begin{vmatrix} x_b-x_a & x_c-x_a \\ y_b-y_a & y_c-y_a \end{vmatrix} \end{aligned} \right. \tag{5}
⎩⎪⎪⎪⎨⎪⎪⎪⎧α=Δ1∣∣∣∣xc−xayc−ya−(xd−xc)−(yd−yc)∣∣∣∣β=Δ1∣∣∣∣xb−xayb−yaxc−xayc−ya∣∣∣∣(5)
需要注意,只有
0
⩽
α
⩽
1
,
0
⩽
β
⩽
1
0\leqslant \alpha \leqslant 1,0\leqslant \beta \leqslant 1
0⩽α⩽1,0⩽β⩽1时两线段才真正相交。否则,交点在两线段或其中某一条线段的延长线上,这时仍然认为是两线段不相交。
将式(5)代入式(1)即可解得两条线段交点。(或者两条线段所在直线的交点)
代码实现
根据上面公式可以很方便求解出线段的交点(或线段所在直线的交点)。由于项目开发经常使用OpenCV,因此给出一份基于OpenCV数据结构的代码实现.
//判断线段是否相交,并求交点
//Input Param:pa,pb--line segment a's 2 points;
// pc,pd--line segments b's 2 points;
//Output Param:crossP--cross point
//Return: true:line a, b intersect;false:none;
bool GetLineIntersect(const cv::Point2f &pa, const cv::Point2f &pb,
const cv::Point2f &pc, const cv::Point2f &pd,cv::Point2f &crossP)
{
//1°构造行列式.并求解
cv::Point2f dpa = pb-pa;
cv::Point2f dpd = pc-pd;
auto delta = dpa.cross(dpd);//(xb-xa)*(yc-yd) - (xc-xd)*(yb-ya)
if(std::abs(delta)<0.0001)//delta=0,重合或平行
return false;
delta = 1.0/delta;
//2°求交点对应参数值
cv::Point2f dp = pc-pa;
auto alpha = delta*(dp.cross(dpd));
auto beta = delta*(dpa.cross(dp));
crossP = pa + alpha*dpa;//交点
return (0<=alpha && alpha<=1) && (0<=beta && beta<=1 );
}
上面代码利用
OpenCV
中点(向量)的叉乘
来代替行列式的求解。建议对照数学公式进行理解!
上面函数返回值用来判断两条线段是否相交,线段相交返回值为true
;线段不相交,但线段所在直线相交,代码同样求出两条直线的交点。