點在多邊形內算法——判斷一個點是否在一個複雜多邊形的內部

新頁面(new page)介紹了將樣條曲線添加到此技術的內容。也可以訪問多邊形內最短路徑頁(shortest-path-through-polygonpage)!


圖 1

圖1顯示了一個具有14條邊的凹多邊形。我們要判斷紅色點是否在多邊形內。

解決方案是將測試點的Y座標與多邊形的每一個點進行比較,我們會得到一個測試點所在的行與多邊形邊的交點的列表。在這個例子中有8條邊與測試點所在的行相交,而有6條邊沒有相交。如果測試點的兩邊點的個數都是奇數個則該測試點在多邊形內,否則在多邊形外。在這個例子中測試點的左邊有5個交點,右邊有三個交點,它們都是奇數,所以點在多邊形內。

(注意:這個算法適用於順時針和逆時針繪製的多邊形。)


圖 2

圖2顯示了多邊形自交的情況。在這個例子中多邊形的10條邊有些互相交叉。這種情況很像彙編語言中的“異或”(XOR)。多邊形中重疊的部分剔除。因此測試點在多邊形的外面,我們從它的左右兩邊各有兩個交點也可以看出來。


圖 3

圖3中多邊形沒有重疊,但是有兩條邊相交。這種情況下算法也沒有問題,任然可以正常工作。


圖 4

圖4顯示了當我們要測試的點所在的掃描行正好穿過多邊形的一個頂點,因此掃描行與邊a有一個交點,與邊b也有一個交點,一共有兩個角點,測試點的右邊也是同樣的情況,那按照我們的算法得到:測試點在多邊形外的結論,但這顯然是錯誤的!

要解決這種情況遇到的問題非常簡單。邊上的點是否與掃描行相交,我們要看邊的兩個端點是否是在掃描行的兩側,在掃描行上或上方的端點我們把它認爲是同一種情況,那圖4中邊a的一個端點在掃描線的下方,另一個點在掃描線上或上方,所以邊a與掃描線相交,而邊b的兩個端點都在掃描線上或上方,所以邊b與掃描線不相交。


圖 5

圖5顯示的多邊形上一條邊完全與掃描行重合的情況。根據圖4中具體描述的邊c的一個端點在掃描線的下方另一個端點在掃描線上或上方,所以邊c與掃描線有一個交點,而邊d的兩個端點都在掃描線上或以上,所以無交點,邊e也是兩個端點都在掃描線上或以上,所以也沒交點。


圖 6

圖6說明了一種特殊的情況(由加州州立理工大學的John David Munch指出)。多邊形的一個角剛好落在掃描線上。其實這也沒問題,上面的圖中只有紅色的邊與掃描線相交產生交點,所以第一張圖有1個交點第二張圖有3個交點,交點的個數任然是奇數個,所以測試點在多邊形內部。

多邊形的邊

如果測試點剛好在多邊形的邊上,則這種算法得到的結果是不確定的;例如結果可能是“在裏面”或“不在裏面”,這取決於很多不定的因素例如多邊形在座標系統中的方向。(不過這也沒啥影響,因爲多邊形的邊是無限小的,and points that fall right on the edge can Go either way withouthurting the look of the polygon)

 

C代碼例子

// Globals which should be set before calling this function:
//
// int    polySides  =  how many cornersthe polygon has
// float  polyX[]    =  horizontalcoordinates of corners
// float  polyY[]    =  verticalcoordinates of corners
// float  x,y       =  point to be tested
//
// (Globals are used in this example for purposes of speed.  Change as
// desired.)
//
//  Thefunction will return YES if the point x,y is inside the polygon, or
//  NOif it is not.  If the point is exactly on the edge of the polygon,
// then the function may return YES or NO.
//
// Note that division by zero is avoided because the division is protected
//  bythe "if" clause which surrounds it.

bool pointInPolygon() {

  int   i,j=polySides-1 ;
  bool  oddNodes=NO     ;

  for (i=0;i<polySides; i++) {
    if(polyY[i]<y && polyY[j]>=y
    ||  polyY[j]<y&& polyY[i]>=y) {
      if(polyX[i]+(y-polyY[i])/(polyY[j]-polyY[i])*(polyX[j]-polyX[i])<x) {
        oddNodes=!oddNodes;}}
    j=i;}

  returnoddNodes; }

 

下面是由Nathan Mercer提供的效力比較的的版本,藍色的代碼eliminatescalculations on sides that are entirely to the right of the test point.。雖然對某些多邊形來說可能不是最快的方法,但是大多數情況下是最快的。

// Globals which should be set before calling this function:
//
// int    polySides  =  how many cornersthe polygon has
// float  polyX[]    =  horizontalcoordinates of corners
// float  polyY[]    =  verticalcoordinates of corners
// float  x,y       =  point to be tested
//
// (Globals are used in this example for purposes of speed.  Change as
// desired.)
//
//  Thefunction will return YES if the point x,y is inside the polygon, or
//  NOif it is not.  If the point is exactly on the edge of the polygon,
// then the function may return YES or NO.
//
// Note that division by zero is avoided because the division is protected
//  bythe "if" clause which surrounds it.

bool pointInPolygon() {

  int   i,j=polySides-1 ;
  bool  oddNodes=NO     ;

  for (i=0;i<polySides; i++) {
    if((polyY[i]< y && polyY[j]>=y
    ||   polyY[j]<y && polyY[i]>=y)
    &&  (polyX[i]<=x || polyX[j]<=x)){
      if(polyX[i]+(y-polyY[i])/(polyY[j]-polyY[i])*(polyX[j]-polyX[i])<x) {
        oddNodes=!oddNodes;}}
    j=i;}

  returnoddNodes; }

這裏是由提供了另一個高效的版本。內部的if語句消除或替代了異或操作。

// Globals which should be set before calling this function:
//
// int    polySides  =  how many cornersthe polygon has
// float  polyX[]    =  horizontalcoordinates of corners
// float  polyY[]    =  verticalcoordinates of corners
// float  x,y       =  point to be tested
//
// (Globals are used in this example for purposes of speed.  Change as
// desired.)
//
//  Thefunction will return YES if the point x,y is inside the polygon, or
//  NOif it is not.  If the point is exactly on the edge of the polygon,
// then the function may return YES or NO.
//
// Note that division by zero is avoided because the division is protected
//  bythe "if" clause which surrounds it.

bool pointInPolygon() {

  int   i,j=polySides-1 ;
  bool  oddNodes=NO     ;

  for (i=0;i<polySides; i++) {
    if((polyY[i]< y && polyY[j]>=y
    ||   polyY[j]<y && polyY[i]>=y)
    && (polyX[i]<=x || polyX[j]<=x)) {
      oddNodes^=(polyX[i]+(y-polyY[i])/(polyY[j]-polyY[i])*(polyX[j]-polyX[i])<x);}
    j=i;}

  returnoddNodes; }

 

整型問題

假如你試圖畫一個像下圖中藍色的多邊形,但是出來的卻是有橫線和豎線組成的邊的多邊形,如下圖中的紅色的多邊形。出現這種情況可能是將多邊形的頂點座標變量定義成了整型而非浮點型,仔細地檢查你的代碼確保多邊形的頂點是以浮點型定義且傳遞的。


圖 7

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章