计算几何之判断线段相交

链接:http://blog.csdn.net/chenbang110/article/details/7742535

给定两个点:

typedef  struct {

  double  x, y;

} Point;

Point A1,A2,B1,B2;

首先引入两个实验:

a.快速排斥实验

设以线段A1A2和线段B1B2为对角线的矩形为M,N;

若M,N 不相交,则两个线段显然不相交;

所以:满足第一个条件时:两个线段可能相交。

 

b.跨立实验

如果两线段相交,则两线段必然相互跨立对方.若A1A2跨立B1B2,则矢量( A1 - B1 ) 和(A2-B1)位于矢量(B2-B1)的两侧,

即(A1-B1) × (B2-B1) * (A2-B1) × (B2-B1)<0。

上式可改写成(A1-B1) × (B2-B1) * (B2-B1) × (A2-A1)>0。

应该判断两次,即两条线段都要为直线,判断另一直线的两端点是否在它两边,若是则两线段相交。

若积极满跨立实验是不行的,如下面的情况:

即两条线段在同一条直线上。所以我们要同时满足两次跨立和快速排斥实验。

 

总体分析:

当(A1-B1) × (B2-B1)=0时,说明(A1-B1)和(B2-B1)共线,但是因为已经通过快速排斥试验,所以 A1一定在线段 B1B2上;同理,(B2-B1)×(A2-B1)=0 说明A2一定在线段B1B2上。所以判断A1A2跨立B1B2的依据是:(A1-B1) × (B2-B1) * (B2-B1) × (A2-B1) >= 0。

同理判断B1B2跨立A1A2的依据是:(B1-A1) × (A2-A1) * (A2-A1) × (B2-A1) >= 0。

如图:

应用:

1.       判断两个线段相交

2.       判断线段与直线相交

3.       判断点在矩形内

代码:

struct node  
{  
    double x,y;  
};  
node P[35][105];  
double Cross_Prouct(node A,node B,node C)       //  计算BA叉乘CA;  
{  
    return (B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x);  
}  
bool Intersect(node A,node B,node C,node D)     //  通过叉乘判断线段是否相交;  
{  
    if(min(A.x,B.x)<=max(C.x,D.x)&&         //  快速排斥实验;  
       min(C.x,D.x)<=max(A.x,B.x)&&  
       min(A.y,B.y)<=max(C.y,D.y)&&  
       min(C.y,D.y)<=max(A.y,B.y)&&  
       Cross_Prouct(A,B,C)*Cross_Prouct(A,B,D)<0&&      //  跨立实验;  
       Cross_Prouct(C,D,A)*Cross_Prouct(C,D,B)<0)       //  叉乘异号表示在两侧;  
       return true;  
    else return false;  
}

这个可能好理解

    #include<iostream>  
    #include<cstdio>  
    #include<cstring>  
    #include<algorithm>  
    #include<set>  
    #include<vector>  
    #include<cmath>  
    using namespace std;  
    typedef long long ll;  
    struct Point{  
        double x,y;  
    };  
    double mult(Point a, Point b, Point c)  
    {  
        return (a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y);  
    }  
    //aa, bb为一条线段两端点 cc, dd为另一条线段的两端点 相交返回true, 不相交返回false  
    bool intersect(Point aa, Point bb, Point cc, Point dd)  
    {  
        if ( max(aa.x, bb.x)<min(cc.x, dd.x) )  
            return false;  
        if ( max(cc.x, dd.x)<min(aa.x, bb.x) )  
            return false;  
        if ( max(aa.y, bb.y)<min(cc.y, dd.y) )  
            return false;  
        if ( max(cc.y, dd.y)<min(aa.y, bb.y) )  
            return false;  
        //如果 向量 ac ab 和 ad ab反向 或者共线,  
        //并且 向量 ca cd 和 cb cd 反向 则判断相交(跨立)  
        if(mult(cc,bb,aa) * mult(dd,bb,aa) <= 0  
           && mult(aa,dd,cc) * mult(bb,dd,cc) <= 0)  
            return true;  
        return false;  
      
      
        //第二种方法 如果本该同向的向量反向了 直接return false;  
       /*if ( mult(cc, bb, aa)*mult(bb, dd, aa)<0 ) 
            return false; 
        if ( mult(aa, dd, cc)*mult(dd, bb, cc)<0 ) 
            return false; 
        return true;*/  
      
    }  
    double dis(Point& a,Point& b){  
        return sqrt((a.x - b.x)*(a.x - b.x) +(a.y - b.y)*(a.y - b.y) );  
    }  
    int main(){  
        int T; scanf("%d",&T);  
        while(T--){  
            Point a,b,c,d;  
            cin>>a.x>>a.y>>b.x>>b.y>>c.x>>c.y>>d.x>>d.y;  
            if(intersect(a,b,c,d))  
                printf("Interseetion\n");  
            else  
                printf("Not Interseetion\n");  
        }  
    }  


  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值