此篇博客实现了判定平面一点是否在给定多边形内部的功能。精确,性能优良,因为只包含加法和乘法运算,效率极高。
实现的C++源码如下,注意点在源码的相应注释中。
有错误和不好的地方还望各位批评指正。
#include<iostream>
using namespace std;
struct Point2f // 用以进行简单测试的结构。opencv中也有同名的数据类型,注意其x和y与我们想的不一样。
{
double x;
double y;
};
bool onPolygonTest(Point2f polygon[], int num, Point2f test_point); // 用以测试在多边形边上的退化情况
bool onLineTest(Point2f p, Point2f q, Point2f s); // 用以测试在边上的退化情况
bool inPolygonTest(Point2f polygon[], int num, Point2f test_point); // 一般情况
bool toLeftTest(Point2f p, Point2f q, Point2f s); // 核心技术
double Area2(Point2f p, Point2f q, Point2f s); // 核心技术实现
/*----------------------------------------------------------------------*/
/* 函数bool isInner(Point2f [], int num, Point2f, bool) */
/* 输入 */
/* 参数1 多边形顶点: Point2f polygon[] */
/* 参数2 多边形顶点个数 */
/* 参数3 测试点:Point2f test_point */
/* 参数4 是否包含边上的点:bool isIncludeLine */
/* 输出 */
/* true:在多边形内; */
/* false:不在多边形内 */
/* 作者:小鸭酱的书签 */
/* 日期:2016年9月28日 */
/*--------------------------------------------------------------------*/
bool isInner(Point2f polygon[], int num, Point2f test_point, bool isIncludeLine)
{
if(num < 3)
return false;
if (isIncludeLine) // 首先判断退化情况
return (onPolygonTest(polygon, num, test_point) || inPolygonTest(polygon, num, test_point));
else // 一般情况
return inPolygonTest(polygon, num, test_point);
}
bool onPolygonTest(Point2f polygon[], int num, Point2f test_point) // 只要点在任意一条边上,都算作在内部
{
int sum = 0;
int i = 0;
for(; i < num - 1; ++i)
{
sum += onLineTest(polygon[i], polygon[i+1], test_point);
if (sum > 0)
return true;
}
sum += onLineTest(polygon[i], polygon[0], test_point);
return sum > 0;
}
bool onLineTest(Point2f p, Point2f q, Point2f s) // 处理退化情况,判断点是否在线上;这里只处理多边形是没有旋转的矩形的情况。后期更新任意线段
{
if(p.x == q.x && q.x == s.x && (s.y - p.y) * (s.y - q.y) <= 0 )
return true;
else if(q.y == p.y && p.y == s.y && (s.x - p.x) * (s.x - q.x) <= 0 )
return true;
else
return false;
}
bool inPolygonTest(Point2f polygon[], int num, Point2f test_point) // 根据点在每条边的left或者right的pattern来判断是否在内部
{
int sum = 0; // 用以记录有多少个left的pattern
int i = 0;
for(; i < num - 1; ++i)
{
sum += toLeftTest(polygon[i], polygon[i+1], test_point);
}
sum += toLeftTest(polygon[i], polygon[0], test_point);
return (sum == 0 || sum == num) ? true : false; // 很显然,当点在多边形的每条有向边的right或者left,它才会在内部
}
bool toLeftTest(Point2f p, Point2f q, Point2f s) // 主要技术。有向面积值大于0则在有向边的左边
{
return Area2(p, q, s) > 0;
}
double Area2(Point2f p, Point2f q, Point2f s) // 可见其只包含加法和乘法运算,计算给定点和多边形的一条有向边所组成的三角形的2倍面积,我们是要使用其符号来判断在左边还是右边
{
return
p.x * q.y - p.y * q.x
+ q.x * s.y - q.y * s.x
+ s.x * p.y - s.y * p.x;
}
int main() // test
{
Point2f polygon[4];
polygon[0].x = 0;
polygon[0].y = 0;
polygon[1].x = 2;
polygon[1].y = 0;
polygon[2].x = 2;
polygon[2].y = 1;
polygon[3].x = 0;
polygon[3].y = 1;
Point2f test_point;
test_point.x = 0;
test_point.y = 2;
int n = sizeof(polygon) / sizeof(Point2f);
cout << "in = " << inPolygonTest(polygon, n, test_point) << endl;
cout << "online = " << onPolygonTest(polygon, n, test_point) << endl;
cout << "output = " << isInner(polygon, n, test_point, false) << endl;
cout << n << endl << endl;
return 0;
}