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

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

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

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

 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)的相交。

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

猜你喜欢

转载自www.cnblogs.com/roay/p/9029133.html