用射线法实现判断点是否在多边形内部

最近工作中遇到了这个问题,检索之后发现这种实现方式挺有意思的,无论是凸多边形还是凹多边形都可以判断。

射线法是用被测点向任意方向(通常为了好算,使其射向右侧)做一条射线,判断射线与多边形的交点。如果交点的数量为奇数,则被测点在多边形内;如果交点的数量为偶数,则被测点在多边形以外。

期间,有些特殊情况需要判断,比如:

1. 射线刚好经过凸多边形两条相邻边的交点上的情况会导致重复判断;

2.射线和多边形的边重合的情况。

先上js代码。

function isDotInPolygon(point, polygonPoints) {
  var flag = false,
      p1,
      p2;
  for(var i = 0, j = polygonPoints.length - 1; i < polygonPoints.length; j = i++) {
    p1 = polygonPoints[i];
    p2 = polygonPoints[j];
    // 这里判断是否刚好被测点在多边形的边上
    if(isDotInLineSegment(point, p1, p2)) return true;
    if((p1.y > point.y != p2.y > point.y) && (point.x < (point.y - p1.y) * (p1.x - p2.x) / (p1.y - p2.y) + p1.x)) {
      flag = !flag;
    }
  }
  return flag;
}

判断点是否在线段上的 isDotInLineSegment 的函数我懒得写了,这个很好实现。

关键点在于判断射线与多边形边相交的部分,这段代码原理不是我想出来的,它实现的方式很是精妙。

首先咱们看 表达式1:  p1.y > point.y != p2.y > point.y

表面上看,表达式1是用了一个类似于异或的判断,要求被测点的y坐标在循环中多边形当前边的y轴投影范围内;

它其实还隐藏了另外一层条件,可以过滤掉前面提到的特殊情况1 和 2。 举个例子,多边形相邻的俩个边所在线段(p1, p2)和 (p2, p3),为了解释方便,咱们假设其中p3.y > p2.y > p1.y。 如果从被测点发射出来的射线经过了p2 ,那么上面这段表达式1其实在判断(p1, p2)时为false,判断(p2, p3)时为true,这样就巧妙地避免了重复计数的问题。   而如果是个凹多边形,存在p3.y > p2.y < p1.y , 那么表达式1 在判断(p1, p2)与(p2, p3)时都为true, 可以正确地计数两次。

然后看 表达式2 : point.x < (point.y - p1.y) * (p1.x - p2.x) / (p1.y - p2.y) + p1.x

同样为了解释方便,咱们假设其中p2.y > point.y > p1.y, 表达式2 等同于 (point.x - p1.x) / (point.y - p1.y) < (p1.x - p2.x) / (p1.y - p2.y)  ,其中  (p1.x - p2.x) / (p1.y - p2.y) 可以理解为是线段(p1, p2)斜率的倒数, (point.x - p1.x) / (point.y - p1.y) 则是线段 (p1, point) 斜率的倒数。如果线段(p1, p2)的斜率的倒数要大于线段 (p1, point) 的斜率的倒数,则点point就只能在线段(p1, p2)的左侧。这样就确保了射线与线段(p1, p2)的相交。

这段看起来有点复杂,其实拿动笔画一画就很容易明白(没错,我又懒得上图了)。

原文地址:https://www.cnblogs.com/roay/p/9029133.html

时间: 2024-08-25 23:12:29

用射线法实现判断点是否在多边形内部的相关文章

hlg1306再遇攻击--射线法判断点是否在多边形内部

再遇攻击 Time Limit: 1000 MS Memory Limit: 65536 K Total Submit: 313(40 users) Total Accepted: 91(32 users) Rating:  Special Judge: No Description Dota中英雄技能攻击会有一个范围,现在释放一个技能给出他的攻击范围和目标英雄的位置,问是否能攻击到.攻击范围保证是一个多边型. Input 有多组测试数据 第一行输入1个整数n, 期中n代表攻击范围是给出的n个点

判断点是否在多边形内部

转自阿凡卢原文 判断点是否在多边形内部 如何判断一个点是否在多边形内部? (1)面积和判别法:判断目标点与多边形的每条边组成的三角形面积和是否等于该多边形,相等则在多边形内部. (2)夹角和判别法:判断目标点与所有边的夹角和是否为360度,为360度则在多边形内部. (3)引射线法:从目标点出发引一条射线,看这条射线和多边形所有边的交点数目.如果有奇数个交点,则说明在内部,如果有偶数个交点,则说明在外部. 具体做法:将测试点的Y坐标与多边形的每一个点进行比较,会得到一个测试点所在的行与多边形边的

LightOj1190 - Sleepwalking(判断点与多边形的位置关系--射线法模板)

题目链接:http://lightoj.com/volume_showproblem.php?problem=1190 题意:给你一个多边形含有n个点:然后又m个查询,每次判断点(x, y)是否在多边形的内部; 射线法判断即可适用于任何(凸或凹)多边形;时间复杂度为O(n); 判断一个点是在多边形内部,边上还是在外部,时间复杂度为O(n):射线法可以正确用于凹多边形: 射线法是使用最广泛的算法,这是由于相比较其他算法而言,它不但可以正确使用在凹多边形上,而且不需要考虑精度误差问题.该算法思想是从

matlab练习程序(射线法判断点与多边形关系)

依然是计算几何. 射线法判断点与多边形关系原理如下: 从待判断点引出一条射线,射线与多边形相交,如果交点为偶数,则点不在多边形内,如果交点为奇数,则点在多边形内. 原理虽是这样,有些细节还是要注意一下,比如射线过多边形顶点或射线与多边形其中一边重合等情况还需特别判断. 这里就不特别判断了,因为我只是熟悉原理,并不是实际运用. 好吧,我实际是太懒了,不想判断了. 结果如下: 结果图和线性分类器的组合有几分相似. matlab代码如下: clear all;close all;clc; polyn=

【模板】计几 射线法判断点是否在简单多边形内

1 // 2 //线段交点个数 3 int SegCross(Segment a,Segment b){ 4 double x1 = a.s.cross(a.e,b.s); 5 double x2 = a.s.cross(a.e,b.e); 6 double x3 = b.s.cross(b.e,a.s); 7 double x4 = b.s.cross(b.e,a.e); 8 if( b.e.OnLine(a.s,a.e) && b.s.OnSeg(a.s,a.e) || 9 b.s.O

POJ 3182 The Grove [DP(spfa) 射线法]

题意: 给一个地图,给定起点和一块连续图形,走一圈围住这个图形求最小步数 本来是要做课件上一道$CF$题,先做一个简化版 只要保证图形有一个点在走出的多边形内就可以了 $hzc:$动态化静态的思想,假设已经有了路线怎么判断合法 点在多边形内是“点变多边形不变”,我们反过来维护多边形变 $f[i][j][0/1]$表示当前走到$(i,j)$,点是否在多边形内 维护一条向右发出的射线,每次走的时候看看有没有穿过射线就行了 因为这是个网格,我们可以规定只有从上面经过才算穿过 然后,这不是$DAG$啊怎

WebGL模型拾取——射线法的补充

这篇文章是对射线法raycaster的补充,上一篇文章主要讲的是raycaster射线法拾取模型的原理,而这篇文章着重讲使用射线法要注意的地方.首先我们来看下图. 我来解释一下上图中的originTriangle,这就是Triangle2三角形第一次绘制在空间中的位置,而Triangle2当前的位置是经过一系列空间变换而来的(这些位置姿态变换大多是由用户鼠标交互产生),变换矩阵就是transformMatrix.这下就引出了本文第一个重点,那就是做raycaster的时候要保证线段碰撞模型的时候

WebGL模型拾取——射线法二

这篇文章是对射线法raycaster的补充,上一篇文章主要讲的是raycaster射线法拾取模型的原理,而这篇文章着重讲使用射线法要注意的地方.首先我们来看下图. 我来解释一下上图中的originTriangle,这就是Triangle2三角形第一次绘制在空间中的位置,而Triangle2当前的位置是经过一系列空间变换而来的(这些位置姿态变换大多是由用户鼠标交互产生),变换矩阵就是transformMatrix.这下就引出了本文第一个重点,那就是做raycaster的时候要保证线段碰撞模型的时候

射线法

射线法 这是一个大佬看了都说简单的算法....(甚至觉得没有掌握的必要) QAQ 这个算法是用来判断一个点是否在一个多边形以内.很简单 将这个点沿着x轴的正方向作射线.如果穿过的边数为基数,那么这个点在多边形内:反之不在. 由于有可能出现经过两条边的相邻的点,而被重复计算的情况.我们只需要采用上加下不加策略,即如果射线经过了这条边的上顶点,则cnt++,如果经过下顶点,则不变.经过这条边上除了上顶点和下顶点的地方,cnt++.正确性显然. 另外因为我们需要将每条边都遍历完.而我们只向 x的正方向