web-dev-qa-db-ja.com

ポリゴン交差の簡単なアルゴリズム

ポリゴンの交差/クリッピングを計算するための非常に単純なアルゴリズムを探しています。つまり、ポリゴンPQが与えられた場合、TおよびPに含まれるポリゴンQを見つけたい、 Tがすべての可能なポリゴンの中で最大になることを望みます。

実行時間は気にしません(非常に小さなポリゴンがいくつかあります)。また、ポリゴンの交差点(つまり、ポイントは少ないがポリゴンの交差点に含まれているポリゴン)の近似値を取得する余裕もあります。 )。

しかし、私にとっては、アルゴリズムがシンプル(テストが簡単)で、できれば短い(コードが少ない)ことは本当に重要です。

編集:注意してください、交差点を表す多角形を取得したいです。 2つのポリゴンが交差するかどうかの質問に対するブール値の答えだけは必要ありません。

62

元のポスターが簡単な解決策を探していたのは理解していますが、残念ながら簡単な解決策はありません。

それでも、私は最近、オープンソースのフリーウェアクリッピングライブラリ(Delphi、C++、C#で記述)を作成しました。これは、あらゆる種類のポリゴン(自己交差するものを含む)をクリップします。このライブラリの使用方法は非常に簡単です: http://sourceforge.net/projects/polyclipping/ .

51
Angus Johnson

Polygon Clippingアルゴリズムを使用して、2つのポリゴン間の交差を見つけることができます。ただし、これらはすべてのEdgeケースを考慮すると複雑なアルゴリズムになる傾向があります。

お気に入りの検索エンジンを使用して検索できるポリゴンクリッピングの実装の1つは、Weiler-Athertonです。 ウィラー・アサートンに関するウィキペディアの記事

Alan Murtaには、ポリゴンクリッパーの完全な実装があります [〜#〜] gpc [〜#〜]

編集:

別のアプローチは、最初に各ポリゴンを三角形のセットに分割することです。これにより、扱いやすくなります。 Gary H. MeistersによるTwo-Ears Theoremは、トリックを行います。この McGillのページ は、三角形の細分化をうまく説明しています。

17
Doug Ferguson

C++を使用していて、アルゴリズムを自分で作成したくない場合は、 Boost.Geometry を使用できます。上記のWeiler-Athertonアルゴリズムの適応バージョンを使用します。

12
Barend

ポリゴンの表現を提供していません。だから私はあなたのために(提案するような)を選択しています:)

各ポリゴンを1つの大きな凸ポリゴンとして表し、その大きな凸ポリゴンから「減算」する必要がある小さな凸ポリゴンのリストを表します。

その表現に2つのポリゴンがある場合、交差は次のように計算できます。

大きな凸多角形の交差を計算して、交差の大きな多角形を形成します。次に、両方の小さい方のすべての交点を「減算」して、サブラクトされたポリゴンのリストを取得します。

同じ表現に従って新しいポリゴンを取得します。

凸多角形の交差は簡単であるため、この交差の検出も簡単になります。

これはうまくいくように思えますが、正確性/時間/空間の複雑さに関して、これ以上深く考えたことはありません。

6
Aryabhatta

三角形分割に基づくアプローチは、実装が非常に簡単で、O(N2)。

ところで、O(N2)はこの問題に最適です。直角に交差する熊手のような形の2つのポリゴンを想像してください。それぞれには、タインの数に比例した多数のセグメントがあります。交点のポリゴンの数は、タインの数の2乗に比例します。

  1. まず、各ポリゴンを三角形分割します。

  2. Pのすべての三角形をQのすべての三角形とペアで比較して、交差を検出します。交差する三角形のペアは、それぞれがP、Q、または交差点にある小さな三角形に分割できます。 (手順1で使用したものはすべて、これを支援するために再利用できます。)交差点にある三角形のみを保持します。

  3. ペアごとに比較することにより、各三角形の近傍を計算し、隣接グラフを作成します。このグラフには、PとQの交点にある各ポリゴンに対して1つの接続されたサブグラフが含まれます。

  4. そのようなサブグラフごとに、三角形を選択し、エッジまで歩いてから、エッジの周りを歩いて、対応する出力ポリゴンを囲むセグメントを生成します。

5
Eric

これは単純で愚かなアプローチです。入力時に、ポリゴンをビットマップに離散化します。交差するには、ビットマップをANDで結合します。出力ポリゴンを生成するには、ビットマップのギザギザの境界線をトレースし、 polygon-approximation algorithm を使用してギザギザを滑らかにします。 (そのリンクが最適なアルゴリズムを提供するかどうかは覚えていませんが、これはGoogleの最初のヒットです。ビットマップイメージをベクトル表現に変換するツールの1つをチェックアウトできます。アルゴリズムを再実装せずに呼び出すことができます。 ?)

最も複雑な部分は、 境界線を追跡する と思います。

ちなみに、90年代前半、職場でこのような問題に直面していました。私はそれをマフしました:実数座標で動作する(完全に異なる)アルゴリズムを思いつきましたが、浮動小数点(およびノイズの多い入力)の現実に直面して、完全に修正不可能な縮退ケースが多すぎるように見えました。おそらくインターネットの助けを借りて、私はもっとうまくやっていただろう!

5
Darius Bacon

私が同じ問題について働いた方法

  1. ポリゴンをラインセグメントに分割する
  2. IntervalTreesまたはLineSweepAlgoを使用して交差する線を見つける
  3. GrahamScanAlgoを使用して閉じたパスを見つけ、隣接する頂点を持つ閉じたパスを見つけます
  4. クロスリファレンス3. DinicAlgoでそれらを解決する

注:ポリゴンの頂点が共通しているため、私のシナリオは異なっていました。しかし、これが役立つことを願っています

0
Ansh David

非常に簡単な解決策はありませんが、ここにrealアルゴリズムの主な手順があります。

  1. ポリゴンの頂点とエッジに対してカスタムダブルリンクリストを実行します。 std::listを使用しても、ノードでの特別な操作のために、次と前のポインター/オフセットを自分で交換する必要があるため、実行できません。これは単純なコードを作成する唯一の方法であり、これによりパフォーマンスが向上します。
  2. エッジの各ペアを比較して、交点を見つけます。 Edgeの各ペアを比較するとO(N²)時間になりますが、その後アルゴリズムをO(N・logN)に改善するのは簡単です。エッジのペア(たとえば、a→bおよびc→d)の場合、tₐ=d₀/(d₀-d₁)で与えられるEdge a→bのパラメーター(0から1)を使用して交差点を見つけます。 、ここでd₀は(ca)×(ba)であり、d₁は(da)×(ba)です。 ×は、p×q =pₓ・qᵧ-pᵧ・qₓなどの2D外積です。 tₐを見つけた後、交点を見つけることは、セグメントa→bの線形補間パラメーターとしてそれを使用しています:P = a +tₐ(b-a)
  3. セグメントが交差する頂点(およびリンクリスト内のノード)を追加して、各エッジを分割します。
  4. 次に、交差点のノードをcrossする必要があります。これは、カスタムの二重リンクリストを実行する必要がある操作です。 nextポインターのペアを交換する必要があります(そしてpreviousポインターを更新します)したがって)。

これで、ポリゴン交差解決アルゴリズムの未加工の結果が得られます。通常、各リージョンのワインディング番号に応じて、いくつかのリージョンを選択します。この説明については、ポリゴンのワインディング番号を検索してください。

このO(N²)アルゴリズムからO(N・logN)アルゴリズムを作成する場合は、ラインスイープアルゴリズムの内部で行うことを除いて、まったく同じことを行う必要があります。 Bentley Ottmanアルゴリズムを探します。内側のアルゴリズムは同じですが、ループ内で比較するエッジの数が減るという唯一の違いがあります。

0
Dom