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