近期,工作中遇到一个需求:判断一个点是否在一个不规则封闭区域内,根据判断结果进行相应处理。
在网上搜索相关内容后,确定了以射线法来实现点与不规则封闭区域的位置关系,如下图:
图中:以某点为端点做射线(向任何方向做射线都可以,本例中为实现简单,则选择向右做射线),与不规则封闭区域的边存在若个交点。
A点:2个交点(外部);
B点:1个交点(内部);
C点:4个交点(外部);
D点:2个交点(外部);
E点:3个交点(内部);
通过以上数据,我们可以得出一个结论:凡交点是奇数的,则点在内部;否则,点在外部。
不过,本例存在以下两个特殊情况:
- 区域的边是水平方向,即两个节点的X坐标相同,则不计算交点;
- 每条边都有上下两个端点(上端点和下端点),为防止射线经过了两条边的交点而重复计算,我们规定射线只与边的上端点相交为有效交点。
如何判断与不规则区域的边有交点呐?如下图:

线段P1P2为不规则区域的一条边。如果点Pt.Y坐标大于P1P2两点的最大Y坐标或者小于P1P2两点最小Y坐标,那么P1P2与射线无交点;如果点Pt.X坐标大于P1P2两点最大X坐标,则P1P2与射线无交点。在有焦点的情况下,如果Pt.X坐标小于射线与P1P2交点的X坐标,则有交点且有效,否则是Pt点向右水平射线的反向延长线上有交点,则交点无效。(此处计算射线与P1P2交点的X坐标用的是相似三角形的性质即对应边长成正比例)
根据上述研究可得如下方法(C#语言实现):
private bool IsInnerNew(Point pt,Point[] points)
{
Point p1, p2;
int crossPointCounter=0;
for(int i = 0; i < points.Length; i++)
{
//向右做X轴的水平射线
p1=points[i];
p2=points[(i + 1) % points.Length];
if(p1.Y==p2.Y) continue;
if (pt.Y < Math.Min(p1.Y, p2.Y)) continue;
if (pt.Y >= Math.Max(p1.Y, p2.Y)) continue;
if (pt.X >= Math.Max(p1.X, p2.X)) continue;
double x = (double)(pt.Y - p1.Y) * (double)(p2.X - p1.X) / (double)(p2.Y - p1.Y) + p1.X;
if (x>pt.X)
{
crossPointCounter++;
}
}
if (crossPointCounter%2==1)
return true;
else
return false;
}
根据此方法判断结果,绘制一个点的矩阵,得到结果如下图:
符合预期效果!!!
本文参考如下内容: