2009-07-13 17 views
20

Đa giác được đưa ra dưới dạng danh sách đối tượng Vector2I (tọa độ 2 chiều, số nguyên). Làm thế nào tôi có thể kiểm tra nếu một điểm nhất định là bên trong? Tất cả các triển khai tôi tìm thấy trên web đều thất bại đối với một số ví dụ truy cập tầm thường. Nó thực sự có vẻ là khó để viết một thực hiện chính xác. Ngôn ngữ không quan trọng vì tôi sẽ tự mình chuyển nó.Làm thế nào để kiểm tra nếu một điểm nằm trong một đa giác lồi trong tọa độ số nguyên 2D?

+0

Nhận xét. Nếu đó là một vấn đề phỏng vấn, bạn sẽ nhận được một giải pháp O (log n) vì đa giác lồi là một trường hợp đặc biệt.Sử dụng tìm kiếm nhị phân cùng với ý tưởng được đưa ra trong câu trả lời của ufukgun. –

+3

Các câu trả lời ở đây đáng ngạc nhiên là xấu. [Bài viết này của Eric Haines] (http://erich.realtimerendering.com/ptinpoly/) mô tả nhiều phương pháp để làm điều này, và cũng cung cấp các tham chiếu đến các văn bản nổi tiếng. – bobobobo

+0

có thể trùng lặp của [Điểm trong Polygon hay còn gọi là thử nghiệm hit] (http://stackoverflow.com/questions/217578/point-in-polygon-aka-hit-test) – bobobobo

Trả lời

18

Nếu đó là lồi, một cách tầm thường để kiểm tra xem nó là điểm được đặt trên cùng một bên của tất cả các phân đoạn (nếu đi qua theo cùng thứ tự).

Bạn có thể kiểm tra dễ dàng với sản phẩm chéo (vì tỷ lệ thuận với cosin của góc được hình thành giữa phân khúc và điểm, những dấu có dấu dương nằm ở bên phải và dấu có dấu âm ở bên trái bên).

Đây là mã bằng Python:

RIGHT = "RIGHT" 
LEFT = "LEFT" 

def inside_convex_polygon(point, vertices): 
    previous_side = None 
    n_vertices = len(vertices) 
    for n in xrange(n_vertices): 
     a, b = vertices[n], vertices[(n+1)%n_vertices] 
     affine_segment = v_sub(b, a) 
     affine_point = v_sub(point, a) 
     current_side = get_side(affine_segment, affine_point) 
     if current_side is None: 
      return False #outside or over an edge 
     elif previous_side is None: #first segment 
      previous_side = current_side 
     elif previous_side != current_side: 
      return False 
    return True 

def get_side(a, b): 
    x = x_product(a, b) 
    if x < 0: 
     return LEFT 
    elif x > 0: 
     return RIGHT 
    else: 
     return None 

def v_sub(a, b): 
    return (a[0]-b[0], a[1]-b[1]) 

def x_product(a, b): 
    return a[0]*b[1]-a[1]*b[0] 
+7

Hacking một cái gì đó với nhau khi có những giải pháp nổi tiếng sẽ hầu như luôn luôn bỏ lỡ một số trường hợp cạnh. – Eric

+2

hãy thử và cho tôi biết nếu tôi sai – fortran

+0

Điều gì xảy ra với các điểm trên cạnh? Nói k = 0, nó sẽ cho một ZeroDivisionError. – stefano

12

Phương pháp đúc hoặc cuộn dây là phổ biến nhất cho vấn đề này. Xem chi tiết Wikipedia article.

Ngoài ra, Kiểm tra this page kiếm một giải pháp tốt tài liệu trong C.

+0

Đối với các tọa độ nguyên, đoạn mã của wrf sẽ quá đủ . – Eric

+1

Nó là phổ biến nhất ... nhưng nếu bạn đã biết đa giác là CONVEX, như trường hợp này, fortran được cho là nhanh hơn! –

1

Hoặc từ người đàn ông đó đã viết cuốn sách xem - geometry page

Cụ this page, ông thảo luận về lý do tại sao quanh co cai trị nói chung là tốt hơn so với tia qua.

chỉnh sửa - Xin lỗi, đây không phải là Jospeh O'Rourke người đã viết cuốn sách tuyệt vời Computational Geometry in C, đó là Paul Bourke nhưng vẫn là nguồn rất tốt về thuật toán hình học.

+0

Trang bạn trích dẫn sau đó tiếp tục liệt kê đoạn mã từ WRF. – Eric

1

cách tôi biết là điều gì đó tương tự.

bạn chọn một điểm nào đó bên ngoài đa giác có thể cách xa hình học. sau đó bạn vẽ một đường từ điểm này. tôi có nghĩa là bạn tạo một phương trình đường thẳng với hai điểm này.

sau đó cho mỗi dòng trong đa giác này, bạn kiểm tra xem chúng có giao nhau hay không.

chúng tổng số lượng các đường được giao cho bạn bên trong hoặc không.

nếu nó là số lẻ: bên

nếu nó thậm chí còn: bên ngoài

+0

Tôi vừa mới học: đó là thuật toán Ray cast nơi eJames đã nói về – ufukgun

+0

Tôi thấy lời giải thích của bạn khó theo dõi ... điểm khác của dòng là gì? – fortran

+0

Ray đúc nói chung là một giải pháp xấu, nó không đối phó tốt với các điểm gần một đỉnh, nơi các tia cast sẽ được gần một bên. Quy tắc uốn lượn mạnh mẽ hơn và nhanh hơn, đặc biệt cho các hình dạng lồi –

3

câu trả lời của câu trả lời gần như đã hiệu quả đối với tôi ngoại trừ tôi thấy tôi đã dịch đa giác sao cho điểm bạn đang thử nghiệm giống với điểm gốc. Đây là JavaScript mà tôi đã viết để làm cho công việc này:

function Vec2(x, y) { 
    return [x, y] 
} 
Vec2.nsub = function (v1, v2) { 
    return Vec2(v1[0]-v2[0], v1[1]-v2[1]) 
} 
// aka the "scalar cross product" 
Vec2.perpdot = function (v1, v2) { 
    return v1[0]*v2[1] - v1[1]*v2[0] 
} 

// Determine if a point is inside a polygon. 
// 
// point  - A Vec2 (2-element Array). 
// polyVerts - Array of Vec2's (2-element Arrays). The vertices that make 
//    up the polygon, in clockwise order around the polygon. 
// 
function coordsAreInside(point, polyVerts) { 
    var i, len, v1, v2, edge, x 
    // First translate the polygon so that `point` is the origin. Then, for each 
    // edge, get the angle between two vectors: 1) the edge vector and 2) the 
    // vector of the first vertex of the edge. If all of the angles are the same 
    // sign (which is negative since they will be counter-clockwise) then the 
    // point is inside the polygon; otherwise, the point is outside. 
    for (i = 0, len = polyVerts.length; i < len; i++) { 
    v1 = Vec2.nsub(polyVerts[i], point) 
    v2 = Vec2.nsub(polyVerts[i+1 > len-1 ? 0 : i+1], point) 
    edge = Vec2.nsub(v1, v2) 
    // Note that we could also do this by using the normal + dot product 
    x = Vec2.perpdot(edge, v1) 
    // If the point lies directly on an edge then count it as in the polygon 
    if (x < 0) { return false } 
    } 
    return true 
} 
4

Nếu đa giác là lồi, sau đó trong C#, sau đây thực hiện phương pháp "test if always on same side", và chạy nhiều nhất tại O (n điểm đa giá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 < polygon.Count - 1 ? i + 1 : 0; 

     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; 
} 
+1

Xin lỗi nếu điều này có vẻ hơi khó hiểu, nhưng bạn có thể chỉ thất bại (hoặc thậm chí khẳng định) nếu độ dài của danh sách nhỏ hơn 3. Đây là một thử nghiệm cho đa giác, không phải là kiểm tra để xem một điểm có bằng điểm khác hay không, hoặc một điểm là trên một dòng. Xử lý những trường hợp này là một cách tuyệt vời để có được những cơn đau đầu lớn sau này khi một điều gì đó mà bạn mong muốn đi một chiều là đi một cách khác mà không nói với bạn rằng bạn đã làm điều gì đó sai trái. Ngoài ra tên phương thức không ngụ ý rằng nó bao hàm các trường hợp đó rất tốt. – Gurgadurgen

+0

rất hữu ích! nếu điều đó giúp bất cứ ai, tôi đã sửa đổi và chuyển mã đó trong một câu trả lời khác: https://stackoverflow.com/a/48941131/516188 có thể ai đó tìm thấy phiên bản của tôi rõ ràng hơn. –