如何肯定2D点是否在多边形内?

我正在尝试在多边形算法内部建立一个快速的 2D点,以用于命中测试(例如Polygon.contains(p:Point) )。 对于有效技术的建议将不胜感激。 javascript


#1楼

nirg的答案的C#版本在这里:我只分享代码。 这样能够节省一些时间。 html

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的回答对我来讲效果很好。 java

- (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中的简单性算法仿真 app

一些辅助谓词: ide

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),即测试点是否在直线的左半平面上(这是一种品味问题,它也能够是右侧,也能够是边界方向)在这种状况下,必须更改直线),这是将光线从该点投射到右侧(或左侧)并确认与直线的交点。 咱们选择在水平方向上投射光线(一样,这是一个问题,也能够在垂直方向上进行相似的限制),所以咱们具备: spa

(XB-XA)
           X < ------- * (Y - YA) + XA
               (YB-YA)

如今咱们须要知道该点是否仅在线段的左侧(或右侧),而不是整个平面,所以咱们只须要将搜索限制在该线段便可,但这很容易,由于它位于线段内直线中只有一个点能够在垂直轴上高于Y。 因为这是一个更严格的限制,所以须要首先进行检查,所以咱们仅首先选择知足此要求的那些行,而后再检查其位置。 根据约旦曲线定理,任何投射到多边形的光线都必须以偶数条线相交。 这样就完成了,咱们将射线向右投射,而后每次与直线相交时,切换其状态。 可是,在咱们的实施中,咱们要检查符合给定限制的一揽子解决方案的长度,并肯定其内在条件。 对于多边形中的每条线,都必须这样作。 code

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来源中的代码须要归因。 htm

边界复选框已添加到顶部。 可是,正如詹姆斯·布朗指出的那样,主代码几乎与边界框检查自己同样快,所以,若是您要检查的大多数点都在边界框内,边界框检查实际上会减慢总体操做的速度。 。 所以,您能够将边界框检出,或者另外一种选择是,若是多边形的边界框不太频繁地改变形状,则能够对其进行预先计算。

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;
}
相关文章
相关标签/搜索