判断两线段是否相交——快速排斥与跨立实验
生活随笔
收集整理的這篇文章主要介紹了
判断两线段是否相交——快速排斥与跨立实验
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
如何判斷兩條線段是否相交呢?如果是我們?nèi)ソ鉀Q這個(gè)問(wèn)題,用眼睛很容易就看出來(lái)了,但是如果用計(jì)算機(jī)來(lái)解決這個(gè)問(wèn)題,該怎么辦呢?下面介紹兩個(gè)方法,這兩個(gè)方法結(jié)合起來(lái)就能完美解決這個(gè)問(wèn)題了。 一、快速排斥
對(duì)于兩條線段,我們以這兩條線段為對(duì)角線各自作一個(gè)矩形,如圖所示,如果這兩個(gè)矩形沒有相交的部分那么這兩條線段一定不相交,這樣我們可以排除一部分不相交的情況了。
那么又該怎么判斷這兩個(gè)矩形是否相交呢?這就比判斷線段要簡(jiǎn)單的多了,若:
·線段1下面的端點(diǎn)高于線段2上面的端點(diǎn);
·線段1上面的端點(diǎn)低于線段2下面的端點(diǎn);
·線段1左面的端點(diǎn)位于線段2右面的端點(diǎn)的右邊;
·線段1右面的端點(diǎn)位于線段2左面的端點(diǎn)的左邊;
那么我們就可以說(shuō)這兩個(gè)矩形不相交,即這兩個(gè)線段不相交,用代碼實(shí)現(xiàn)如下:
1 bool quick_judge(point a,point b,point c,point d) 2 { 3 if(a.y>c.y||b.y<d.y||a.x>d.x||b.x<c.x) 4 return false; 5 else return true; 6 }
·a,b向量構(gòu)成的平行四邊形的面積。
·如果k>0時(shí),那么a正旋轉(zhuǎn)到b的角度為<180°,如果k<0,那么a正旋轉(zhuǎn)到b的角度為>180°,如果k=0 那么a,b向量平行。 跨立實(shí)驗(yàn)用到的就是上面的第二個(gè)性質(zhì)。 我們?cè)賮?lái)看跨立實(shí)驗(yàn),簡(jiǎn)單來(lái)說(shuō),就是兩條相交線段,其中一條的兩個(gè)點(diǎn)一定在另一條的兩邊,如下圖。
那么如何去判斷呢,這就需要用到我們上面說(shuō)到的叉乘了。如在下圖中,我們選擇線段AB為基準(zhǔn),然后去判斷AC×AB與AD×AB是否是同向的,若不是同向的,則證明了B,D兩點(diǎn)在線段AB的兩邊。即?
用代碼來(lái)實(shí)現(xiàn): 1 bool cross_judge(point a,point b,point c,point d) 2 { 3 const double eps=1e-9; 4 double ac,ad,cb,ca; 5 ac=(c.x-a.x)*(b.y-a.y)-(c.y-a.y)*(b.x-a.x); 6 ad=(d.x-a.x)*(b.y-a.y)-(d.y-a.y)*(b.x-a.x); 7 ca=(a.x-c.x)*(d.y-c.y)-(a.y-c.y)*(d.x-c.x); //保險(xiǎn)起見,把另一條邊也判斷一下 8 cb=(b.x-c.x)*(d.y-c.y)-(b.y-c.y)*(d.x-c.x); 9 if(ac*ad<eps&&ca*cb<eps) return true; 10 else return false; 11 }
對(duì)于兩條線段,我們以這兩條線段為對(duì)角線各自作一個(gè)矩形,如圖所示,如果這兩個(gè)矩形沒有相交的部分那么這兩條線段一定不相交,這樣我們可以排除一部分不相交的情況了。
那么又該怎么判斷這兩個(gè)矩形是否相交呢?這就比判斷線段要簡(jiǎn)單的多了,若:
·線段1下面的端點(diǎn)高于線段2上面的端點(diǎn);
·線段1上面的端點(diǎn)低于線段2下面的端點(diǎn);
·線段1左面的端點(diǎn)位于線段2右面的端點(diǎn)的右邊;
·線段1右面的端點(diǎn)位于線段2左面的端點(diǎn)的左邊;
那么我們就可以說(shuō)這兩個(gè)矩形不相交,即這兩個(gè)線段不相交,用代碼實(shí)現(xiàn)如下:
1 bool quick_judge(point a,point b,point c,point d) 2 { 3 if(a.y>c.y||b.y<d.y||a.x>d.x||b.x<c.x) 4 return false; 5 else return true; 6 }
但是僅這一種判斷方式無(wú)法解決我們的問(wèn)題,有反例如下圖,兩矩形相交但是線段并沒有相交,這就需要我們用第二種方法來(lái)加以輔助。
二、跨立實(shí)驗(yàn) 首先簡(jiǎn)單介紹一下向量的叉乘: 假設(shè)有兩個(gè)二維向量a,b,那么它們的的叉乘結(jié)果為a×b=a.x*b.y-b.x*a.y,我們可以通過(guò)這個(gè)值得到很多有用的性質(zhì):·a,b向量構(gòu)成的平行四邊形的面積。
·如果k>0時(shí),那么a正旋轉(zhuǎn)到b的角度為<180°,如果k<0,那么a正旋轉(zhuǎn)到b的角度為>180°,如果k=0 那么a,b向量平行。 跨立實(shí)驗(yàn)用到的就是上面的第二個(gè)性質(zhì)。 我們?cè)賮?lái)看跨立實(shí)驗(yàn),簡(jiǎn)單來(lái)說(shuō),就是兩條相交線段,其中一條的兩個(gè)點(diǎn)一定在另一條的兩邊,如下圖。
那么如何去判斷呢,這就需要用到我們上面說(shuō)到的叉乘了。如在下圖中,我們選擇線段AB為基準(zhǔn),然后去判斷AC×AB與AD×AB是否是同向的,若不是同向的,則證明了B,D兩點(diǎn)在線段AB的兩邊。即?
用代碼來(lái)實(shí)現(xiàn): 1 bool cross_judge(point a,point b,point c,point d) 2 { 3 const double eps=1e-9; 4 double ac,ad,cb,ca; 5 ac=(c.x-a.x)*(b.y-a.y)-(c.y-a.y)*(b.x-a.x); 6 ad=(d.x-a.x)*(b.y-a.y)-(d.y-a.y)*(b.x-a.x); 7 ca=(a.x-c.x)*(d.y-c.y)-(a.y-c.y)*(d.x-c.x); //保險(xiǎn)起見,把另一條邊也判斷一下 8 cb=(b.x-c.x)*(d.y-c.y)-(b.y-c.y)*(d.x-c.x); 9 if(ac*ad<eps&&ca*cb<eps) return true; 10 else return false; 11 }
至此,我們將這兩個(gè)方法結(jié)合一下,就可以解決我們的問(wèn)題了。
三、相關(guān)題目 1.例題 ZOJ P1648 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const double eps=1e-9; 6 7 struct segment 8 { 9 double x1,x2,y1,y2; 10 }seg[2005]; 11 12 bool judge(segment a,segment b) 13 { 14 if(min(a.x1,a.x2)<max(b.x1,b.x2)&&max(a.x1,a.x2)>min(b.x1,b.x2)&&min(a.y1,a.y2)<max(b.y1,b.y2)&&max(a.y1,a.y2)>min(b.y1,b.y2)) 15 { 16 double v1,v2,v3,v4; 17 v1=(b.x1-a.x1)*(a.y2-a.y1)-(b.y1-a.y1)*(a.x2-a.x1); 18 v2=(b.x2-a.x1)*(a.y2-a.y1)-(b.y2-a.y1)*(a.x2-a.x1); 19 v3=(a.x1-b.x1)*(b.y2-b.y1)-(a.y1-b.y1)*(b.x2-b.x1); 20 v4=(a.x2-b.x1)*(b.y2-b.y1)-(a.y2-b.y1)*(b.x2-b.x1); 21 if(v1*v2<eps&&v3*v4<eps) return true; 22 else return false; 23 } 24 else return false; 25 } 26 27 int main() 28 { 29 int i,j,n,flag=0; 30 while(~scanf("%d",&n)) 31 { 32 flag=0; 33 for(i=0;i<n;i++) scanf("%lf%lf%lf%lf",&seg[i].x1,&seg[i].y1,&seg[i].x2,&seg[i].y2); 34 for(i=0;i<n;i++) 35 for(j=i+1;j<n;j++) 36 { 37 if(judge(seg[i],seg[j])) 38 { 39 flag=1; 40 break; 41 } 42 } 43 if(flag) printf("burned!\n"); 44 else printf("ok!\n"); 45 } 46 return 0; 47 } ZOJ P1648?
Author : Houge Date : 2019.5.31 Update log :?轉(zhuǎn)載于:https://www.cnblogs.com/CSGOBESTGAMEEVER/p/10939868.html
與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的判断两线段是否相交——快速排斥与跨立实验的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 迭代器 生成器 装饰器 匿名函数
- 下一篇: 第二阶段 铁大Facebook——十天冲