在一个二维坐标系中判断一个点是否在多边形内,多边形任意
设点为P,多边形为P0 P1 P2 …… Pn 用传统的做射线方法求解,做平行于Y轴的射线L,求多边形穿过射线L的次数count.
1.取多边形中第一个不在射线L上的点Pm,以Pm作为测试边的开始点Pstart
2.取Pstart的下一个点为测试边为结束点Pend,count赋值0
3.判断结束点Pend是否与p点重合,重合执行8
4.判断结束点Pend是否在射线上,在则从Pend开始顺序查找第一个不再射线上的点Ptemp,验证Pstart,Ptemp是否在同侧,是则count+1
5.判断结束点与开始点是否在同侧,不在则求测试边与射线的交点,如果交点与p点重合则执行8,否则如果交点在射线上则count+1
6.判断Pend是否被测试过,未被测试过则Pstart= Pend,执行2否则执行7
7.判断count奇偶,奇数执行8,偶数执行9
8.点在多边形内,判断结束
9.点不在多边形内,判断结束
C#代码:
using System;
using System.Collections.Generic;
using System.Text;
namespace Rign
{
///
/// 点集
///
public class CPoint
{
// x坐标
public double x;
// y坐标
public double y;
public CPoint()
{
this.x = 0;
this.y = 0;
}
public CPoint(double x, double y)
{
this.x = x;
this.y = y;
}
static public bool operator ==(CPoint pt0, CPoint pt1)
{
if (Math.Abs(pt0.x - pt1.x) < 0.0001 && Math.Abs(pt0.y - pt1.y) < 0.0001)
{
return true;
}
else
{
return false;
}
}
static public bool operator !=(CPoint pt0, CPoint pt1)
{
if (Math.Abs(pt0.x - pt1.x) > 0.0001 && Math.Abs(pt0.y - pt1.y) > 0.0001)
{
return true;
}
else
{
return false;
}
}
}
///
/// 区域测试类
///
public class CRign
{
///
/// 以测试点为坐标原点对多边形的点转换坐标
///
/// 多边形的点集
/// 测试点
/// 转换后的点集
private List CountXY(List list, CPoint ptCheck)
{
List retList = new List();
for (int i = 0; i < list.Count; i++)
{
CPoint ptTemp = new CPoint();
ptTemp.x = list[i].x - ptCheck.x;
ptTemp.y = list[i].y - ptCheck.y;
retList.Add(ptTemp);
}
return retList;
}
///
///
///
///
///
private int getFirstIndex(List list)
{
for (int iCount = 0; iCount < list.Count; iCount++)
{
if (Math.Abs(list[iCount].x) > 0.0001)
{
return iCount;
}
}
return -1;
}
///
/// 测试一个点是否在多边形内
///
/// 多边形的点集
/// 测试点
/// true or false
public bool PtInRegion(List list, CPoint ptCheck)
{
// 以测试点为坐标原点对多边形的点转换坐标
List listTemp = CountXY(list,ptCheck);
// 验证多边形的正确性
if (listTemp.Count < 3)
{
return false;
}
// 取得第一个不在射线上的点
int iStart = getFirstIndex(list);
if (iStart == -1)
{
return false;
}
int iCrossCount = 0;
double fCrossY = 0;
int iEnd = listTemp.Count + iStart;
CPoint ptFirst, ptEnd;
for (int i = iStart; i < iEnd; i++)
{
ptFirst = listTemp[i % listTemp.Count];
ptEnd = listTemp[(i + 1) % listTemp.Count];
if ((ptFirst.x > 0 && ptEnd.x < 0) || (ptFirst.x < 0 && ptEnd.x>0))
{// 端点在射线两边
fCrossY = (ptFirst.x * ptEnd.y - ptFirst.y * ptEnd.x) * (ptFirst.x - ptEnd.x);
if (Math.Abs(fCrossY) < 0.0001)
{// 射线与测试边在一条直线上
return true;
}
if (fCrossY > 0)
{
iCrossCount++;
}
}
else if (Math.Abs(ptEnd.x) < 0.0001 && ptEnd.y > 0)
{// 结束点在射线上,取得下一个有效测试点
int j;
int index = (i+2) % listTemp.Count;
for (j = i+2; j < iEnd+1; j++)
{
index = j%listTemp.Count;
if(Math.Abs(listTemp[index].x) > 0.0001)
{
break;
}
}
if ((ptFirst.x > 0 && listTemp[index].x < 0) || (ptFirst.x < 0 && listTemp[index].x > 0))
{
iCrossCount++;
}
i = j-1;
}
else if(Math.Abs(ptEnd.x) < 0.0001 && Math.Abs(ptEnd.x) < 0.0001)
{// 结束点与原点重合
return true;
}
}
if (iCrossCount % 2 == 1)
{
return true;
}
else
{
return false;
}
}
}