如何判断两条线段是否相交

本篇是在 【C++笔记】如何判断2个线段相交 的基础上加上自己的理解和实践总结出的判断两线段是否相交的方法。

判断两条线段是否相交

先附上判断函数

bool judge(int Ax1,int Ay1,int Ax2,int Ay2,int Bx1,int By1,int Bx2,int By2)
{
    if(
       ( max(Ax1,Ax2)>=min(Bx1,Bx2)&&min(Ax1,Ax2)<=max(Bx1,Bx2) )&&  //判断x轴投影
       ( max(Ay1,Ay2)>=min(By1,By2)&&min(Ay1,Ay2)<=max(By1,By2) )    //判断y轴投影
      )
    {
        if(
            ( (Bx1-Ax1)*(Ay2-Ay1)-(By1-Ay1)*(Ax2-Ax1) ) *          //判断B是否跨过A
            ( (Bx2-Ax1)*(Ay2-Ay1)-(By2-Ay1)*(Ax2-Ax1) ) <=0 &&
            ( (Ax1-Bx1)*(By2-By1)-(Ay1-By1)*(Bx2-Bx1) ) *          //判断A是否跨过B
            ( (Ax2-Bx1)*(By2-By1)-(Ay2-By1)*(Bx2-Bx1) ) <=0
          )
        {
            return 1;
        }
        else
            return 0;
    }
    else
        return 0;
}

判断一共分为两步,即code中的第一个if和第二个if
第一步:判断两线段在x轴和y轴的投影是否有交,有任何一条轴没有交点就不可能相交。(快速排斥实验)
第二步:判断两条直线是否相互跨过,用跨立来判断,具体用到的知识是向量积。(跨立实验)

第一步:快速排斥实验

很好理解吧,如果两线段在x,y的投影都不重合,是不可能会相交的
求解的方法也有很多种,这里我就介绍我理解的这个方法。
拿x轴举例,y轴可类比

投影要有重合(哪怕只是一个点也算),那么两线段中任意一条线段的两端点中x较大的那一个端点的x值一定要大于另一条线段的两端点中x较小的那一个端点的x值,不然这两线段一定是相离的,在x轴投影没有重合。

前面是先A线段对B线段,后面是B线段对A线段

max(Ax1,Ax2)>=min(Bx1,Bx2)&&min(Ax1,Ax2)<=max(Bx1,Bx2)//判断x
max(Ay1,Ay2)>=min(By1,By2)&&min(Ay1,Ay2)<=max(By1,By2)//判断y

我还有一种非正式但很好理解的说法
我们把投影到x轴上的投影线段看作两个队伍,这两个队伍要各派一个队员进行比赛,投影线段上的每一个点就是一个队员,点对应的值就是这个队员的能力,两投影线段有重合说明两个队伍都有获胜或平手的可能,怎么样才会出现两个队伍都有获胜或平手可能呢?那就是两个队伍中任意一个队伍能力最强的选手能力要>=另一个队伍中能力最弱的选手,即max(A)>=min(B)&&max(B)>=min(A)。这样,只要A派出最强的,B派出最弱的,A就会获胜或平手;B派出最强的,A派出最弱的,B就会获胜或平手。

第二步:跨立实验

两个坐标A(x1,y1),B(x2,y2),那么AxB的向量积就是x1y2-y1x2。
我们假定一个向量积R,R=x1y2-y1x2。
R<0 说明A在B的逆时针方向
R=0 说明A与B共线,可能正向也可能反向
R>0 说明A在B的顺时针方向

我们证明两线段的跨立就需要证明A跨立B且B跨立A
如何证明跨立?
我们以B跨立A举例
B跨立A的意思就是B线段与A所在的直线有交点。
我们在A的两端点中任意选一个端点,将它与B的两个端点相连得到L1,L2
在这里插入图片描述
若此时A线段向量在L1,L2的中间或L1,L2的边上,就能说明B跨立A
即L1,L2在A的不同的顺逆时针方向,我们就可以分别求出两个L的向量积,再将他们相乘,如果结果<=0,即向量积异号或有0。

if(
            ( (Bx1-Ax1)*(Ay2-Ay1)-(By1-Ay1)*(Ax2-Ax1) ) *          //判断B是否跨过A
            ( (Bx2-Ax1)*(Ay2-Ay1)-(By2-Ay1)*(Ax2-Ax1) ) <=0 &&
            ( (Ax1-Bx1)*(By2-By1)-(Ay1-By1)*(Bx2-Bx1) ) *          //判断A是否跨过B
            ( (Ax2-Bx1)*(By2-By1)-(Ay2-By1)*(Bx2-Bx1) ) <=0
          )

完整代码

#include <bits/stdc++.h>
using namespace std;
bool judge(int Ax1,int Ay1,int Ax2,int Ay2,int Bx1,int By1,int Bx2,int By2);
int main()
{
    int Ax1,Ay1,Ax2,Ay2;
    int Bx1,By1,Bx2,By2;
    while(cin >> Ax1 >> Ay1 >> Ax2 >> Ay2 >> Bx1 >> By1 >> Bx2 >> By2)
    {
        if(judge(Ax1,Ay1,Ax2,Ay2,Bx1,By1,Bx2,By2))
            cout << "YES!" << endl ;
        else
            cout << "NO" << endl ;
    }
    return 0;
}
bool judge(int Ax1,int Ay1,int Ax2,int Ay2,int Bx1,int By1,int Bx2,int By2)
{
    if(
       ( max(Ax1,Ax2)>=min(Bx1,Bx2)&&min(Ax1,Ax2)<=max(Bx1,Bx2) )&&  //判断x轴投影
       ( max(Ay1,Ay2)>=min(By1,By2)&&min(Ay1,Ay2)<=max(By1,By2) )    //判断y轴投影
      )
    {
        if(
            ( (Bx1-Ax1)*(Ay2-Ay1)-(By1-Ay1)*(Ax2-Ax1) ) *          //判断B是否跨过A
            ( (Bx2-Ax1)*(Ay2-Ay1)-(By2-Ay1)*(Ax2-Ax1) ) <=0 &&
            ( (Ax1-Bx1)*(By2-By1)-(Ay1-By1)*(Bx2-Bx1) ) *          //判断A是否跨过B
            ( (Ax2-Bx1)*(By2-By1)-(Ay2-By1)*(Bx2-Bx1) ) <=0
          )
        {
            return 1;
        }
        else
            return 0;
    }
    else
        return 0;
}

关于代码优化

【C++笔记】如何判断2个线段相交 中已经有谈到,如果不要第一步,可能会出现两条共线但不相交的线段判断的错误,因为共线后向量积都为0,这种情况可以在第一步中被排除掉。

如果将第二个if的<=0改为<0呢,不就排除了共线不相交了吗?但这样的话我们把刚好相交(一点相交)和共线相交的情况也都排除了。

那我们允许向量积最多一个为0行不行呢,同样,这样我们把共线相交的情况和两线段共用一个端点的情况也排除了

暂时还没有想出更优的代码

相关题目

P922

#include <bits/stdc++.h>
using namespace std;
bool judge(int Ax1,int Ay1,int Ax2,int Ay2,int Bx1,int By1,int Bx2,int By2);
int main()
{
    int Ax1,Ay1,Ax2,Ay2;
    int Bx1,By1,Bx2,By2;
    int n;
    while(cin >> n && n)
    {
        while(n--)
        {
            cin >> Ax1 >> Ay1 >> Ax2 >> Ay2 >> Bx1 >> By1 >> Bx2 >> By2;
            if(judge(Ax1,Ay1,Ax2,Ay2,Bx1,By1,Bx2,By2))
                cout << "YES" << endl ;
            else
                cout << "no" << endl ;
        }
    }
    return 0;
}
bool judge(int Ax1,int Ay1,int Ax2,int Ay2,int Bx1,int By1,int Bx2,int By2)
{
    if(
        ( max(Ax1,Ax2)>=min(Bx1,Bx2)&&min(Ax1,Ax2)<=max(Bx1,Bx2) )&&  //判断x轴投影
        ( max(Ay1,Ay2)>=min(By1,By2)&&min(Ay1,Ay2)<=max(By1,By2) )    //判断y轴投影
    )
    {
        if(
            ( (Bx1-Ax1)*(Ay2-Ay1)-(By1-Ay1)*(Ax2-Ax1) ) *          //判断B是否跨过A
            ( (Bx2-Ax1)*(Ay2-Ay1)-(By2-Ay1)*(Ax2-Ax1) ) <=0 &&
            ( (Ax1-Bx1)*(By2-By1)-(Ay1-By1)*(Bx2-Bx1) ) *          //判断A是否跨过B
            ( (Ax2-Bx1)*(By2-By1)-(Ay2-By1)*(Bx2-Bx1) ) <=0
        )
        {
            return 1;
        }
        else
            return 0;
    }
    else
        return 0;
}

发布了44 篇原创文章 · 获赞 13 · 访问量 2326

猜你喜欢

转载自blog.csdn.net/NEFU_kadia/article/details/104462906