web-dev-qa-db-ja.com

ポイントが2D整数座標の凸多角形の内側にあるかどうかをテストする方法は?

ポリゴンは、Vector2Iオブジェクト(2次元の整数座標)のリストとして指定されます。特定のポイントが内部にあるかどうかをどのようにテストできますか? Webで見つけたすべての実装は、いくつかの些細な反例で失敗します。正しい実装を書くのは本当に難しいようです。自分で移植するので、言語は関係ありません。

25
usr

凸状の場合、それをチェックする簡単な方法は、ポイントがすべてのセグメントの同じ側にあることです(同じ順序でトラバースする場合)。

これは外積で簡単に確認できます(セグメントとポイントの間に形成される角度の余弦に比例するため、正の符号を持つものは右側に、負の符号を持つものは左側に配置されます)。

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]
24
fortran

この問題では、レイキャスティングまたはワインディングの方法が最も一般的です。詳細は Wikipediaの記事 を参照してください。

また、Cで十分に文書化されたソリューションについては、 このページ を確認してください。

14
e.James

ポリゴンが凸型の場合、C#では、次は " 常に同じ側にあるかどうかをテスト "メソッドを実装し、最大でO(nのポリゴンポイント)で実行されます。

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;
}
8
Justas

あなたがテストしている点が原点と同じになるようにポリゴンを翻訳しなければならなかったのを除いて、fortranの答えはほとんどうまくいきました。これを機能させるために私が書いたJavaScriptは次のとおりです。

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
}
3
Elliot Winkler

OpenCVのpointPolygonTest関数は、ポイントが輪郭の内側か、外側か、エッジ上にあるかを決定します: http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html ?highlight = pointpolygontest#pointpolygontest

3
RawMean

私が知っている方法はそのようなものです。

ポリゴンの外側のどこかの点を選択すると、ジオメトリから遠く離れている可能性があります。次に、この点から線を引きます。つまり、これらの2つの点を使用して直線方程式を作成します。

次に、このポリゴンのすべてのラインについて、それらが交差しているかどうかを確認します。

それらは交差した線の数の合計があなたにそれが内部かそうでないかを与えます。

奇数の場合:中

それさえあれば:外

2
ufukgun

または、本を書いた人から参照してください- geometry page

具体的には このページ であり、曲がりの法則が一般に光線の交差よりも優れている理由について説明します。

編集-これは申し訳ありません Jospeh O'Rourke 優れた本を書いた人 Computational Geometry in C 、それはPaul Bourkeですが、ジオメトリアルゴリズムの非常に優れたソースです。

1
Martin Beckett