判断点在多边形内

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

0.前言

最近不断遇到类似的几何位置问题,一直没有花时间去总结,本文总结了我常用点跟多边形的位置判断方法以及代码。希望能够对大家有所帮助。

文中所指的多边形均为凸多边形,一些描述可能有误,欢迎指正。

1.测试的多边形

在开始之前,我们需要先构建好测试环境。

我构建了一个比较特殊的多边形,如下。

/ \

|  |

|_|

从最上面的顶点顺时针坐标(屏幕坐标系)分别为:(40,10) (60,30)(60,50) (20,50) (20,30)

2.射线判别法

 

根据对多边形的了解,我们可以得出如下结论:

如果一个点在多边形内部,任意角度做射线肯定会与多边形要么有一个交点,要么有与多边形边界线重叠。

如果一个点在多边形外部,任意角度做射线要么与多边形有一个交点,要么有两个交点,要么没有交点,要么有与多边形边界线重叠。

利用上面的结论,我们只要判断这个点与多边形的交点个数,就可以判断出点与多边形的位置关系了。

首先罗列下注意事项:

l 射线跟多边形的边界线重叠的情况

l 区别内部点和外部点的射线在有一个交点时的情况

对于第一个注意事项,可以将射线角度设为零度,这样子只需要判断两个相邻顶点的Y值是否相等即可。然后再判断这个点的X值方位。

对于第二个注意事项,网上许多文章都说到做射线以后交点为奇数则表示在多边形内部,这是一个错误的观点,不仅对于凹多边形不成立,对于凸多边形也不成立。

例如:从外部点做射线刚好经过一顶点,这样子交点个数就为奇数,但是该点却不在多边形内部。

至于要如何区分这两种情况呢,我能想到了一个不完美的方法,外部点的射线跟多边形有一个交点的时候,该交点肯定为顶点,如果该射线上移一位或者下移 一位,要么变成有两个交点要么没有交点。当然为了安全起见,这里把射线尽量往相邻点中心移动。这样子就能够判断出是外部点的射线跟多边形有一个交点。

不过这个方法并不完美,因为有了移位操作,可能会把内部点移动出外部。而且如果判断点在(60,30)位置,判断的时候先遇到(20,30),然后移位操作,就判断就出错了。

为了解决这些问题,我在起初先扫描一次判断点是否在顶点上虽然影响了一点效率,而且当判定点距离多边形一个单位时,判断可能会有误。不过只要不是需要高精度的话,这个方法还是很有效的。

代码如下:

 

复制代码
bool InsidePolygon1( POINTD *polygon,int N,POINTD pt )
{ int i,j; bool inside,redo;redo = true; for (i = 0;i < N;++i){ if (polygon[i].x == pt.x && // 是否在顶点上  polygon[i].y == pt.y ){redo = false;inside = true; break;}} while (redo){redo = false;inside = false; for (i = 0,j = N - 1;i < N;j = i++) { if ( (polygon[i].y < pt.y && pt.y < polygon[j].y) || (polygon[j].y < pt.y && pt.y < polygon[i].y) ) { if (pt.x <= polygon[i].x || pt.x <= polygon[j].x) { double _x = (pt.y-polygon[i].y)*(polygon[j].x-polygon[i].x)/(polygon[j].y-polygon[i].y)+polygon[i].x; if (pt.x < _x) // 在线的左侧  inside = !inside; else if (pt.x == _x) // 在线上  {inside = true; break;}}} else if ( pt.y == polygon[i].y) { if (pt.x < polygon[i].x) // 交点在顶点上  {polygon[i].y > polygon[j].y ? --pt.y : ++pt.y;redo = true; break;}} else if ( polygon[i].y ==  polygon[j].y && // 在水平的边界线上  pt.y == polygon[i].y &&( (polygon[i].x < pt.x && pt.x < polygon[j].x) || (polygon[j].x < pt.x && pt.x < polygon[i].x) ) ){inside = true; break;}}} return inside;
}
复制代码

 

3.角度和判别法

 

  角度和判别法就是判定点与多边形所有相邻顶点组成的角的角度和来判断点与多边形的位置关系。

如果点在多边形内部,只要该点不在边界线或者顶点上,则角度和为三百六十度。

如果在边界线上,则角度和为一百八十度。

如果点在多边形外部,则角度和无法达到三百六十度。但是角度和可能会达到一百八十度,比如判断点在(60,10)

代码如下:

 

复制代码
// 根据需要不判断顶点 bool IsPointInLine( const POINTD &pt,const POINTD &pt1,const POINTD &pt2 )
{ bool inside = false; if (pt.y == pt1.y &&pt1.y == pt2.y &&((pt1.x < pt.x && pt.x < pt2.x) || (pt2.x < pt.x && pt.x < pt1.x)) ){inside = true;} else if (pt.x == pt1.x &&pt1.x == pt2.x &&((pt1.y < pt.y && pt.y < pt2.y) || (pt2.y < pt.y && pt.y < pt1.y)) ){inside = true;} else if ( ((pt1.y < pt.y && pt.y < pt2.y) || (pt2.y < pt.y && pt.y < pt1.y)) &&((pt1.x < pt.x && pt.x < pt2.x) || (pt2.x < pt.x && pt.x < pt1.x)) ){ if (0 == (pt.y-pt1.y)/(pt2.y-pt1.y)-(pt.x - pt1.x) / (pt2.x-pt1.x)){inside = true;}} return inside;
} bool InsidePolygon2( POINTD *polygon,int N,POINTD p )
{ int i,j; double angle = 0; bool inside = false; for (i = 0,j = N - 1;i < N;j = i++) { if (polygon[i].x == p.x && // 是否在顶点上  polygon[i].y == p.y){inside = true; break;} else if (IsPointInLine(p,polygon[i],polygon[j])) // 是否在边界线上  {inside = true; break;} double x1,y1,x2,y2;x1 = polygon[i].x - p.x;y1 = polygon[i].y - p.y;x2 = polygon[j].x - p.x;y2 = polygon[j].y - p.y; double radian = atan2(y1,x1) - atan2(y2,x2);radian = abs(radian); if (radian > M_PI) radian = 2* M_PI - radian;angle += radian; // 计算角度和  } if ( fabs(6.28318530717958647692 - angle) < 1e-7 )inside = true; return inside;
}
复制代码

 

有的人管角度和判别法叫做转角法,还有一个类似的方法叫弧长法。

http://haibarali.blog.163.com/blog/static/23474013200722813356858/ 

4.面积和判别法

 

根据角度和判别法,我们可以继续演化,可以得出如下结论:

如果一个点在多边形内部,该点与多边形所有相邻顶点组成的三角形面积和为多边形面积。反之不成立。

求三角形面积:

已知三角形Ax1,y1Bx2,y2Cx3,y3),则面积公式为:

                 |x1  x2  x3|   

S(A,B,C) = |y1  y2  y3| * 0.5 (当三点为逆时针时为正,顺时针则为负的)   

                 |1   1   1 |  

= ( x1*y2 - x1*y3 - x2*y1 + x2*y3 + x3*y1 - x3*y2 ) * 0.5

求多边形面积:

根据上面求三角形的方法,

已知多边形A1A2A3...An(顺或逆时针都可以),设平面上有任意的一点P,则面积公式为:   

S(A1,A2,A3...An)   

     =  S(PA1A2)+ S(PA2A3)+...+S(P,An,A1)

    

既然P是可以任取,为了方便用(0,0)好了。

代码如下:

复制代码
bool InsidePolygon3( POINTD *polygon,int N,POINTD pt )
{ int i,j; bool inside = false; double polygon_area = 0; double trigon_area = 0; for (i = 0,j = N - 1;i < N;j = i++) {polygon_area += polygon[i].x * polygon[j].y - polygon[j].x * polygon[i].y;trigon_area += abs(pt.x * polygon[i].y -pt.x * polygon[j].y -polygon[i].x * pt.y +polygon[i].x * polygon[j].y +polygon[j].x * pt.y -polygon[j].x * polygon[i].y);}trigon_area *= 0.5;polygon_area = abs(polygon_area * 0.5); if ( fabs(trigon_area - polygon_area) < 1e-7 )inside = true; return inside;
}
复制代码


多边形面积计算

http://www.cppblog.com/zyzx/archive/2009/04/27/81241.html 

行列式如何展开

http://www.tongji.edu.cn/~math/xxds/kcja/kcja_b/1-6.htm 

5.点线判别法

经网友glshader提示,添加了这个方法。

如果判断点在所有边界线的同侧,就能判定该点在多边形内部。

判断方法就是判断两条同起点射线斜率差。

代码如下:

 

复制代码
bool InsidePolygon4( POINTD *polygon,int N,POINTD p )
{ int i,j; bool inside = false; int count1 = 0; int count2 = 0;      for (i = 0,j = N - 1;i < N;j = i++) {          double value = (p.x - polygon[j].x) * (polygon[i].y - polygon[j].y) - (p.y - polygon[j].y) * (polygon[i].x - polygon[j].x);if (value > 0)++count1;          else if (value < 0)++count2;} if (0 == count1 || 0 == count2){inside = true;}     return inside;
}

转载于:https://my.oschina.net/u/1247611/blog/169680

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/292642.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Centos6.5使用ELK(Elasticsearch + Logstash + Kibana) 搭建日志集中分析平台实践

Centos6.5安装Logstash ELK stack 日志管理系统概述&#xff1a;日志主要包括系统日志、应用程序日志和安全日志。系统运维和开发人员可以通过日志了解服务器软硬件信息、检查配置过程中的错误及错误发生的原因。经常分析日志可以了解服务器的负荷&#xff0c;性能安全性&#…

k8s 手动恢复redis 集群_二进制手动部署k8s-1.14高可用集群(二、集群部署)

1. CA证书&#xff08;任意节点&#xff09;1.1 安装cfsslcfssl是非常好用的CA工具&#xff0c;我们用它来生成证书和秘钥文件 安装过程比较简单&#xff0c;如下&#xff1a;# 下载 $ mkdir -p ~/bin $ wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 -O ~/bin/cfssl $ w…

实现DDD领域驱动设计: Part 1

原文链接: https://dev.to/salah856/implementing-domain-driven-design-part-i-5a72简单的代码&#xff01;踢足球很简单&#xff0c;难的是踢简单的足球。— 克鲁伊夫如果我们将这句话用到编程上&#xff0c;我们可以说&#xff1b;写代码很简单&#xff0c;难的是写简单的代…

XE5 Android 开发实现手机打电话和发短信 [转]

其实都可以通过intent和URI调用系统功能.Windows程序员可以理解成是ShellExecute.这个是万金油.可以有调用各种功能.后面会介绍. 1.短信息.很简单 方法a.不使用Intent而是直接发短信. smsManager对应的Delphi代码应该是: usesAndroidapi.JNI.JavaTypes,Androidapi.JNI.Telephon…

安装程序无法创建新的系统分区也无法定位现有分区的解决方法

2019独角兽企业重金招聘Python工程师标准>>> 在安装Windows7时&#xff0c;想必有很多人都安碰到这样的情况吧!在安装界面里选择安装时&#xff0c;却出现“安装程序无法创建新的系统分区&#xff0c;也无法定位现有系统分区” 网上提供的另外解决方法大全&#xff…

python多线程读取数据库数据_Python基于多线程操作数据库相关知识点详解

Python基于多线程操作数据库相关问题分析 本文实例分析了Python多线程操作数据库相关问题。分享给大家供大家参考&#xff0c;具体如下&#xff1a; python多线程并发操作数据库&#xff0c;会存在链接数据库超时、数据库连接丢失、数据库操作超时等问题。 解决方法&#xff1a…

IDA64 Fatal error before kernel init

http://www.tuicool.com/articles/7FZVZna 第一次看到这个错误还以为是修改文件导致的&#xff0c;但是觉得又不大像&#xff0c;因为在Win7底下是完全正常的。搜索了一下才发现是由于插件导致的&#xff1a; NOTE3: You get a “Fatal error before kernel init” when trying…

Android安全与逆向之在ubuntu上面搭建NDK环境

1、下载Android NDK自解压包&#xff0c; 官方地址&#xff1a;https://developer.android.com/ndk/downloads/index.html#download下载&#xff1a;$ wget -c http://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.bin 2、解压&#xff0c; 将Android NDK压缩包…

和我一起来分析某药品仓储管理系统 卡死现象

一&#xff1a;背景 1. 讲故事这个月初&#xff0c;有位朋友wx上找到我&#xff0c;说他的api过一段时间后&#xff0c;就会出现只有请求&#xff0c;没有响应的情况&#xff0c;截图如下&#xff1a;从朋友的描述中看样子程序是被什么东西卡住了&#xff0c;这种卡死的问题解决…

如何定位Source Generators性能问题

前言在以前的文章中&#xff0c;我们介绍过如何调试Source Generators。但是当实现较为复杂时&#xff0c;我们需要能够快速地分析Source Generators性能的方法。默认情况下&#xff0c;使用VS 2019自带的“性能探查器”不能分析Source Generators项目&#xff0c;因为它是类库…

什么是域(domain)

在今天很多人都有意识或无意识的跟域这个东西打过交道。如果你在公司里使用电脑&#xff0c;并且你的电脑接入了公司的局域网&#xff0c;那你的电脑很可能就在一个域中。如何查看你的电脑是否连接到一个域中&#xff0c;以Windows为例&#xff0c;右击我的电脑 –>属性&…

四舍五入运算

为什么80%的码农都做不了架构师&#xff1f;>>> 请用户输入1个实数(浮点型),请编写算法对这个实数进行四舍五入到个位的运算. 例如 12.56经过四舍五入运算得到13. 而12.45经过四舍五入运算得到12 代码如下地址: http://git.oschina.net/touch1D/qf17j3eughwdp8zxi…

python 新建html_Python学习第226课——html中创建按钮

前面我们学习了一些常见的表单元素&#xff0c;表单就是用来收集用户的信息的&#xff0c;比如调查问卷、用户登录网站的页面等这类的网页&#xff0c;里面都要用到表单元素&#xff0c;当用户在前端页面上填入账号、密码、或者调查问卷的答案等等信息之后&#xff0c;就需要提…

WPF 实现图片切成九宫格控件~

WPF开发者QQ群&#xff1a; 340500857由于微信群人数太多入群请添加小编微信号yanjinhuawechat 或 W_Feng_aiQ 邀请入群需备注WPF开发者 PS&#xff1a;有更好的方式欢迎推荐。接着上一篇倒计时控件01—代码如下一、创建 CropControl.cs代码如下。&#xff08;修改RowColumn “…

GCT之数学公式(三角函数)

转载于:https://www.cnblogs.com/jyh317/p/3386598.html

Android安全与逆向之Java虚拟机和Dalvik虚拟机的区别

Google于2007年底正式发布了Android SDK, 作为 Android系统的重要特性&#xff0c;Dalvik虚拟机也第一次进入了人们的视野。它对内存的高效使用&#xff0c;和在低速CPU上表现出的高性能&#xff0c;确实令人刮目相看。 依赖于底层Posix兼容的操作系统&#xff0c;它可以简单的…