如何判斷一個點是否在多邊形內部?
(1)面積和判別法:判斷目標點與多邊形的每條邊組成的三角形面積和是否等於該多邊形,相等則在多邊形內部。
(2)夾角和判別法:判斷目標點與所有邊的夾角和是否為360度,為360度則在多邊形內部。
(3)引射線法:從目標點出發引一條射線,看這條射線和多邊形所有邊的交點數目。如果有奇數個交點,則說明在內部,如果有偶數個交點,則說明在外部。
本文采用引射線法。
原理:從測試點引一條垂直於y軸的射線,若射線與多邊形交點為奇數時,則測試點在多邊形內,若為偶數點,則在多邊形外。測試點在多邊形的邊上時則需特殊考慮。
如圖所示 交點為2點,點在圖形外。
算法圖解:
參考代碼:
1 /************************************************************ 2 ** 函數名稱: InOrOutPolygon 3 ** 功能描述: 判斷點在多邊形內外 4 ** 輸入參數: nvert 頂點個數 vertx 多邊形頂點x坐標數組 verty 多邊形頂點y坐標數組 5 testx 被判斷點位置x坐標 testy 被判斷點位置y坐標 6 ** 輸出參數: NULL 7 ** 返 回 值: 0:外 1:內 8 ** 作 者: 9 ** 日 期: 2018年3月21日 10 **************************************************************/ 11 int InOrOutPolygon(int nvert, float *vertx, float *verty, float testx, float testy) 12 { 13 int i, j, crossings = 0; 14 for (i = 0, j = nvert-1; i < nvert; j = i++) 15 { 16 // 點在兩個x之間 且以點垂直y軸向上做射線 17 if(((vertx[i]>testx) != (vertx[j]>testx)) 18 && (testx > (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i])) 19 crossings++; 20 } 21 return (crossings % 2 != 0); 22 }
代碼原理:
引射線法是做一條射線,判斷與多邊形每條邊的交點。
那么我們即可認為做一條方向向上且與y軸垂直的射線。
所以只要測試點在邊的下方且橫坐標在邊兩頂點之間,即有一個交點,否則無交點。
那么我們只需要通過2點((x1,y1)(x2,y2))求出邊的直線方程,將測試點(x,y)帶入邊的直線方程,測試點滿足 x1<x<x2 或 x2<x<x1 且y<k(x-x1)+y1
若點在邊界則測試點滿足 x1<x<x2 或 x2<x<x1 且y=k(x-x1)+y1
參考資料:http://www.cnblogs.com/luxiaoxun/p/3722358.html
2018.3.30 更新
以上方法針對普通情況可適用,但是針對某些特殊情況,可能出現判斷錯誤的情況
1.若射線引出的點直接穿過頂點。若按上述算法進行計算,則穿過點的數量為0 得出點在多邊形外。
所以我們計算時,應當取半開半閉區間,即將頂點算入線中,但是每條線只計算其一邊的頂點(若為閉區間,則會重復計算頂點)
2.若射線穿過定點
使用1所述方法可以解決。
參考代碼:
int InOrOutPolygon(int nvert, float *vertx, float *verty, float testx, float testy) { // int i, j, crossings = 0; crossings = 0; for (i = 0, j = nvert-1; i < nvert; j = i++) { // 點在兩個x之間 且以點垂直y軸向上做射線 x1[i] = vertx[i]; x1[j] = vertx[j]; y1[i] = verty[i]; y1[j] = verty[j]; if((((vertx[i] < testx) && (vertx[j] >= testx))||((vertx[i] >= testx) && (vertx[j] < testx))) && (testx > (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i])) crossings++; } return (crossings % 2 != 0); }
關於這個算法的具體的更多圖形例子:http://alienryderflex.com/polygon/