幾何經常使用算法與判斷線段相交【轉】

下面這個函數在我寫的計算幾何庫函數裏面有,那個庫能夠在http://algorithm.126.com/的資源中心   -   代碼角   找到。         算法

  算法簡單說明:    編程

  首先判斷以兩條線段爲對角線的矩形是否相交,若是不相交兩條線段確定也不相交。 函數

(所謂以a1b2爲對角錢的矩形就是以兩邊長爲|a1.x  b2.x||a1.y  b2.y|以及a1b2爲對角線的矩形)。 測試

若是相交的話,利用矢量叉乘判斷兩條線段是否相互跨越,若是相互跨越顯然就相交,反之則不相交。算法不難,可是一些特殊狀況須要考慮到,好比兩條相段共線且在斷點處相交。下面的代碼通過測試了,應該沒有bug,若是你真的發現了bug請告訴我:)         spa

  /********************************************************   * *        * 返回(P1-P0)*(P2-P0)的叉積。 *    排序

    * 若結果爲正,則<P0,P1><P0,P2>的順時針方向; *        * 若爲0<P0,P1><P0,P2>共線; *    ip

    * 若爲負則<P0,P1><P0,P2>的在逆時針方向; *        * 能夠根據這個函數肯定兩條線段在交點處的轉向, *    資源

    * 好比肯定p0p1p1p2p1處是左轉仍是右轉,只要     *    it

    *               (p2-p0)*(p1-p0),若<0則左轉,>0則右轉,=0   *    bug

    *               共線                                                                                     *        * *    

  \********************************************************/         

  float   multiply(TPoint   p1,TPoint   p2,TPoint   p0)      {    

  return((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));      }                   

  //肯定兩條線段是否相交    

  int   intersect(TLineSeg   u,TLineSeg   v)      {    

   return(   (max(u.a.x,u.b.x)>=min(v.a.x,v.b.x))&& 

                      (max(v.a.x,v.b.x)>=min(u.a.x,u.b.x))&& 

                          (max(u.a.y,u.b.y)>=min(v.a.y,v.b.y))&&    

                (max(v.a.y,v.b.y)>=min(u.a.y,u.b.y))&&  (以兩線段爲對角線的矩形是否相交)                      (multiply(v.a,u.b,u.a)*multiply(u.b,v.b,u.a)>=0)&&    

                         (multiply(u.a,v.b,v.a)*multiply(v.b,u.b,v.a)>=0));(是否相互跨立)      }    

 

忘記了說明TPointTLineSeg的定義了:

 struct   TPoint   {      float   x,y;      };         

 

  struct   TLineSeg   {      TPoint   a,b;      };    

上面的算法避免了除法運算,因此不會出現計算偏差  

NOwcan兄的方法雖然簡單,可是求兩條直線的交點須要用到除法,當兩條線段相交可是很接近平行的時候,會有精度上的偏差,因此個人方法不用除法更好一點。這是計算幾何中的經典算法   

計算幾何經常使用算法(一共23)         

  1.   矢量減法         

  設二維矢量   P   =   x1,y1   Q   =   (x2,y2)      

  則矢量減法定義爲:   P   -   Q   =   (   x1   -   x2   ,   y1   -   y2   )      顯然有性質   P   -   Q   =   -   (   Q   -   P   )    

  如不加說明,下面全部的點都看做矢量,兩點的減法就是矢量相減;         

  2.矢量叉積         

  設矢量P   =   x1,y1   Q   =   (x2,y2)    

  則矢量叉積定義爲:     P   ×   Q   =   x1*y2   -   x2*y1       獲得的是一個標量    

  顯然有性質   P   ×   Q   =   -   (   Q   ×   P   )       P   ×   (   -   Q   )   =   -   (   P   ×   Q   )      如不加說明,下面全部的點都看做矢量,點的乘法看做矢量叉積;      叉乘的重要性質:    

    >      P   ×   Q     >   0   ,     P   Q的順時針方向        >      P   ×   Q     <   0   ,     P   Q的逆時針方向    

    >      P   ×   Q     =   0   ,     P   Q共線,但可能同向也可能反向         

  3.判斷點在線段上         

  設點爲Q,線段爲P1P2   ,判斷點Q在該線段上的依據是:      (   Q   -   P1   )   ×   (   P2   -   P1   )   =   0  =>   共線       Q   在以   P1P2爲對角頂點的矩形內         

  4.判斷兩線段是否相交         

  咱們分兩步肯定兩條線段是否相交:      (1) 快速排斥試驗    

  設以線段   P1P2   爲對角線的矩形爲R   設以線段   Q1Q2   爲對角線的矩形爲T,若是RT不相交,顯然兩線段不會相交;      (2) 跨立試驗    

  若是兩線段相交,則兩線段必然相互跨立對方,如圖1所示。在圖1中,P1P2跨立Q1Q2   ,則矢量   (   P1   -   Q1   )   

 

(   P2   -   Q1   )位於矢量(   Q2   -   Q1   )   的兩側,即    

  (   P1   -   Q1   )   ×   (   Q2   -   Q1   )     *     (   P2   -   Q1   )   ×   (   Q2   -   Q1   )     <     0      上式可改寫成    

  (   P1   -   Q1   )   ×   (   Q2   -   Q1   )     *     (   Q2   -   Q1   )   ×   (   P2   -   Q1   )     >     0         (   P1   -   Q1   )   ×   (   Q2   -   Q1   )   =   0   時,說明   (   P1   -   Q1   )      (   Q2   -   Q1   )共線,可是由於已經經過快速排斥試驗,因此   P1   必定在線段   Q1Q2上;同理,(   Q2   -   Q1   )   ×(   P2   -   Q1   )     =   0   說明   P2   必定在線段   Q1Q2上。      因此判斷P1P2跨立Q1Q2的依據是:    

  (   P1   -   Q1   )   ×   (   Q2   -   Q1   )     *     (   Q2   -   Q1   )   ×   (   P2   -   Q1   )          0      同理判斷Q1Q2跨立P1P2的依據是:    

  (   Q1   -   P1   )   ×   (   P2   -   P1   )     *     (   P2   -   P1   )   ×   (   Q2   -   P1   )          0      至此已經徹底解決判斷線段是否相交的問題。         

  5.判斷線段和直線是否相交         

  若是線段   P1P2和直線Q1Q2相交,則P1P2跨立Q1Q2,即:    

  (   P1   -   Q1   )   ×   (   Q2   -   Q1   )     *     (   Q2   -   Q1   )   ×   (   P2   -   Q1   )          0         

  6.判斷矩形是否包含點         

  只要判斷該點的橫座標和縱座標是否夾在矩形的左右邊和上下邊之間。      判斷線段、折線、多邊形是否在矩形中    

  由於矩形是個凸集,因此只要判斷全部端點是否都在矩形中就能夠了。         

  7.判斷矩形是否在矩形中         

  只要比較左右邊界和上下邊界就能夠了。(左右邊界相互包含且上下邊界相互包含)         

  8.判斷圓是否在矩形中         

  圓在矩形中的充要條件是:圓心在矩形中且圓的半徑小於等於圓心到矩形四邊的距離的最小值。         

 

  9.判斷點是否在多邊形中         

  以點P爲端點,向左方做射線L,因爲多邊形是有界的,因此射線L的左端必定在多邊形外,考慮沿着L從無窮遠處開始自左向右移動,遇到和多邊形的第一個交點的時候,進入到了多邊形的內部,遇到第二個交點的時候,離開了多邊形,……因此很容易看出當L和多邊形的交點數目C是奇數的時候,P在多邊形內,是偶數的話P在多邊形外。    

  可是有些特殊狀況要加以考慮。若是L和多邊形的頂點相交,有些狀況下交點只能計算一個,有些狀況下交點不該被計算(你本身畫個圖就明白了);若是L和多邊形的一條邊重合,這條邊應該被忽略不計。爲了統一塊兒見,咱們在計算射線L和多邊形的交點的時候,1。對於多邊形的水平邊不做考慮;2。對於多邊形的頂點和L相交的狀況,若是該頂點是其所屬的邊上縱座標較大的頂點,則計數,不然忽略;3。對於P在多邊形邊上的情形,直接可判斷P屬於多邊行。由此得出算法的僞代碼以下:         

  1.   count      0;    

  2.   P爲端點,做從右向左的射線L;        

  3,   for   多邊形的每條邊s      

 4.       do   if   P在邊s        

  5.                     then   return   true;     

  6.             if   s不是水平的    

  7.                     then   if   s的一個端點在L上且該端點是s兩端點中縱座標較大的端點      

 9.                                     then   count      count+1     

 10.                             else   if   sL相交    

  11.                                   then   count      count+1;      

 12.   if   count   mod   2   =   1       

 13.       then   return   true      

 14.       else   return   false;              

  其中作射線L的方法是:設P'的縱座標和P相同,橫座標爲正無窮大(很大的一個正數),則PP'就肯定了射線L。這個算法的複雜度爲O(n)         

 

  10.判斷線段是否在多邊形內         

  線段在多邊形內的一個必要條件是線段的兩個端點都在多邊形內;    

  若是線段和多邊形的某條邊內交(兩線段內交是指兩線段相交且交點不在兩線段的端點),由於多邊形的邊的左右兩側分屬多邊形內外不一樣部分,因此線段必定會有一部分在多邊形外。因而咱們獲得線段在多邊形內的第二個必要條件:線段和多邊形的全部邊都不內交;    

  線段和多邊形交於線段的兩端點並不會影響線段是否在多邊形內;可是若是多邊形的某個頂點和線段相交,還必須判斷兩相鄰交點之間的線段是否包含與多邊形內部。所以咱們能夠先求出全部和線段相交的多邊形的頂點,而後按照X-Y座標排序,這樣相鄰的兩個點就是在線段上相鄰的兩交點,若是任意相鄰兩點的中點也在多邊形內,則該線段必定在多邊形內。證實以下:      命題1    

  若是線段和多邊形的兩相鄰交點P1   P2的中點P'   也在多邊形內,則P1,   P2之間的全部點都在多邊形內。      證實:    

  假設P1,P2之間含有不在多邊形內的點,不妨設該點爲Q,在P1,   P'之間,由於多邊形是閉合曲線,因此其內外部之間有界,而P1屬於多邊行內部,Q屬於多邊性外部,P'屬於多邊性內部,P1-Q-P'徹底連續,因此P1QQP'必定跨越多邊形的邊界,所以在P1,P'之間至少還有兩個該線段和多邊形的交點,這和P1P2是相鄰兩交點矛盾,故命題成立。證畢      由命題1直接可得出推論:      推論2    

  設多邊形和線段PQ的交點依次爲P1,P2,……Pn,其中PiPi+1是相鄰兩交點,線段PQ在多邊形內的充要條件是:PQ在多邊形內且對於i   =1,   2,……,   n-1Pi   ,Pi+1的中點也在多邊形內。         

  在實際編程中,沒有必要計算全部的交點,首先應判斷線段和多邊形的邊是否內交,假若線段和多邊形的某條邊內交則線段必定在多邊形外;若是線段和多邊形的每一條邊都不內交,則線段和多邊形的交點必定是線段的端點或者多邊形的頂點,只要判斷點是否在線段上就能夠了。      至此咱們得出算法以下:         

  1.   if   線端PQ的端點不都在多邊形內   

  2.       then   return   false;     

 3.   點集pointSet初始化爲空;    

  4.   for   多邊形的每條邊s    

  5.       do   if   線段的某個端點在s    

  6.                   then   將該端點加入pointSet;     

 7. else   if   s的某個端點在線段PQ     

 8.       then 將該端點加入pointSet;    

  9. else   if   s和線段PQ相交 //   這時候能夠確定是內交      

 10.       then return   false;    

  11.   pointSet中的點按照X-Y座標排序,X座標小的排在前面,對於X座標相同的點,Y座標小的排在前面;      

 12.   for   pointSet中每兩個相鄰點   pointSet[i]   ,   pointSet[   i+1]     

 13.         do   if   pointSet[i]   ,   pointSet[   i+1]   的中點不在多邊形中     

 14.                   then   return   false;      

 15.   return   true;         

  這個算法的複雜度也是O(n)。其中的排序由於交點數目確定遠小於多邊形的頂點數目n,因此最可能是常數級的複雜度,幾乎能夠忽略不計。   

      

  11.判斷折線在多邊形內         

  只要判斷折線的每條線段是否都在多邊形內便可。設折線有m條線段,多邊形有n個頂點,則複雜度爲O(m*n)         

  12.判斷多邊形是否在多邊形內    

  只要判斷多邊形的每條邊是否都在多邊形內便可。判斷一個有m個頂點的多邊形是否在一個有n個頂點的多邊形內複雜度爲O(m*n)         

  13.判斷矩形是否在多邊形內         

  將矩形轉化爲多邊形,而後再判斷是否在多邊形內。         

  14.判斷圓是否在多邊形內         

  只要計算圓心到多邊形的每條邊的最短距離,若是該距離大於等於圓半徑則該圓在多邊形內。計算圓心到多邊形每條邊最短距離的算法在後文闡述。         

  15.判斷點是否在圓內         

  計算圓心到該點的距離,若是小於等於半徑則該點在圓內。         

  16.判斷線段、折線、矩形、多邊形是否在圓內         

  由於圓是凸集,因此只要判斷是否每一個頂點都在圓內便可。         

  17.判斷圓是否在圓內         

  設兩圓爲O1,O2,半徑分別爲r1,   r2,要判斷O2是否在O1內。先比較r1r2的大小,若是r1<r2O2不可能在O1內;

相關文章
相關標籤/搜索