如何判断点在多边形内部

如何判断点在多边形内部

预备知识

​ 向量的叉乘
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JRDypVrB-1663419615077)(点在多边形内部.assets/image-20220917205427141.png)]

​ 向量的点乘

​ a = (x1,y1) b=(x2,y2)

​ a·b = x1x2 + y1y2 = |a||b|cos<a,b>

思路

射线法:从一个点向任意方向《不妨设水平向右》发出一条射线,该射线如多边形的交点的个数,如果是奇数,则在多边形内部,如果是偶数则在外部。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ujc0eBCa-1663419615078)(点在多边形内部.assets/image-20220917205603361.png)]

需要前期解决的问题

  • 怎么判断点在一条线上?

  • 怎么判断点和线的交点?

  • 怎么判断点和交点的位置关系?

特殊情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dAP6ACPN-1663419615079)(点在多边形内部.assets/image-20220917205720325.png)]

\

解决方案

  • 对于多边形的水平边不作考虑;
  • 对于多边形的顶点和射线相交的情况,如果该顶点是其所属的边上纵坐标较大的顶点,则计数,否则忽略该点:
  • 对于О在多边形边上的情形,直接可判断О属于多边行。

测试用例

在这里插入图片描述

代码

#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;
}
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Yi_Xiao

来瓶可乐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值