如何肯定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;
}
相關文章
相關標籤/搜索