web-dev-qa-db-ja.com

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

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

alt text

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

私が探しているものの数学用語は、実際には内向き/外向きのポリゴンオフセットです。これを指摘するためにbalintに+1。代替命名法は、ポリゴンバッファリングです。

私の検索結果:

リンクは次のとおりです。

185
Igor Brejc

簡単に自分のポリゴンクリッピングとオフセットライブラリ- Clipper

Clipper は主にポリゴンクリッピング操作用に設計されていますが、ポリゴンオフセットも行います。ライブラリは、オープンソースのフリーウェアで記述されており、Delphi、C++およびC#で記述されています。非常に邪魔されない Boost ライセンスがあり、フリーウェアと商用アプリケーションの両方で無料で使用できます。

ポリゴンオフセットは、四角、丸、マイターの3つのオフセットスタイルのいずれかを使用して実行できます。

Polygon offsetting styles

129
Angus Johnson

探しているポリゴンは、計算ジオメトリで内向き/外向きオフセットポリゴンと呼ばれ、 ストレートスケルトン

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

そして、これは別のポリゴンのまっすぐなスケルトンです:

他のコメントでも指摘されているように、ポリゴンをどれだけ「膨張/収縮」させるかによって、出力の接続性が異なる場合があります。

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

パッケージマニュアル は、CGALを使用しない場合でも、これらの構造を構築する方法に関する適切な出発点を提供するものであり、数学的な定義とプロパティを持つ論文への参照が含まれています。

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

38
balint.miklos

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

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

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

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

9
Steve Jessop

これらのタイプの場合、通常 JTS を使用します。デモンストレーションのために、これを作成しました jsFiddleJSTS (JTSのJavaScriptポート)を使用します。必要な座標を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;
}

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

enter image description here

追加情報:私は通常、地図(リーフレットまたはGoogleマップを使用)。 (lat、lng)ペアをJSTS座標に変換するだけで、他はすべて同じです。例:

enter image description here

8
Marko Letic

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

すべての行を一定の距離だけ外側に移動します。

隣接線のすべてのペア(線分ではなく線分)を検討し、交差点を見つけます。これらは新しい頂点です。

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

(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のより一般的なケースです。

(d)ケース4

case3と同じですが、2つ消費します。

実際、ケース4を処理できる場合、他のすべてのケースは、ラインまたは頂点がオーバーラップする特殊なケースです。

ケース4を実行するには、頂点のスタックを保持します。後の行と重複する行が見つかったらプッシュし、後の行が取得されたらポップします。 -凸包で行うことと同じです。

5
J-16 SDiZ

ここに代替ソリューションがあります。これがより良いかどうかを確認してください。

  1. triangulation を実行します。それはdelaunayである必要はありません-どんな三角形分割でも実行できます。

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

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

5
J-16 SDiZ

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
stryeko

クリッパーライブラリの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;
    }
3
PainElemental

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

poly += 2; // buffer polygon by 2
2
Paul R

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

1
Carl Witthoft

使用できるライブラリがいくつかあります(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
stephanmg

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

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

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

  3. すべての角でこれを繰り返します

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

0
user2800464