Geometry 如何在二维整数坐标中测试点是否位于凸多边形的内部?

Geometry 如何在二维整数坐标中测试点是否位于凸多边形的内部?,geometry,polygon,hittest,Geometry,Polygon,Hittest,多边形以矢量2i对象(二维整数坐标)列表的形式给出。如何测试给定点是否在内部?我在web上发现的所有实现都因一些微不足道的反例而失败。写一个正确的实现似乎真的很难。语言并不重要,因为我将自己移植它。对于这个问题,光线投射或缠绕方法是最常见的。有关详细信息,请参阅 此外,请查看C.中有详细记录的解决方案。如果它是凸的,检查它的一个简单方法是该点位于所有线段的同一侧(如果以相同的顺序遍历) 你可以很容易地用点积来检查(因为它与线段和点之间形成的角度的余弦成正比,如果我们用边的法线来计算,带正号的将位

多边形以矢量2i对象(二维整数坐标)列表的形式给出。如何测试给定点是否在内部?我在web上发现的所有实现都因一些微不足道的反例而失败。写一个正确的实现似乎真的很难。语言并不重要,因为我将自己移植它。

对于这个问题,光线投射或缠绕方法是最常见的。有关详细信息,请参阅


此外,请查看C.中有详细记录的解决方案。

如果它是凸的,检查它的一个简单方法是该点位于所有线段的同一侧(如果以相同的顺序遍历)

你可以很容易地用点积来检查(因为它与线段和点之间形成的角度的余弦成正比,如果我们用边的法线来计算,带正号的将位于右侧,带负号的将位于左侧)

以下是Python中的代码:

RIGHT=“RIGHT”
LEFT=“LEFT”
def内凸多边形(点、顶点):
前一方=无
n_顶点=len(顶点)
对于xrange中的n(n_顶点):
a、 b=顶点[n],顶点[(n+1)%n_顶点]
仿射_段=v_子(b,a)
仿射点=v_子(点,a)
当前_边=获取_边(仿射_段,仿射_点)
如果当前_侧为无:
返回False#在边缘外部或上方
如果前一方为无:#第一段
前一方=当前方
elif上一方!=电流侧:
返回错误
返回真值
def get_侧(a、b):
x=余弦符号(a,b)
如果x<0:
左转
elif x>0:
返回权
其他:
一无所获
def v_接头(a、b):
返回(a[0]-b[0],a[1]-b[1])
定义余弦符号(a,b):
返回a[0]*b[1]-a[1]*b[0]

或出自该书作者之手参见-

具体来说,他讨论了为什么缠绕规则通常比光线交叉更好


编辑-很抱歉,这不是写这本好书的人,是保罗·伯克,但他仍然是几何算法的一个非常好的来源。

我知道的是这样的

在多边形外的某个位置拾取一个点,该点可能远离几何体。 然后从这一点画一条线。我的意思是用这两点建立一个直线方程

然后,对于多边形中的每条线,检查它们是否相交

它们是相交线数的总和,给出它是否在内部

如果是奇怪的:内部


如果是偶数:外部

openCV中的PointPolyContest函数“确定点是在轮廓内部、外部还是位于边上”:
fortran的答案几乎对我有用,只是我发现我必须平移多边形,以便测试的点与原点相同。以下是我为实现这一目标而编写的JavaScript:

函数Vec2(x,y){
返回[x,y]
}
Vec2.nsub=函数(v1,v2){
返回向量2(v1[0]-v2[0],v1[1]-v2[1])
}
//又名“标量叉积”
Vec2.perpdot=函数(v1,v2){
返回v1[0]*v2[1]-v1[1]*v2[0]
}
//确定点是否位于多边形内。
//
//点-Vec2(2元素数组)。
//多顶点-向量2的数组(2元素数组)。构成
//沿多边形向上,按顺时针顺序围绕多边形。
//
函数坐标系(点,多顶点){
变量i,len,v1,v2,edge,x
//首先平移多边形,使“点”为原点。然后,对于每个点
//边,获取两个向量之间的角度:1)边向量和2)边向量
//边的第一个顶点的向量。如果所有角度都相同
//符号(是负数,因为它们是逆时针的)然后
//点位于多边形内部;否则,点位于外部。
对于(i=0,len=polyVerts.length;ilen-1?0:i+1],点)
edge=Vec2.nsub(v1,v2)
//注意,我们也可以通过使用普通+点积来实现这一点
x=Vec2.perpdot(边,v1)
//如果点直接位于边上,则将其作为多边形计算
如果(x<0){返回false}
}
返回真值
}

如果多边形是凸的,那么在C#中,以下实现了“”方法,最多在O(n个多边形点)处运行:

publicstaticbool是nConvexpolygon(点测试点,列表多边形)
{
//检查是否有三角形或更高的n边形
Assert(polygon.Length>=3);
//n> 2跟踪跨产品标志的更改
var-pos=0;
var-neg=0;
对于(var i=0;i0)pos++;
if(d<0)neg++;
//如果标志改变,则点在外侧
如果(位置>0&&neg>0)
返回false;
}
//如果方向没有变化,则在所有线段的同一侧,因此在内侧
返回true;
}

您必须检查要测试的点是否保持相对于凸多边形所有线段的方向。如果是的话,它在里面。要对每个线段执行此操作,请检查线段向量的行列式(如AB)和点的向量(如AP)是否保留其符号。如果行列式为零,则点位于线段上

要在C#代码中公开这一点


这是我在项目中使用的版本。它非常优雅简洁
public static bool IsInConvexPolygon(Point testPoint, List<Point> polygon)
{
    //Check if a triangle or higher n-gon
    Debug.Assert(polygon.Length >= 3);

    //n>2 Keep track of cross product sign changes
    var pos = 0;
    var neg = 0;

    for (var i = 0; i < polygon.Count; i++)
    {
        //If point is in the polygon
        if (polygon[i] == testPoint)
            return true;

        //Form a segment between the i'th point
        var x1 = polygon[i].X;
        var y1 = polygon[i].Y;

        //And the i+1'th, or if i is the last, with the first point
        var i2 = (i+1)%polygon.Count;

        var x2 = polygon[i2].X;
        var y2 = polygon[i2].Y;

        var x = testPoint.X;
        var y = testPoint.Y;

        //Compute the cross product
        var d = (x - x1)*(y2 - y1) - (y - y1)*(x2 - x1);

        if (d > 0) pos++;
        if (d < 0) neg++;

        //If the sign changes, then point is outside
        if (pos > 0 && neg > 0)
            return false;
    }

    //If no change in direction, then on same side of all segments, and thus inside
    return true;
}
  public bool IsPointInConvexPolygon(...)
  {
     Point pointToTest = new Point(...);
     Point pointA = new Point(...);
     ....

     var polygon = new List<Point> { pointA, pointB, pointC, pointD ... };
     double prevPosition = 0;
     // assuming polygon is convex.
     for (var i = 0; i < polygon.Count; i++)
     {
        var startPointSegment = polygon[i];
        // end point is first point if the start point is the last point in the list
        // (closing the polygon)
        var endPointSegment = polygon[i < polygon.Count - 1 ? i + 1 : 0];
        if (pointToTest.HasEqualCoordValues(startPointSegment) ||
            pointToTest.HasEqualCoordValues(endPointSegment))
          return true;

        var position = GetPositionRelativeToSegment(pointToTest, startPointSegment, endPointSegment);
        if (position == 0) // point position is zero so we are on the segment, we're on the polygon.
           return true;

        // after we checked the test point's position relative to the first segment, the position of the point 
        // relative to all other segments must be the same as the first position. If not it means the point 
        // is not inside the convex polygon.
        if (i > 0 && prevPosition != position)
           return false;

        prevPosition = position;
     }
     return true; 
  }
  public double GetPositionRelativeToSegment(Point pointToTest, Point segmentStart, Point segmentEnd)
  {
     return Math.Sign((pointToTest.X - segmentStart.X) * (segmentEnd.Y - segmentStart.Y) -
                (pointToTest.Y - segmentStart.Y) * (segmentEnd.X - segmentStart.X));
  }