判斷點在多邊形內算法


點和多邊形關系的算法實現

        好了,現在我們已經了解了矢量叉積的意義,以及判斷直線段是否有交點的算法,現在回過頭看看文章開始部分的討論的問題:如何判斷一個點是否在多邊形內部? 根據射線法的描述,其核心是求解從P點發出的射線與多邊形的邊是否有交點。注意,這里說的是射線,而我們前面討論的都是線段,好像不適用吧?沒錯,確實是 不適用,但是我要介紹一種用計算機解決問題時常用的建模思想,應用了這種思想之后,我們前面討論的方法就適用了。什么思想呢?就是根據問題域的規模和性質 抽象和簡化模型的思想,這可不是故弄玄虛,說說具體的思路吧。

       計算機是不能表示無窮大和無窮小,計算機處理的每一個數都有確定的值,而且必須有確定的值。我們面臨的問題域是整個實數空間的坐標系,在每個維度上都是 從負無窮到正無窮,比如射線,就是從坐標系中一個明確的點到無窮遠處的連線。這就有點為難計算機了,為此我們需要簡化問題的規模。假設問題中多邊形的每個 點的坐標都不會超過(-10000.0, +10000.0)區間(比如我們常見的圖形輸出設備都有大小的限制),我們就可以將問題域簡化為(-10000.0, +10000.0)區間內的一小塊區域,對於這塊區域來說,>= 10000.0就意味着無窮遠。你肯定已經明白了,數學模型經過簡化后,算法中提到的射線就可以理解為從模型邊界到內部點P之間的線段,前面討論的關於線 段的算法就可以使用了。

 射線法的基本原理是判斷由P點發出的射線與多邊形的交點個數,交點個數是奇數表示P點在多邊形內(在多邊形的邊上也視為在多邊形內部的特殊情 況),正常情況下經過點P的射線應該如圖6(a)所示那樣,但是也可能碰到多種非正常情況,比如剛好經過多邊形一個定點的情況,如圖6 (b),這會被誤認為和兩條邊都有交點,還可能與某一條邊共線如圖6 (c)和(d),共線就有無窮多的交點,導致判斷規則失效。還要考慮凹多邊形的情況,如圖6(e)。

圖6 射線法可能遇到的各種交點情況

       針對這些特殊情況,在對多邊形的每條邊進行判斷時,要考慮以下這些特殊情況,假設當前處理的邊是P1P2,則有以下原則:

1)如果點P在邊P1P2上,則直接判定點P在多邊形內;

2)如果從P發出的射線正好穿過P1或者P2,那么這個交點會被算作2次(因為在處理以P1或P2為端點的其它邊時可能已經計算過這個點了),對這種情況的處理原則是:如果P的y坐標與P1、P2中較小的y坐標相同,則忽略這個交點;

3)如果從P發出的射線與P1P2平行,則忽略這條邊;

       對於第三個原則,需要判斷兩條直線是否平行,通常的方法是計算兩條直線的斜率,但是本算法因為只涉及到直線段(射線也被模型簡化為長線段了),就簡化了 很多,判斷直線是否水平,只要比較一下線段起始點的y坐標是否相等就行了,而判斷直線是否垂直,也只要比較一下線段起始點的x坐標是否相等就行了。

       應用以上原則后,掃描線法判斷點是否在多邊形內的算法流程就完整了,圖7就是算法的流程圖:

       最終掃描線法判斷點是否在多邊形內的算法實現如下:

228 bool IsPointInPolygon(const Polygon& py, const Point& pt)   
       
229 {   
       
230     assert(py.IsValid()); /*只考慮正常的多邊形,邊數>=3*/
       
231    
       
232     int count = 0;   
       
233     LineSeg ll = LineSeg(pt, Point(-INFINITE, pt.y)); /*射線L*/
       
234     for(int i = 0; i < py.GetPolyCount(); i++)   
       
235     {   
       
236         /*當前點和下一個點組成線段P1P2*/
       
237         LineSeg pp = LineSeg(py.pts[i], py.pts[(i + 1) % py.GetPolyCount()]);   
       
238         if(IsPointOnLineSegment(pp, pt))   
       
239         {   
       
240             return true;   
       
241         }   
       
242    
       
243         if(!pp.IsHorizontal())   
       
244         {   
       
245             if((IsSameFloatValue(pp.ps.y, pt.y)) && (pp.ps.y > pp.pe.y))   
       
246             {   
       
247                 count++;   
       
248             }   
       
249             else if((IsSameFloatValue(pp.pe.y, pt.y)) && (pp.pe.y > pp.ps.y))   
       
250             {   
       
251                 count++;   
       
252             }   
       
253             else
       
254             {   
       
255                 if(IsLineSegmentIntersect(pp, ll))   
       
256                 {   
       
257                     count++;   
       
258                 }   
       
259             }   
       
260         }   
       
261     }   
       
262    
       
263     return ((count % 2) == 1);   
       
264 }

在圖形學領域實施的真正工程代碼,通常還會增加一個多邊形的外包矩形快速判斷,對點根本就不在多邊形周圍的情況做快速排除, 提高算法效率。這又涉及到求多邊形外包矩形的算法,這個算法也很簡單,就是遍歷多邊形的所有節點,找出各個坐標方向上的最大最小值。以下就是求多邊形外包 矩形的算法:

266 void GetPolygonEnvelopRect(const Polygon& py, Rect& rc)   
       
267 {   
       
268     assert(py.IsValid()); /*只考慮正常的多邊形,邊數>=3*/
       
269    
       
270     double minx = py.pts[0].x;   
       
271     double maxx = py.pts[0].x;   
       
272     double miny = py.pts[0].y;   
       
273     double maxy = py.pts[0].y;   
       
274     for(int i = 1; i < py.GetPolyCount(); i++)   
       
275     {   
       
276         if(py.pts[i].x < minx)   
       
277             minx = py.pts[i].x;   
       
278         if(py.pts[i].x > maxx)   
       
279             maxx = py.pts[i].x;   
       
280         if(py.pts[i].y < miny)   
       
281             miny = py.pts[i].y;   
       
282         if(py.pts[i].y > maxy)   
       
283             maxy = py.pts[i].y;   
       
284     }   
       
285    
       
286     rc = Rect(minx, miny, maxx, maxy);   
       
287 }

除了掃描線法,還可以通過多邊形邊的法矢量方向、多邊形面積以及角度和等方法判斷點與多邊形的關系。但是這些算法要么只支持 凸多邊形,要么需要復雜的三角函數運算(多邊形邊數小於44時,可采用近似公式計算夾角和,避免三角函數運算),使用的范圍有限,只有掃描線法被廣泛應 用。

       至此,本文的內容已經完結,以上通過對點與矩形、點與圓、點與直線以及點與多邊形位置關系判斷算法的講解,向大家展示了幾種常見的計算幾何算法實現,用簡短而易懂的代碼剖析了這些算法的實質。下一篇將介紹計算機圖形學中最基本的直線生成算法。

參考資料:

【1】計算幾何:算法設計與分析 周培德  清華大學出版社 2005年

【2】計算幾何:算法與應用 德貝爾赫(鄧俊輝譯)  清華大學出版社 2005年

【3】算法導論 Thomas H.Cormen等(潘金貴等譯) 機械工業出版社 2006年

【4】計算機圖形學 孫家廣、楊常貴 清華大學出版社 1995年


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM