方法一:扫描法(使用于任意多边形)
通常情况下,当射线与多边形的交点个数是奇数时,Q在多边形内,是偶数时,Q在多边形外。
通常将射线设为水平向右,那么就有一些特殊情况值得考虑
1.射线与多边形的顶点相交,这是交点只能计算一个。
2.射线与多边形顶点的交点不应该被计算
3.射线与多边形的一条边重合,这条边应该被忽略
算法描述:首先,对于多边形的水平边不做考虑,其次,对于多边形的顶点和射线相交的情况,如果该顶点时其所属的边上纵坐标较大的顶点,则计数,否则忽略该点,最后,对于Q在多边形上的情形,直接判断Q是否属于多边形。
时间复杂度分析:O(n)
注意:点的输入必须有顺序,不是顺时针就是逆时针
附上代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=110;
const double eps=1e-5;
struct point{
double x,y;
};
point poly[maxn];
int n,q;
bool onsegment(point pi,point pj,point Q)
{
if((Q.x-pi.x)*(pj.y-pi.y)==(pj.x-pi.x)*(Q.y-pi.y)&&min(pi.x,pj.x)<=Q.x&&Q.x<=max(pi.x,pj.x)&&min(pi.y,pj.y)<=Q.y&&Q.y<=max(pi.y,pj.y)){
return true;
}else{
return false;
}
}
bool insidepolygon(point p)
{
int counter=0;
double xinters;
point p1,p2;
p1=poly[0];
for(int i=1;i<=n;i++){
p2=poly[i%n];
if(onsegment(p1,p2,p)){
return true;
}
if(p.y>min(p1.y,p2.y)){
if(p.y<=max(p1.y,p2.y)){
if(p.x<=max(p1.x,p2.x)){
if(p1.y!=p2.y){
xinters=(p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x;
if(p1.x==p2.x||p.x<=xinters){
counter++;
}
}
}
}
}
p1=p2;
}
if(counter%2==0){
return false;
}
return true;
}
int main()
{
point p;
scanf("%d%d",&n,&q);
for(int i=0;i<n;i++){
scanf("%lf%lf",&poly[i].x,&poly[i].y);
}
for(int i=0;i<q;i++){
scanf("%lf%lf",&p.x,&p.y);
if(insidepolygon(p)){
printf("within\n");
}else{
printf("outside\n");
}
}
return 0;
}
方法二:叉乘判别法(只适用于凸多边形)
设这个多边形的边数为n。选一条边作为1号边,然后按照顺时针或者逆时针给每条边编号,连接第i(i=1,2,3......,n)条边的第一个端点和要测试的点得到一个向量vi。连接第一个端点与第二个端点得到向量ui,都以第一个端点为起点,作叉积vi*ui,记录结果。除了第一条边以外,都与前一次运算得到的叉积作乘积。如果为正则继续判断,知道遍历所有边,若全部满足,则证明点在多边形内,否则,证明点在多边形外。
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=110;
struct point{
double x,y;
};
point poly[maxn];
int n,q;
double multi(point p1,point p2,point p0)
{
return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
bool isinside(point p)
{
double pre,now;
for(int i=0;i<n;i++){
now=multi(p,poly[i],poly[(i+1)%n]);
if(i>0){
if(pre*now<0){
return 0;
}
}
pre=now;
}
return true;
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=0;i<n;i++){
scanf("%lf%lf",&poly[i].x,&poly[i].y);
}
point p;
for(int i=0;i<q;i++){
scanf("%lf%lf",&p.x,&p.y);
if(isinside(p)){
printf("Yes\n");
}else{
printf("No\n");
}
}
return 0;
}
方法三:角度和的判断法(适用于任意多边形)
对于平面多边形来说,连接多边形内点于多边形所有顶点所形成的所有角的角度在要求精度范围内应该等于360度,如果小于360度或者大于360度,则证明不在多边形中。
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=110;
const double pi=3.1415926;
struct point{
double x,y;
};
point poly[maxn];
int n,q;
double ang(double x1,double y1,double x2,double y2)
{
double ans=x1*x2+y1*y2;
double base=sqrt(x1*x1+y1*y1)*sqrt(x2*x2+y2*y2);
ans/=base;
return acos(ans);
}
int inpolygon(point p)
{
double angle=0;
for(int i=0;i<n;i++){
double x1=poly[i].x-p.x;
double y1=poly[i].y-p.y;
double x2=poly[(i+1)%n].x-p.x;
double y2=poly[(i+1)%n].y-p.y;
angle+=ang(x1,y1,x2,y2);
}
if(fabs(angle-2*pi)<0.000001){
return true;
}else{
return false;
}
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=0;i<n;i++){
scanf("%lf%lf",&poly[i].x,&poly[i].y);
}
point p;
for(int i=0;i<q;i++){
scanf("%lf%lf",&p.x,&p.y);
if(inpolygon(p)){
printf("Yes\n");
}else{
printf("No\n");
}
}
return 0;
}