计算几何工具算法-判断点与一个多边形的位置关系

51 篇文章 1 订阅
15 篇文章 0 订阅

题目描述

给出一个n边形的n个顶点坐标 然后给出一个点的坐标,判断这个点与多边形的位置关系

题目分析:

对于在边上的情况很好搞,直接遍历n条边,然后依次用叉积判断即可
对于此点在多边形内部还是外部的问题 可以利用一个性质
从点P画一条足够长的射线 我们会发现 如点P在多边形内部 这条射线与多边形的交点个数为奇数
反之,如果点P在多边形外部,那么这条射线与多边形的交点个数为偶数(包括0)
但会有这样一种特殊情况 如果这条边经过多边形的一个顶点或者另外一个端点就是多边形的某个顶点 就会出现下面的情况
这里写图片描述
虽然在内部,但是也为偶数个交点
对于这种情况,我们有两种解决办法
1.在画一条射线,直到不出现这种特殊情况为止
2.对特殊情况另做处理
由于我们在做射线时使用的是一种随机化的算法,这种特殊情况出现的概率非常小,于是我们采取第一种策略.

算法流程:

1.输入数据
2.逐一判断是否在边上
3.随机在多边形外产生一个点,做出射线,直到射线不过多边形的任何一个点且不予任何一个边共线为止.
4.通过交点个数判断位置关系

代码实现:

#include <cstdio>
#include <iostream>
#include <cmath>
#include <ctime>
#include <cstdlib>
#define eps 1e-10
using namespace std;
int n;
struct Point{
    double x,y;
    Point operator -(Point &s)
    {return (Point){x-s.x,y-s.y};}  
};
Point d[2005];
double operator *(Point a,Point b)
{
    return a.x*b.y-b.x*a.y;
}
bool atedge(Point p0,Point p1,Point p2)
{
    return fabs((p0-p1)*(p2-p1))<eps&&min(p1.x,p2.x)-eps<=p0.x&&p0.x-eps<=max(p1.x,p2.x)&&min(p1.y,p2.y)-eps<=p0.y&&p0.y-eps<=max(p1.y,p2.y);//边上判断,使用叉积
}
bool cross(Point p1,Point p2,Point p3,Point p4)
{
    if(!(max(p1.x,p2.x)>=min(p3.x,p4.x)+eps&&max(p3.x,p4.x)>=min(p1.x,p2.x)+eps)) return 0;
    if(!(max(p1.y,p2.y)>=min(p3.y,p4.y)+eps&&max(p3.y,p4.y)>=min(p1.y,p2.y)+eps)) return 0;
    if(((p3-p1)*(p2-p1))*((p4-p1)*(p2-p1))>eps) return 0;
    if(((p1-p3)*(p4-p3))*((p2-p3)*(p4-p3))>eps) return 0;
    return 1;
}
int check(Point p)
{
    for(int i=1;i<=n;i++)
     if(atedge(p,d[i],d[i==n?1:i+1])) return -1;
    Point prand; 
    while(1)
    {
        bool flag=1;
        prand.x=(rand()<<15)+rand()+1e7;
        prand.y=(rand()<<15)+rand()+1e7;
        for(int i=1;i<=n&&flag;i++)
        if(atedge(d[i],p,prand)) flag=0;
        if(flag) break;
    }
    int sum=0;
    for(int i=1;i<=n;i++)
     sum+=cross(p,prand,d[i],d[i==n?1:i+1]);
    return sum;
}
int main()
{
    Point P;
    scanf("%d",&n);
    srand(time(0));
    for(int i=1;i<=n;i++)
     scanf("%lf%lf",&d[i].x,&d[i].y);
    scanf("%lf%lf",&P.x,&P.y);
    int ans=check(P);
    if(ans==-1) puts("Bian");
    else if(ans%2) puts("Neibu");
     else puts("Waibu");
    return 0;
}

测试数据:

8
2 2
3 6
3 2
6 3
6 4
4 3
7 6
7 1
5 3 外部

8
2 2
3 6
3 2
6 3
6 4
4 3
7 6
7 1
5 2 内部

8
2 2
3 6
3 2
6 3
6 4
4 3
7 6
7 1
5 4 边上
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值