web-dev-qa-db-ja.com

ラインセグメントの頂点を含む一連のポイントからラインセグメントに最も近いポイントを見つけるための効率的な方法

ポイントのリストがあるとしましょう(私の場合、Python実装のポイントオブジェクト)。次に、これらのポイントの2つを結ぶ線分があります。

ラインセグメントに最も近いリストからポイントを効率的に見つける方法があるかどうかを知りたいです。すべてのポイントをステップ実行し、個別に距離を確認してから、最小距離を選択できることに気づきましたが、最終的にはプログラムをスケールアップして、おそらく数百万のポイントを処理することを望んでいます。それで、多分それよりも効率的なものでしょうか?

役立つ場合は、CCWの観点から、問題となっているすべてのポイントは、ラインセグメントの「左」/「下」または「右」/「上」のいずれかにある必要があります。私の実装では、セグメントの両側のポイントをチェックする必要はないと思います。

ポイントは、(x、y)座標を持つポイントオブジェクトと、この質問に直接関連しないその他のジャンクです。ラインセグメントは現在、2つのポイントオブジェクト(その頂点)への参照とその長さを含むオブジェクトとして実装されています。

先ほど触れたように、これはPython実装の一部です。ポイントのセットに対して凹型の船体を見つける方法を設計しようとしています(ポイントを決定する方法のいくつかの事前定義されたパラメーターを前提として)凸包上ではなく凹包上にあるかどうかです。最初に凸包を検索し、次に凸包の各線分に対して最も近い点を見つけ、その点で三角形を作成してから、三角形のクマ「内部ポイントが船体にあるように削除しています。

これを数学に入れることを検討しましたが、距離方程式は必要ありません。線分に最も近い点を見つけるための効率的なアルゴリズムが必要です。また、入力ポイントへの直線上の最も近いポイントを探していないことにも注意してください。セットから入力ラインセグメントまでの最も近いポイントを探しています。

皆さん、ありがとうございました!

5
acm_myk

すべてのポイントをループして距離を計算する必要があります。 StackOverflowで距離を計算する方法について素晴らしい質問があります: 点と線分間の最短距離

特定のラインセグメントに対してこれを複数回実行する必要がある場合、一部の作業は事前計算できます。また、最小距離を計算する必要はありません。最小距離squaredのみを計算する必要があります(平方が厳密に増加しているため)。私はその質問で上位投票された回答を(コンストラクターで)この事前計算を行うJavaバージョンに変換しました。残念ながら、私はあなたにPythonコードを与えるほど十分にPythonを知りませんが、あなたはそれを理解することができるはずです。

import Java.util.Arrays;
import Java.util.List;

public class LineSegment {
    public static class Point {
        public final double x;
        public final double y;

        public Point(double x, double y) {
            this.x = x;
            this.y = y;
        }

        public String toString() {
            return "(" + x + "," + y + ")";
        }
    }

    public static void main(String[] args) {
        LineSegment segment = new LineSegment(new Point(0, 3), new Point(2, 0));
        List<Point> pointList =
                Arrays.asList(new Point[] { new Point(-5, 3), new Point(1, 1),
                        new Point(2, 3), new Point(0, 5) });

        Point answer = segment.closestPoint(pointList);
        System.out.println("The closest point is: " + answer);
    }

    private static double sqr(double x) {
        return x * x;
    }

    private static double distanceSquared(Point v, Point w) {
        return sqr(v.x - w.x) + sqr(v.y - w.y);
    }

    private final Point firstSegPoint;
    private final Point secondSegPoint;
    private final double segmentDistance;
    private double xDifference;
    private double yDifference;

    public LineSegment(Point firstSegPoint, Point secondSegPoint) {
        this.firstSegPoint = firstSegPoint;
        this.secondSegPoint = secondSegPoint;
        this.segmentDistance = distanceSquared(firstSegPoint, secondSegPoint);
        this.xDifference = secondSegPoint.x - firstSegPoint.x;
        this.yDifference = secondSegPoint.y - firstSegPoint.y;
    }

    public Point closestPoint(List<Point> pointList) {
        double minDistance = Double.POSITIVE_INFINITY;
        Point answer = null;

        for (Point point : pointList) {
            double distSquared = distToSegmentSquared(point);
            if (distSquared < minDistance) {
                answer = point;
                minDistance = distSquared;
            }
        }

        return answer;
    }

    private double distToSegmentSquared(Point input) {
        if (segmentDistance == 0)
            return distanceSquared(input, firstSegPoint);

        double xComponent = (input.x - firstSegPoint.x) * xDifference;
        double yComponent = (input.y - firstSegPoint.y) * yDifference;
        double t = (xComponent + yComponent) / segmentDistance;
        if (closestPointIsFirst(t))
            return distanceSquared(input, firstSegPoint);
        if (closestPointIsSecond(t))
            return distanceSquared(input, secondSegPoint);
        Point closestPointOnLine =
                new Point(firstSegPoint.x + t * xDifference, firstSegPoint.y
                        + t * yDifference);
        return distanceSquared(input, closestPointOnLine);
    }

    private boolean closestPointIsFirst(double t) {
        return t < 0;
    }

    private boolean closestPointIsSecond(double t) {
        return t > 1;
    }
}

ここで完全な実装を参照してください: http://ideone.com/fBFwda

6
durron597

何が価値があるのか​​、静的な点のセットがラインセグメントをチェックする場合:

ボロノイ図( http://en.wikipedia.org/wiki/Voronoi_diagram )を使用した事前計算は、最近傍問題をポリゴンラインの交差クエリに減らすので役立ちます。

ボロノイ図は多角形で構成されています。比較しようとしている一連のポイントの各ポイントは、それ自体のポリゴン内にあります。ポイントが複数のポリゴン内に含まれておらず、そのポリゴン内に他のポイントが含まれていない。

1つのポリゴンのみを交差する場合は、答えがあります。複数のポリゴンを交差させる場合は、交差させた各ポリゴンをチェックする必要があります。

ボロノイダイアグラムの事前計算は、クエリの対象となるポイントの動的セットには役立ちません。

1
Peter Smith

なんて楽しい質問でしょう!私は個人的にグリッドが大好きです。私はそれらを愛し、私はそれらを愛し、私はそれらを愛する。理由もなく、すべてをグリッドに分割したい。

しかし、私がこれを試してみる方法は、グリッドから始まります。ポイントをグリッドセルに分割できます。各グリッドセルは32ビットインデックスにすることができます。各ポイントには、次のように、セル内の次のポイントを指すnextインデックスを含めることができます。

enter image description here

次に、次のように、Bresenhamを使用してラインをグリッドにラスタライズできます。

enter image description here

次に、セグメントに最も近い点を見つけるために、線でプロットされたセル内の点を探します。ポイントが見つからない場合は、隣接セルのすべてのポイントを展開して、幅優先の方法で探します。

これはおそらく最も最適なアルゴリズムではなく、最悪のシナリオは、単一のポイントを見つける前に多くのフェーズについてこの種の幅優先検索を実行する必要がある場合ですが、これは常に調整が非常に簡単であることがわかったタイプです。入力に基づいて、各グリッドセルの適切な密度/粗さ(小さいセルと大きいセルの比較)を試してください。ポイントをセルに分割した後の後処理ができる場合は、キャッシュフレンドリーパスを実行して、各セルの各ポイントが空間局所性のためにセルの次のポイントに隣接していることを確認できます。

大量のポイントがあることに加えて、検索対象のラインセグメントも多数あると想定しています。 3次元以外ではこのタイプのソリューションも使用しました。私の目標は、セグメントに最も近いポイントを見つけることではなく、どのポイントがどのセグメントに最も近いかを見つけることでした。私の目標は、次のように、骨格階層のボーンを表す3D線分セグメントを見つけることでした。

enter image description here

ボクセルのようなアプローチを使用して3D bresenhamラスタライザを使用する:

enter image description here

...次に、キャラクターのボーンウェイトを生成します。

enter image description here

...そしてアニメーションを開始します:

enter image description here

ストレステストでは、この単純な古いダムグリッドアプローチを使用するだけで、数百のボーン(3Dラインセグメント)に対して100万の頂点(ポイント)を持つメッシュのスキニングウェイトを約20msで生成できました。アーティストが骨をキャラクターに描き込んでアニメーションを開始したり、骨の位置を微調整したりするたびに、プログレスバーを待たずにそのようなパフォーマンスが必要でした。

競合する製品では、このプロセスに数分かかることがよくあることを私はしばしば発見しました。私は、彼らが普通の古いグリッドよりもはるかに洗練されたアルゴリズムを使用していることを望んでいます。私は実際には数学的にもアルゴリズム的にも非常に能力がありませんが、このようなダムソリューションを使用して私よりも100万倍スマートに同僚をしのぐことができました。中央分離アルゴリズム。 BVH、KD-tree、quad-trees、octreesなどのより洗練されたソリューションとは対照的に、メモリアクセスパターンなどの点で効率的であれば、このようなくだらないソリューションはニーズにぴったりです。もちろん、3次元グリッドを使用してレイトレーサーを作成することはお勧めしません。制限はありますが、単純な古いグリッドを使用するだけで、多くのことを競争力のある高速で実行できます。

これは、凸包とすべての2次元でどのような用途に使用できますか?私は今それを少し実装したいと思います、そしてそのような場合にグリッドがどれだけうまくいくかを見ます。

1
user204677