[轉載]計算機幾何算法

[轉載]計算機幾何算法

原文地址:計算機幾何算法作者:yuanmercu_oxxdl
這裏討論計算機2D空間內的一些幾何算法。討論的主題包括:兩條線段之間的方向、折線在某個頂點上的轉向、點是否在線段上的判定、線段是否相交的判定、凸包、給定點組成的多邊形是否構成凸多邊形的判定、凸多邊形面積的計算以及判斷點是否在一個多邊形(包括凹凸兩種多邊形) 當中的算法。
        在討論這些主題之前,首先確定點在計算機內的表示使用一個數對(整數或者浮點數,看具體需求)<X,Y>來表示,而線段使用線段的兩個端點來表示。在後面的所有主題討論之中我們經常會用到一個幾何術語:叉積,具體的幾何意義表示由兩個向量及其平移得到的向量圍城的平行四邊形的面積,而其算法是這樣表示的:向量a=(x1,y1)向量b=(x2,y2),叉積a×b=x1*y2 + x2*y1;爲了這些主題討論的方便線段l我們有時也會表示成向量,表示的方法是:假設線段l上的兩個端點p1(x1,y1),p2(x2,y2)那麼表示出來的向量p1p2=(x2-x1,y2-y1)。
一、兩條線段的方向
        這個主題要求的就是線段l2在給定線段l1的順時針或者逆時針方向上。正如上面所說的,我們把兩條線段表示成向量的方式,這樣轉化而來的向量都以共同頂點原點爲出發點。這樣我們求得兩個向量的叉積m,如果m<0說明線段l2在線段l1的逆時針方向上;m>0說明在順時針方向上;m==0表示兩條線段共線。
二、折線在某個頂點上的轉向
        假設折線上依次有點p1、p2、p3,求在頂點p2上折線是向左轉或者向右轉。如圖:
        顯然如果我們要求在P2點上的折向我們只需要把P1、P2、P3表示成第二張圖上紅色部分表示的兩個向量P1P2和P1P3,如果向量P1P3在向量P1P2的順時針方向那麼在P2點上就是向右轉,否則就是左轉,而判斷兩個向量(或者線段)方向的算法就是第一個主題當中提到的內容。
三、點在線段上的判斷
        點是否在線段上的判斷有兩種方式,第一種計算出線段的方程然後判斷,但是這種方式當中由於存在大量除法運算,因此不但效率較差而且存在着精度的問題。而另一種判斷方式則是,設線段l上兩個頂點p1p2,要求判斷的點爲p3,我們前面講到過如果兩個向量的叉積爲0那麼兩個向量共線,因此我們首先判斷向量p1p3和向量p1p2是否共線,這樣我們可以確定點p3是否在線段l確定的直線上,接着我們判斷點p3是否在點p1p2確定的矩形當中(判斷方式很簡單,點p3滿足x1<=x3<=x2 && y1<=y3<=y2,切記不可省略兩個條件中的任意一個)。
四、判斷兩條線段是否相交
       這個判斷我們當然也可以使用兩個線段方程聯立求解的方式來判斷,但是同樣存在性能和精度上的問題。而改進的算法是這樣的思想,如果線段l1和l2相交那麼線段l1的兩個頂點必然在線段l2的兩端,同理l2的兩個端點也必然在l1的兩端,如圖:
這張圖中包括了兩種情況相交與不相交,可以看到我們每次都是判斷一條線段上的兩個點(藍色表示)是否在另一條線段(用紫色加粗)的兩側,而判斷方法很簡單,我們使用了兩個向量(紅色表示)是否在那條線段(紫色加粗)的兩端,判斷很簡單,用叉積,如果兩個叉積的積爲負表示兩個叉積不同號,紅色向量在紫色向量的兩側,如果兩次判斷都滿足上面的條件那麼可以確定兩條線段相交。下面的部分表示了不相交的情況這時求出的兩個叉積的積必然爲正。當然我們還要考慮下面的邊界條件。還是用圖來表示
用同樣方法來計算叉積的時候在處理叉積m1 = P1P3 * P1P2 與m2 = P1P4 * P1P2時由於m1 == 0 所以 m1*m2 == 0這時說明有線段的端點在另一條線段上的情況出現,通過m1==0我們可以知道P3在線段P1P2上或者在其延長線上,這時需要判斷的是點P3是否在線段P1P2上,這時判斷回到了第三個主題的情況下。千萬要注意的是當叉積等於0的時候並不意味着兩條線段相交的情況出現,因爲所謂的交點可能出現在延長線上。
五、凸包問題
        所謂凸包就是這樣一個問題給定一組點,求用這組點集中的某些點組成的一個最小凸多邊形能夠包圍點集當中所有的點。算法如下:
1、取得一個角點,作爲開始點。開始點爲最左下點(即y座標最小,如果有多個相同點則再取x座標最小)。
2、對所有其他的點,按照傾斜角遞增的順序排序,形成一個閉合的多邊形
      這個部分需要注意的有兩點,第一、在給對象排序的過程中最重要的是比較兩個對象的大小問題,即compareLT(obj1,obj2)算法的實現,在這裏由於是按傾斜角來排序,我們可以採用這樣的一種比較方式,給定點p1、p2,從原點出發到這兩個點的向量也爲p1、p2,那麼求這兩個向量的叉積得到m,如果m<0那麼說明向量p2在向量p1的逆時針方向,p2的傾斜角較大,compareLT(p1,p2)應該返回true;第二、對於傾角相同點的處理方式,即m相同的點,對於一組傾角相同的點我們只取其中離第一步當中確定的點距離最遠的點,其他的點刪除掉。
3、根據第二部排序及預先處理好的一個點集我們以此對除第一步確定的點p0外其他的點做掃描,這裏要用到一個堆棧,首先初始化堆棧並將p0以及排序好的點集當中的前兩個點入棧,接着掃描剩餘的點,每次掃描到一個點則查看堆棧頂部的兩個點Pi和Pj,確定在Pi點的轉向,如果是向左轉則入棧當前點Pm,否則出棧Pi,然後繼續查看棧頂兩點,比較棧頂點到當前點的轉向,知道轉向爲左Pm點入棧爲止。
       這一步當中所說的確定轉向部分的算法又回到第二個主題所描述的算法當中。
六、判斷給定點所組成的多邊形是否爲凸多邊形
       首先假定給定的點按照某個順序排定(或者順時針或者逆時針),對於給定的點序列<P1,P2,P3,P4...Pm>當中的任意三個點Pi,Pj,Pk,如果滿足折線在Pj處的轉向都相同(或都爲左或都爲右),那麼即可判斷多邊形爲凸,判斷折線轉向的問題又回到主題2所討論的算法。而事實上如果給定的點位逆時針序,那麼轉向必然爲左;如果爲順時針序必然爲右。
七、凸多邊形面積的計算
        根據向量叉積的意義我們知道對於三角形P1P2P3,我們求得叉積m=P1P2 * P1P3的絕對值:abs(m)就等於三角形面積的兩倍,基於這樣的原因我們可以根據給定凸多邊形上按逆時針排列的頂點幾何選定一點,依次求該點到連續兩點組成三角形的面積並最終求和即得到凸多邊形的面積,如圖:
八、判斷點是否在多邊形內
        判斷點是否在多邊形內的方法爲將點像左做一條射線(實際只要做一條線段,線段的右端點的x值只要比所有可能點小即可y值與給定點相同),然後求這條所謂射線與多邊形各個邊交點的總和count,如果count爲偶數則在多邊形外否則在多邊形內
        但是這裏需要注意的是這麼幾種情況:1.如果多邊形的某條邊爲水平(與X軸平行)則這條邊不放入比較;2.如果邊與射線的交點在頂點,那麼分兩種情況,如果該頂點是這條邊兩個頂點當中y值較大者count++否則count不變;3.如果點P直接在多邊形某條邊上那麼直接判斷點在多邊形內(當然也要看程序要求)。
        因此該判斷的程序流程應該是:
0.count <- 0
1.從多邊形依次取出每條邊
    Y:有邊,轉2
    N:無邊,轉6
2.判斷點是否在邊上
   Y:返回true
   N:繼續
3.判斷邊是否水平(y1 == y2)
   Y:從1繼續執行
   N:繼續
4.邊的兩個頂點是否在射線上
   Y:  4-1-1 判斷該頂點是否是y值較大者
              Y:count++;
    N: 判斷射線與邊是否相交
         Y:count++
5.轉1執行
6.結束,輸出count
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章