判斷一點是否在不規則圖像的內部算法,如下圖是由一個個點組成的不規則圖像,判斷某一點是否在不規則矩形內部,先上效果圖
算法實現如下,算法簡單,親試有效
public class PositionAlgorithmHelper { /// <summary> /// 判斷當前位置是否在不規則形狀里面 /// </summary> /// <param name="nvert">不規則形狀的定點數</param> /// <param name="vertx">當前x坐標</param> /// <param name="verty">當前y坐標</param> /// <param name="testx">不規則形狀x坐標集合</param> /// <param name="testy">不規則形狀y坐標集合</param> /// <returns></returns> public static bool PositionPnpoly(int nvert, List<double> vertx, List<double> verty, double testx, double testy) { int i, j, c = 0; for (i = 0, j = nvert - 1; i < nvert; j = i++) { if (((verty[i] > testy) != (verty[j] > testy)) && (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i])) { c = 1 + c; ; } } if (c % 2 == 0) { return false; } else { return true; } } }
用上圖坐標進行測試:
class Program { static void Main(string[] args) { test1(); } /// <summary> /// test1 /// </summary> public static void test1() { //不規則圖像坐標 List<Position> position = new List<Position>(); position.Add(new Position() { x = 6, y = 0 }); position.Add(new Position() { x = 10, y = 2 }); position.Add(new Position() { x = 16, y = 2 }); position.Add(new Position() { x = 20, y = 6 }); position.Add(new Position() { x = 14, y = 10 }); position.Add(new Position() { x = 16, y = 6 }); position.Add(new Position() { x = 12, y = 6 }); position.Add(new Position() { x = 14, y = 8 }); position.Add(new Position() { x = 10, y = 8 }); position.Add(new Position() { x = 8, y = 6 }); position.Add(new Position() { x = 12, y = 4 }); position.Add(new Position() { x = 6, y = 4 }); position.Add(new Position() { x = 8, y = 2 }); //用戶當前位置坐標 List<Position> userPositions = new List<Position>(); userPositions.Add(new Position() { x = 14, y = 4 }); userPositions.Add(new Position() { x = 15, y = 4 }); userPositions.Add(new Position() { x = 10, y = 6 }); userPositions.Add(new Position() { x = 8, y = 5 }); //不規則圖像x坐標集合 List<double> xList = position.Select(x => x.x).ToList(); //不規則圖像y坐標集合 List<double> yList = position.Select(x => x.y).ToList(); foreach (var userPosition in userPositions) { bool result = PositionAlgorithmHelper.PositionPnpoly(position.Count, xList, yList, userPosition.x, userPosition.y); if (result) { Console.WriteLine(string.Format("{0},{1}【在】坐標內", userPosition.x, userPosition.y)); } else { Console.WriteLine(string.Format("{0},{1}【不在】坐標內", userPosition.x, userPosition.y)); } } } }
另外兩種方式:
/// <summary> /// 判斷點是否在多邊形內. /// ----------原理---------- /// 注意到如果從P作水平向左的射線的話,如果P在多邊形內部,那么這條射線與多邊形的交點必為奇數, /// 如果P在多邊形外部,則交點個數必為偶數(0也在內)。 /// 所以,我們可以順序考慮多邊形的每條邊,求出交點的總個數。還有一些特殊情況要考慮。假如考慮邊(P1,P2), /// 1)如果射線正好穿過P1或者P2,那么這個交點會被算作2次,處理辦法是如果P的從坐標與P1,P2中較小的縱坐標相同,則直接忽略這種情況 /// 2)如果射線水平,則射線要么與其無交點,要么有無數個,這種情況也直接忽略。 /// 3)如果射線豎直,而P0的橫坐標小於P1,P2的橫坐標,則必然相交。 /// 4)再判斷相交之前,先判斷P是否在邊(P1,P2)的上面,如果在,則直接得出結論:P再多邊形內部。 /// </summary> /// <param name="checkPoint">要判斷的點</param> /// <param name="polygonPoints">多邊形的頂點</param> /// <returns></returns> public static bool IsInPolygon2(Position checkPoint, List<Position> polygonPoints) { int counter = 0; int i; double xinters; Position p1, p2; int pointCount = polygonPoints.Count; p1 = polygonPoints[0]; for (i = 1; i <= pointCount; i++) { p2 = polygonPoints[i % pointCount]; if (checkPoint.y > Math.Min(p1.y, p2.y)//校驗點的Y大於線段端點的最小Y && checkPoint.y <= Math.Max(p1.y, p2.y))//校驗點的Y小於線段端點的最大Y { if (checkPoint.x <= Math.Max(p1.x, p2.x))//校驗點的X小於等線段端點的最大X(使用校驗點的左射線判斷). { if (p1.y != p2.y)//線段不平行於X軸 { xinters = (checkPoint.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x; if (p1.x == p2.x || checkPoint.x <= xinters) { counter++; } } } } p1 = p2; } if (counter % 2 == 0) { return false; } else { return true; } } /// <summary> /// 判斷點是否在多邊形內. /// ----------原理---------- /// 注意到如果從P作水平向左的射線的話,如果P在多邊形內部,那么這條射線與多邊形的交點必為奇數, /// 如果P在多邊形外部,則交點個數必為偶數(0也在內)。 /// </summary> /// <param name="checkPoint">要判斷的點</param> /// <param name="polygonPoints">多邊形的頂點</param> /// <returns></returns> public static bool IsInPolygon(Position checkPoint, List<Position> polygonPoints) { bool inside = false; int pointCount = polygonPoints.Count; Position p1, p2; for (int i = 0, j = pointCount - 1; i < pointCount; j = i, i++)//第一個點和最后一個點作為第一條線,之后是第一個點和第二個點作為第二條線,之后是第二個點與第三個點,第三個點與第四個點... { p1 = polygonPoints[i]; p2 = polygonPoints[j]; if (checkPoint.y < p2.y) {//p2在射線之上 if (p1.y <= checkPoint.y) {//p1正好在射線中或者射線下方 if ((checkPoint.y - p1.y) * (p2.x - p1.x) > (checkPoint.x - p1.x) * (p2.y - p1.y))//斜率判斷,在P1和P2之間且在P1P2右側 { //射線與多邊形交點為奇數時則在多邊形之內,若為偶數個交點時則在多邊形之外。 //由於inside初始值為false,即交點數為零。所以當有第一個交點時,則必為奇數,則在內部,此時為inside=(!inside) //所以當有第二個交點時,則必為偶數,則在外部,此時為inside=(!inside) inside = (!inside); } } } else if (checkPoint.y < p1.y) { //p2正好在射線中或者在射線下方,p1在射線上 if ((checkPoint.y - p1.y) * (p2.x - p1.x) < (checkPoint.x - p1.x) * (p2.y - p1.y))//斜率判斷,在P1和P2之間且在P1P2右側 { inside = (!inside); } } } return inside; }
/// <summary> /// 判斷點是否在多邊形內. /// ----------原理---------- /// 注意到如果從P作水平向左的射線的話,如果P在多邊形內部,那么這條射線與多邊形的交點必為奇數, /// 如果P在多邊形外部,則交點個數必為偶數(0也在內)。 /// 所以,我們可以順序考慮多邊形的每條邊,求出交點的總個數。還有一些特殊情況要考慮。假如考慮邊(P1,P2), /// 1)如果射線正好穿過P1或者P2,那么這個交點會被算作2次,處理辦法是如果P的從坐標與P1,P2中較小的縱坐標相同,則直接忽略這種情況 /// 2)如果射線水平,則射線要么與其無交點,要么有無數個,這種情況也直接忽略。 /// 3)如果射線豎直,而P0的橫坐標小於P1,P2的橫坐標,則必然相交。 /// 4)再判斷相交之前,先判斷P是否在邊(P1,P2)的上面,如果在,則直接得出結論:P再多邊形內部。 /// </summary> /// <param name="checkPoint">要判斷的點</param> /// <param name="polygonPoints">多邊形的頂點</param> /// <returns></returns> public static bool IsInPolygon2(Position checkPoint, List<Position> polygonPoints) { int counter = 0; int i; double xinters; Position p1, p2; int pointCount = polygonPoints.Count; p1 = polygonPoints[0]; for (i = 1; i <= pointCount; i++) { p2 = polygonPoints[i % pointCount]; if (checkPoint.y > Math.Min(p1.y, p2.y)//校驗點的Y大於線段端點的最小Y && checkPoint.y <= Math.Max(p1.y, p2.y))//校驗點的Y小於線段端點的最大Y { if (checkPoint.x <= Math.Max(p1.x, p2.x))//校驗點的X小於等線段端點的最大X(使用校驗點的左射線判斷). { if (p1.y != p2.y)//線段不平行於X軸 { xinters = (checkPoint.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x; if (p1.x == p2.x || checkPoint.x <= xinters) { counter++; } } }
} p1 = p2; }
if (counter % 2 == 0) { return false; } else { return true; } }
/// <summary> /// 判斷點是否在多邊形內. /// ----------原理---------- /// 注意到如果從P作水平向左的射線的話,如果P在多邊形內部,那么這條射線與多邊形的交點必為奇數, /// 如果P在多邊形外部,則交點個數必為偶數(0也在內)。 /// </summary> /// <param name="checkPoint">要判斷的點</param> /// <param name="polygonPoints">多邊形的頂點</param> /// <returns></returns> public static bool IsInPolygon(Position checkPoint, List<Position> polygonPoints) { bool inside = false; int pointCount = polygonPoints.Count; Position p1, p2; for (int i = 0, j = pointCount - 1; i < pointCount; j = i, i++)//第一個點和最后一個點作為第一條線,之后是第一個點和第二個點作為第二條線,之后是第二個點與第三個點,第三個點與第四個點... { p1 = polygonPoints[i]; p2 = polygonPoints[j]; if (checkPoint.y < p2.y) {//p2在射線之上 if (p1.y <= checkPoint.y) {//p1正好在射線中或者射線下方 if ((checkPoint.y - p1.y) * (p2.x - p1.x) > (checkPoint.x - p1.x) * (p2.y - p1.y))//斜率判斷,在P1和P2之間且在P1P2右側 { //射線與多邊形交點為奇數時則在多邊形之內,若為偶數個交點時則在多邊形之外。 //由於inside初始值為false,即交點數為零。所以當有第一個交點時,則必為奇數,則在內部,此時為inside=(!inside) //所以當有第二個交點時,則必為偶數,則在外部,此時為inside=(!inside) inside = (!inside); } } } else if (checkPoint.y < p1.y) { //p2正好在射線中或者在射線下方,p1在射線上 if ((checkPoint.y - p1.y) * (p2.x - p1.x) < (checkPoint.x - p1.x) * (p2.y - p1.y))//斜率判斷,在P1和P2之間且在P1P2右側 { inside = (!inside); } } } return inside; }