ポリゴンの膨張/収縮(オフセット、バッファリング)のアルゴリズム


202

ポリゴンを「膨らませる」にはどうすればよいですか?つまり、私はこれに似た何かをしたいです:

代替テキスト

要件は、新しい(インフレートされた)ポリゴンのエッジ/ポイントが古い(元の)ポリゴンからすべて同じ一定の距離にあることです(例の画像ではそうではありません。それから、膨らんだ頂点にアークを使用する必要があるためですが、今はそのことを忘れてください;))。

私が探している数学的用語は、実際には内側/外側のポリゴンオフセットです。これを指摘するためのbalintへの+1。別の名前は、ポリゴンバッファリングです。

私の検索の結果:

ここにいくつかのリンクがあります:


17
これは決して些細な問題ではありません。デフレ/インフレが小さい場合、深刻なことは何も起こりませんが、ある時点で頂点が消えます。おそらくこれは以前に行われたため、他の誰かのアルゴリズムを使用し、独自のアルゴリズムを構築しないでください。
Martijn、

1
実際、ポリゴンが最初に凹型である場合(上記の例のように)、ナイーブアルゴリズムが自己交差する「ポリゴン」を作成したいポイントで何が起こるかを決定する必要があります...
AakashM

はい、主な問題はポリゴンの凹面部分です。これが複雑さの原因です。特定の頂点を削除する必要がある場合に計算することは、そのような問題ではないはずです。主な問題は、これに必要な漸近的な複雑さの種類です。
Igor Brejc、2009

こんにちは、これも私の問題ですが、3Dで行う必要があります。文書arxiv.org/pdf/0805.0022.pdfに記載されている3次元多面体の直線骨格アプローチに代わるものはありますか?
stephanmg 2018

回答:


138

私は簡単に自分の言及かもしれないと思ったポリゴンクリッピングと相殺ライブラリ - クリッパーを

ながらクリッパーは、主にポリゴンクリッピング操作のために設計されて、それがあまりにも多角形相殺ありません。ライブラリは、Delphi、C ++、C#で記述されたオープンソースのフリーウェアです。非常に邪魔されないBoostライセンスがあり、無料でフリーウェアと商用アプリケーションの両方で使用できます。

ポリゴンのオフセットは、正方形、円形、留め継ぎの3つのオフセットスタイルのいずれかを使用して実行できます。

ポリゴンオフセットスタイル


2
とてもかっこいい!2年前どこにいたの?:)結局、私は自分の相殺ロジックを実装しなければなりませんでした(そしてそれで多くの時間を失いました)。ところで、ポリゴンオフセットにはどのアルゴリズムを使用していますか?草火を使いました。ポリゴンの穴を処理しますか?
Igor Brejc

2
2年前、私は、トリッキーなライセンスの問題に悩まされることのない、ポリゴンクリッピングに対する適切なソリューションを探していました。エッジのオフセットは、すべてのエッジの単位法線を生成することによって実現されます。これらの重なり合った交差の向きはポリゴンの向きと反対であるため、エッジ結合はポリゴンクリッパーによって整頓されています。穴は、自己交差するポリゴンなどと同様に確実に処理されます。タイプや数に制限はありません。こちらの「巻数を
Angus Johnson

うわあ!この質問は「忘れられた」と思わないでください!私は先週ここを見て、これに戻るとは思っていませんでした。本当にありがとう!
Chris Burt-Brown、

ポリバッファリング上のクリッパーのドキュメントはここにある:angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/...
ドリューNoakes

5
これを実行したい人のために、別の方法はGEOSを使用することです。Pythonを使用している場合は、GEOSのラッパーであるShapelyを使用します。本当にかわいい例:toblerity.github.com/shapely/manual.html#object.buffer
pelson

40

探しているポリゴンは、計算ジオメトリでは内向き/外向きオフセットポリゴンと呼ばれ、直線スケルトンと密接に関連しています。

これらは、複雑なポリゴンのいくつかのオフセットポリゴンです。

そして、これは別のポリゴンのストレートスケルトンです。

他のコメントでも指摘されているように、ポリゴンの「膨張/収縮」をどこまで計画するかに応じて、出力の接続が異なる可能性があります。

計算の観点から:ストレートスケルトンが得られたら、オフセットポリゴンを比較的簡単に構築できるはずです。オープンソースおよび(非商用の場合は無料で)CGALライブラリには、これらの構造を実装するパッケージがあります。CGALを使用してオフセットポリゴンを計算するには、このコード例を参照してください。

パッケージのマニュアルは、あなたがCGALを使用しない場合でも、これらの構造を構築する方法についての出発点に良いを与え、数学的な定義や性質を持つ論文への参照が含まれている必要があります。

CGALマニュアル:2Dストレートスケルトンとポリゴンオフセット


12

これらのタイプの場合、私は通常JTSを使用します。デモのために、JSTS(JTSのJavaScriptポート)を使用するこのjsFiddleを作成しました。必要な座標をJSTS座標に変換するだけです。

function vectorCoordinates2JTS (polygon) {
  var coordinates = [];
  for (var i = 0; i < polygon.length; i++) {
    coordinates.push(new jsts.geom.Coordinate(polygon[i].x, polygon[i].y));
  }
  return coordinates;
}

結果は次のようになります。

ここに画像の説明を入力してください

追加情報:通常、このタイプのインフレート/デフレート(目的に合わせて少し変更)を使用して、マップに描かれたポリゴン(リーフレットまたはGoogleマップ)に半径で境界を設定します。(lat、lng)のペアをJSTS座標に変換するだけで、その他はすべて同じです。例:

ここに画像の説明を入力してください


9

あなたが望むもののように私に聞こえます:

  • 頂点から始めて、隣接するエッジに沿って反時計回りに向けます。
  • エッジをd、古いエッジの「左」から離れた距離に配置された新しい平行エッジに置き換えます。
  • すべてのエッジについて繰り返します。
  • 新しいエッジの交点を見つけて、新しい頂点を取得します。
  • あなたが交差したポリゴンになったかどうかを検出し、それについて何をすべきかを決定します。おそらく交差点に新しい頂点を追加し、いくつかの古いものを取り除きます。これを検出するために、隣接していないエッジのすべてのペアを比較して、それらの交点が両方の頂点のペアの間にあるかどうかを確認するよりも良い方法があるかどうかはわかりません。

結果のポリゴンは、頂点から「十分に遠い」古いポリゴンから必要な距離にあります。頂点の近くではd、古いポリゴンから離れたポイントのセットは、言うまでもなくポリゴンではないため、前述の要件を満たすことができません。

このアルゴリズムに名前が付いているのか、Web上のコードの例なのか、それとも悪意のある最適化なのかはわかりませんが、あなたが望んでいることを説明していると思います。


6

GISの世界では、このタスクに負のバッファリングを使用します:http : //www-users.cs.umn.edu/~npramod/enc_pdf.pdf

JTSライブラリは、あなたのためにこれを行う必要があります。バッファ操作のドキュメントを参照してください:http : //tsusiatsoftware.net/jts/javadoc/com/vividsolutions/jts/operation/buffer/package-summary.html

大まかな概要については、開発者ガイドも参照してください。http//www.vividsolutions.com/jts/bin/JTS%20Developer%20Guide.pdf


5

各線は、平面を「内側」と「輪郭」に分割する必要があります。通常の内積法を使用してこれを見つけることができます。

すべての線を少し距離だけ外側に移動します。

隣接するラインのすべてのペア(ラインセグメントではなくライン)を考慮して、交点を見つけます。これらは新しい頂点です。

交差している部分を削除して、新しい頂点をクリーンアップします。-ここにはいくつかのケースがあります

(a)ケース1:

 0--7  4--3
 |  |  |  |
 |  6--5  |
 |        |
 1--------2

あなたがそれを1つ消費すると、あなたはこれを得ました:

0----a----3
|    |    |
|    |    |
|    b    |
|         |
|         |
1---------2

7と4が重なります。これが表示された場合は、このポイントとその間のすべてのポイントを削除します。

(b)ケース2

 0--7  4--3
 |  |  |  |
 |  6--5  |
 |        |
 1--------2

あなたがそれを2つ消費すると、あなたはこれを得ました:

0----47----3
|    ||    |
|    ||    |
|    ||    |
|    56    |
|          |
|          |
|          |
1----------2

これを解決するには、ラインの各セグメントについて、それが後のセグメントとオーバーラップするかどうかを確認する必要があります。

(c)ケース3

       4--3
 0--X9 |  |
 |  78 |  |
 |  6--5  |
 |        |
 1--------2

1で消費します。これは、ケース1のより一般的なケースです。

(d)ケース4

case3と同じですが、2を使います。

実際、ケース4を処理できる場合。他のすべてのケースは、線または頂点が重なっている特殊なケースです。

ケース4を行うには、頂点のスタックを保持します。後者のラインと重複するラインを見つけたらプッシュし、後者のラインを取得したらポップします。-凸包で行うのと同じです。


このための疑似アルゴリズムを知っていますか?
EmptyData 2018

5

ここに代替の解決策があります。これがもっと好きかどうか見てください。

  1. やる三角測量を、それがドロネーである必要はありません-任意の三角測量を行うだろう。

  2. 各三角形を膨らませます-これは簡単なはずです。三角形を反時計回りに保存する場合は、線を右側に移動して交差させます。

  3. 変更されたWeiler-Athertonクリッピングアルゴリズムを使用してそれらをマージする


三角形をどのように正確に膨らませますか?出力は三角形分割に依存しますか?このアプローチで、ポリゴンを縮小する場合に対処できますか?
balint.miklos 2009

このアプローチがポリゴンインフレーションで本当に機能することを確信していますか?ポリゴンの凹面部分が膨張して、一部の頂点を削除する必要がある場合はどうなりますか。問題は、ポリゴンの後に三角形に何が起こるかを見るときです。膨張すると、三角形は膨張せず、変形します。
Igor Brejc 2009

1
Igor:Weiler-Athertonクリッピングアルゴリズムは、「一部の頂点を削除する必要がある」ケースを正しく処理できます。
J-16 SDiZ 2009

@balint:三角形を膨らませるのは簡単です。バーテックスを通常の順序で保存すると、右側は常に「外側」になります。これらのラインセグメントをラインとして扱い、外側に移動して、相互作用を見つけます。これらは新しい頂点です。三角形分割自体については、考え直してみると、delaunay三角形分割の方が良い結果が得られる可能性があります。
J-16 SDiZ 2009

4
このアプローチは簡単に悪い結果をもたらすと思います。対角線を使用して四角形に三角形分割された単純な例でもです。2つの拡大された三角形については、img200.imageshack.us / img200 / 2640 / counterm.pngが得られ、それらの和集合は、探しているものとは異なります。この方法がどのように役立つかわかりません。
balint.miklos 2009

3

Angus Johnson氏のクリッパーライブラリに感謝します。http://www.angusj.com/delphi/clipper.php#codeのクリッパーホームページにクリッピングを行うための適切なコードサンプルがあります が、ポリゴンオフセットの例はありませんでした。だから私は私のコードを投稿すれば誰かのために役立つかもしれないと思いました:

    public static List<Point> GetOffsetPolygon(List<Point> originalPath, double offset)
    {
        List<Point> resultOffsetPath = new List<Point>();

        List<ClipperLib.IntPoint> polygon = new List<ClipperLib.IntPoint>();
        foreach (var point in originalPath)
        {
            polygon.Add(new ClipperLib.IntPoint(point.X, point.Y));
        }

        ClipperLib.ClipperOffset co = new ClipperLib.ClipperOffset();
        co.AddPath(polygon, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);

        List<List<ClipperLib.IntPoint>> solution = new List<List<ClipperLib.IntPoint>>();
        co.Execute(ref solution, offset);

        foreach (var offsetPath in solution)
        {
            foreach (var offsetPathPoint in offsetPath)
            {
                resultOffsetPath.Add(new Point(Convert.ToInt32(offsetPathPoint.X), Convert.ToInt32(offsetPathPoint.Y)));
            }
        }

        return resultOffsetPath;
    }

2

もう1つのオプションは、boost :: polygonを使用することです。ドキュメントにはいくらか欠けていますが、メソッドresizeandとbloat+=実際にバッファリングを実装するオーバーロードされた演算子も見つかるはずです。したがって、たとえば、ポリゴン(またはポリゴンのセット)のサイズをある値だけ増やすことは、次のように簡単です。

poly += 2; // buffer polygon by 2

整数座標しかサポートしていないので、boost :: polygonで何をすることになっているのか理解できません。一般的な(浮動小数点座標)ポリゴンがあり、それを拡張したいとします-どうしますか?
David Doria

@DavidDoria:座標に必要な解像度/精度とダイナミックレンジによって異なりますが、適切なスケーリングファクターで32ビットまたは64ビットの整数を使用できます。ちなみに私は過去に(偶然に)float座標のboost :: polygonを使用しており、正常に動作するようですが、100%堅牢ではない可能性があります(ドキュメントで警告されています!)。
Paul R

私は「ほとんどの場合それはうまくいく」と大丈夫でしょう:)。私はこれを試しました:ideone.com/XbZeBfがコンパイルされません-何か考えはありますか?
David Doria

明らかな問題はありませんが、私の場合は、直線的な特殊化(polygon_90)を使用していたため、違いがあるかどうかはわかりません。私がこれをいじってから数年になります。
Paul R

OK-戻ってきます- 個々のポリゴンではなく+=、ポリゴンセットでのみ使用できます。ポリゴンのstd :: vectorで試してください。(もちろん、ベクトルに必要なポリゴンは1つだけです)。
Paul R

1

@ JoshO'Brianからのアドバイスに基づいてrGeosR言語のパッケージがこのアルゴリズムを実装しているようです。を参照してくださいrGeos::gBuffer


0

いくつかのライブラリが使用できます(3Dデータセットにも使用できます)。

  1. https://github.com/otherlab/openmesh
  2. https://github.com/alecjacobson/nested_cages
  3. http://homepage.tudelft.nl/h05k3/Projects/MeshThickeningProj.htm

これらのライブラリに対応する出版物を見つけて、アルゴリズムをより詳しく理解することもできます。

最後のものは依存関係が最も少なく、自己完結型であり、.objファイルを読み取ることができます。

ステファン


0

私は単純なジオメトリを使用します:ベクトルおよび/または三角法

  1. 各コーナーで中央ベクトルと中央角度を見つけます。中央ベクトルは、コーナーのエッジによって定義される2つの単位ベクトルの算術平均です。中間角度は、エッジによって定義される角度の半分です。

  2. 各エッジからdの量だけポリゴンを拡張(または縮小)する必要がある場合。新しいコーナーポイントを取得するには、d / sin(midAngle)だけ(in)出ます。

  3. すべてのコーナーでこれを繰り返します

***方向に注意してください。コーナーを定義する3つのポイントを使用してCounterClockWiseテストを作成します。どちらの方法が出ているのか、出ているのかを調べる

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.