我正在尝试在多边形算法内部创建一个快速的 2D点,以用于命中测试(例如Polygon.contains(p:Point) )。 对于有效技术的建议将不胜感激。
#1楼
nirg的答案的C#版本在这里:我只分享代码。 这样可以节省一些时间。
public static bool IsPointInPolygon(IList<Point> polygon, Point testPoint) {
bool result = false;
int j = polygon.Count() - 1;
for (int i = 0; i < polygon.Count(); i++) {
if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y) {
if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X) {
result = !result;
}
}
j = i;
}
return result;
}
#2楼
nirg答案的Obj-C版本,带有用于测试点的样本方法。 Nirg的回答对我来说效果很好。
- (BOOL)isPointInPolygon:(NSArray *)vertices point:(CGPoint)test {
NSUInteger nvert = [vertices count];
NSInteger i, j, c = 0;
CGPoint verti, vertj;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
verti = [(NSValue *)[vertices objectAtIndex:i] CGPointValue];
vertj = [(NSValue *)[vertices objectAtIndex:j] CGPointValue];
if (( (verti.y > test.y) != (vertj.y > test.y) ) &&
( test.x < ( vertj.x - verti.x ) * ( test.y - verti.y ) / ( vertj.y - verti.y ) + verti.x) )
c = !c;
}
return (c ? YES : NO);
}
- (void)testPoint {
NSArray *polygonVertices = [NSArray arrayWithObjects:
[NSValue valueWithCGPoint:CGPointMake(13.5, 41.5)],
[NSValue valueWithCGPoint:CGPointMake(42.5, 56.5)],
[NSValue valueWithCGPoint:CGPointMake(39.5, 69.5)],
[NSValue valueWithCGPoint:CGPointMake(42.5, 84.5)],
[NSValue valueWithCGPoint:CGPointMake(13.5, 100.0)],
[NSValue valueWithCGPoint:CGPointMake(6.0, 70.5)],
nil
];
CGPoint tappedPoint = CGPointMake(23.0, 70.0);
if ([self isPointInPolygon:polygonVertices point:tappedPoint]) {
NSLog(@"YES");
} else {
NSLog(@"NO");
}
}
#3楼
没有什么比对问题的归纳定义更美了。 为了完整起见,您在序言中有一个版本,该版本可能还会阐明ray cast背后的观点:
基于http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html中的简单性算法仿真
一些辅助谓词:
exor(A,B):- \+A,B;A,\+B.
in_range(Coordinate,CA,CB) :- exor((CA>Coordinate),(CB>Coordinate)).
inside(false).
inside(_,[_|[]]).
inside(X:Y, [X1:Y1,X2:Y2|R]) :- in_range(Y,Y1,Y2), X > ( ((X2-X1)*(Y-Y1))/(Y2-Y1) + X1),toggle_ray, inside(X:Y, [X2:Y2|R]); inside(X:Y, [X2:Y2|R]).
get_line(_,_,[]).
get_line([XA:YA,XB:YB],[X1:Y1,X2:Y2|R]):- [XA:YA,XB:YB]=[X1:Y1,X2:Y2]; get_line([XA:YA,XB:YB],[X2:Y2|R]).
给定2个点A和B(Line(A,B))的直线方程为:
(YB-YA)
Y - YA = ------- * (X - XA)
(XB-YB)
重要的是,将直线的旋转方向设置为边界的顺时针方向,将孔设置为逆时针方向。 我们将检查点(X,Y),即测试点是否在直线的左半平面上(这是一种品味问题,它也可以是右侧,也可以是边界方向)在这种情况下,必须更改直线),这是将光线从该点投射到右侧(或左侧)并确认与直线的交点。 我们选择在水平方向上投射光线(同样,这是一个问题,也可以在垂直方向上进行类似的限制),因此我们具有:
(XB-XA)
X < ------- * (Y - YA) + XA
(YB-YA)
现在我们需要知道该点是否仅在线段的左侧(或右侧),而不是整个平面,因此我们只需要将搜索限制在该线段即可,但这很容易,因为它位于线段内直线中只有一个点可以在垂直轴上高于Y。 由于这是一个更严格的限制,因此需要首先进行检查,因此我们仅首先选择满足此要求的那些行,然后再检查其位置。 根据约旦曲线定理,任何投射到多边形的光线都必须以偶数条线相交。 这样就完成了,我们将射线向右投射,然后每次与直线相交时,切换其状态。 但是,在我们的实施中,我们要检查符合给定限制的一揽子解决方案的长度,并确定其内在条件。 对于多边形中的每条线,都必须这样做。
is_left_half_plane(_,[],[],_).
is_left_half_plane(X:Y,[XA:YA,XB:YB], [[X1:Y1,X2:Y2]|R], Test) :- [XA:YA, XB:YB] = [X1:Y1, X2:Y2], call(Test, X , (((XB - XA) * (Y - YA)) / (YB - YA) + XA));
is_left_half_plane(X:Y, [XA:YA, XB:YB], R, Test).
in_y_range_at_poly(Y,[XA:YA,XB:YB],Polygon) :- get_line([XA:YA,XB:YB],Polygon), in_range(Y,YA,YB).
all_in_range(Coordinate,Polygon,Lines) :- aggregate(bag(Line), in_y_range_at_poly(Coordinate,Line,Polygon), Lines).
traverses_ray(X:Y, Lines, Count) :- aggregate(bag(Line), is_left_half_plane(X:Y, Line, Lines, <), IntersectingLines), length(IntersectingLines, Count).
% This is the entry point predicate
inside_poly(X:Y,Polygon,Answer) :- all_in_range(Y,Polygon,Lines), traverses_ray(X:Y, Lines, Count), (1 is mod(Count,2)->Answer=inside;Answer=outside).
#4楼
这是nirg给出的答案的C#版本,该答案来自该RPI教授 。 请注意,使用该RPI来源中的代码需要归因。
边界复选框已添加到顶部。 但是,正如詹姆斯·布朗指出的那样,主代码几乎与边界框检查本身一样快,因此,如果您要检查的大多数点都在边界框内,边界框检查实际上会减慢整体操作的速度。 。 因此,您可以将边界框检出,或者另一种选择是,如果多边形的边界框不太频繁地改变形状,则可以对其进行预先计算。
public bool IsPointInPolygon( Point p, Point[] polygon )
{
double minX = polygon[ 0 ].X;
double maxX = polygon[ 0 ].X;
double minY = polygon[ 0 ].Y;
double maxY = polygon[ 0 ].Y;
for ( int i = 1 ; i < polygon.Length ; i++ )
{
Point q = polygon[ i ];
minX = Math.Min( q.X, minX );
maxX = Math.Max( q.X, maxX );
minY = Math.Min( q.Y, minY );
maxY = Math.Max( q.Y, maxY );
}
if ( p.X < minX || p.X > maxX || p.Y < minY || p.Y > maxY )
{
return false;
}
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
bool inside = false;
for ( int i = 0, j = polygon.Length - 1 ; i < polygon.Length ; j = i++ )
{
if ( ( polygon[ i ].Y > p.Y ) != ( polygon[ j ].Y > p.Y ) &&
p.X < ( polygon[ j ].X - polygon[ i ].X ) * ( p.Y - polygon[ i ].Y ) / ( polygon[ j ].Y - polygon[ i ].Y ) + polygon[ i ].X )
{
inside = !inside;
}
}
return inside;
}
#5楼
就像Nirg发布并由bobobobo编辑的解决方案一样。 我只是使它对javascript友好,并且使我更易读:
function insidePoly(poly, pointx, pointy) {
var i, j;
var inside = false;
for (i = 0, j = poly.length - 1; i < poly.length; j = i++) {
if(((poly[i].y > pointy) != (poly[j].y > pointy)) && (pointx < (poly[j].x-poly[i].x) * (pointy-poly[i].y) / (poly[j].y-poly[i].y) + poly[i].x) ) inside = !inside;
}
return inside;
}
来源:oschina
链接:https://my.oschina.net/u/3797416/blog/3160363