最近项目需要, 所以查了一下实现2D平面内判断一个点在多边形内的方法. 感觉这个方法非常巧妙且计算量不大.
在这个方法中, 多边形边界是用点来描述的. 多边形的凸凹性不受限制, 也就是说, 多边形可以是凸也可以是凹的.
对于任意两个构成多边形边界的点, 有如下这种情况:(这里假设这两个点连线斜率为正于是画成了下面的样子)
构成多边形的点是 ( x i , y i ) (x_i, y_i) (xi,yi)和 ( x i + 1 , y i + 1 ) (x_{i+1}, y_{i+1}) (xi+1,yi+1), 要判断的点是 ( x 0 , y 0 ) (x_0, y_0) (x0,y0). 红色的线是一条从 ( x 0 , y 0 ) (x_0, y_0) (x0,y0)出发斜率为 ∞ \infty ∞的射线.
首先判断
(
x
0
,
y
0
)
(x_0, y_0)
(x0,y0)是否在
x
i
x_i
xi和
x
i
+
1
x_{i+1}
xi+1之间. 故该点需要满足
x
i
<
x
0
<
x
i
+
1
或
者
是
x
i
+
1
<
x
0
<
x
i
x_i<x_0<x_{i+1}或者是\\ x_{i+1}<x_0<x_{i}
xi<x0<xi+1或者是xi+1<x0<xi
接下来还需要判断
(
x
0
,
y
0
)
(x_0, y_0)
(x0,y0)是不是在黑色的线的下面
k
=
y
i
+
1
−
y
i
x
i
+
1
−
x
i
y
0
<
k
(
x
0
−
x
i
)
+
y
i
k=\frac{y_{i+1}-y_i}{x_{i+1} - x_i}\\ y_0<k(x_0-x_i)+y_i
k=xi+1−xiyi+1−yiy0<k(x0−xi)+yi
这样就完成了判断点
(
x
0
,
y
0
)
(x_0, y_0)
(x0,y0)在黑色直线下的过程.
接下来对于构成多边形边界的所有的点
x
i
x_i
xi和
x
i
+
1
x_{i+1}
xi+1都应用上面的方法进行判断. 只要满足上述准则, 就设置计数器加1.
如果要判断的点
(
x
0
,
y
0
)
(x_0, y_0)
(x0,y0)在多边形的范围内, 那一定满足计数器为奇数, 也就是对2取余为1.
例如, 下图计数器结果是1.
下图中结果是3.
代码如下
bool contains(double x0, double y0)
{
int crossings = 0;
for(int i=0; i<N; i++)
{
double slope = (y[i+1] - y[i])/(x[i+1] - x[i]);
bool cond1 = (x[i] < x0) && (x[i+1] > x0);
bool cond2 = (x[i+1] < x0) && (x[i] > x0);
bool above = (y0 < slope * (x0 - x[i]) + y[i]);
if((cond1 || cond2) && above)
{
crossings++;
}
}
return (crossings %2 != 0)
}
参考文献
https://github.com/sromku/polygon-contains-point
小结
前面有一篇博客是来说如何判断两个三角形是否相交的问题. 仔细想想之后发现, 本篇文章提供的方法虽然简单, 但是不能用在那个问题上. 例如这种情况 :
没有一个点在三角形内, 然而两个三角形却是相交的.