如何判断点在多边形内部
预备知识
向量的叉乘
向量的点乘
a = (x1,y1) b=(x2,y2)
a·b = x1x2 + y1y2 = |a||b|cos<a,b>
思路
射线法:从一个点向任意方向《不妨设水平向右》发出一条射线,该射线如多边形的交点的个数,如果是奇数,则在多边形内部,如果是偶数则在外部。
需要前期解决的问题
-
怎么判断点在一条线上?
-
怎么判断点和线的交点?
-
怎么判断点和交点的位置关系?
特殊情况
解决方案
- 对于多边形的水平边不作考虑;
- 对于多边形的顶点和射线相交的情况,如果该顶点是其所属的边上纵坐标较大的顶点,则计数,否则忽略该点:
- 对于О在多边形边上的情形,直接可判断О属于多边行。
测试用例
代码
#include <iostream>
#include <vector>
using namespace std;
const double eps = 1e-6;
int dcmp(double a,double b)
{
if (fabs(a - b) < eps)
return 0;
return a - b > 0 ? 1 : -1;
}
struct Point
{
double x, y;
Point(double x = 0,double y= 0):x(x),y(y){}
Point operator+(const Point& p)const
{
return Point(x + p.x, y + p.y);
}
Point operator-(const Point& p)const
{
return Point(x - p.x, y - p.y);
}
//点积
double operator*(const Point&p)const
{
return x * p.x + y * p.y;
}
//叉积
double operator^(const Point&p)const
{
return x * p.y - y * p.x;
}
};
//点在线上
bool PointOnLine(const Point& p1,const Point& p2,const Point& q)
{
//1、三点共线
//2、q 在不在p1,p2中间(包括p1,p2)
return dcmp((p1 - q) ^ (p2 - q), 0) == 0 && dcmp((p1 - q) * (p2 - q), 0) <= 0;
}
bool PointInPolygon(const vector<Point>& polygon,const Point& p)
{
int size = polygon.size();
if (size < 3)return false;
bool flag = false;
for(int i=0,j=size-1;i<size;j=i++)
{
if (PointOnLine(polygon[i], polygon[j], p))
return true;
//存在水平线段忽略不计
if(dcmp(polygon[i].y,polygon[j].y)==0)
continue;
if(dcmp(min(polygon[i].y,polygon[j].y),p.y)<0 && dcmp(max(polygon[i].y, polygon[j].y), p.y) >= 0)
{
//是否为竖直线段
if(dcmp(polygon[i].x,polygon[j].x) == 0 && dcmp(p.x,polygon[i].x)<=0)
{
flag = !flag;
continue;
}
//p 是否在相交点的左边
if(dcmp(p.x, polygon[i].x - (polygon[i].y - p.y) * (polygon[i].x - polygon[j].x) / (polygon[i].y - polygon[j].y)) < 0)
{
flag = !flag;
}
}
}
return flag;
}
//测试用例
/*
3 1
1 2
2 4
6 4
0.5 5
2 5
6 5
6 5.5
1 8
3 1 no
1 2 no
2 4 yes
6 4 yes
0.5 5 no
2 5 no
6 5 yes
6 5.5 yes
1 8 no
*/
int main()
{
double x = 0, y = 0;
vector<Point> polygon = { Point(1,5),Point(2,2)
,Point(5,1),Point(7,4),Point(9,4)
,Point(7,5),Point(7,6),Point(3,8)
,Point(1,7),Point(3,4) };
while (1)
{
cin >> x;
cin >> y;
if (PointInPolygon(polygon, Point(x, y)))
{
std::cout << "yes\n";
}
else
{
std::cout << "no\n";
}
if (dcmp(x, -1) == 0)
break;
}
return 0;
}