Algoritma untuk menggembungkan / mengempiskan (offsetting, buffering) poligon

202

Bagaimana saya "mengembang" poligon? Yaitu, saya ingin melakukan sesuatu yang serupa dengan ini:

teks alternatif

Syaratnya adalah bahwa tepi / titik poligon baru (meningkat) semuanya pada jarak konstan yang sama dari poligon lama (asli) (pada contoh gambar tidak, karena kemudian harus menggunakan busur untuk simpul yang digembungkan, tapi mari kita lupakan itu untuk saat ini;)).

Istilah matematika untuk apa yang saya cari sebenarnya mengimbangi ke dalam / luar poligon . +1 ke balint untuk menunjukkan ini. Penamaan alternatif adalah penyangga poligon .

Hasil pencarian saya:

Berikut ini beberapa tautan:

Igor Brejc
sumber
17
Ini sama sekali bukan pertanyaan sepele: jika deflasi / inflasi kecil, tidak ada yang serius terjadi, tetapi pada titik tertentu, simpul akan hilang. Mungkin ini telah dilakukan sebelumnya, jadi saya akan mengatakan: gunakan algoritma orang lain, jangan buat sendiri.
Martijn
1
Memang, jika poligon Anda cekung untuk memulai dengan (seperti dalam contoh di atas), Anda harus memutuskan apa yang harus terjadi pada titik di mana algoritma naif ingin membuat 'poligon' yang memotong sendiri ...
AakashM
Ya, masalah utama adalah bagian cekung poligon, di sinilah kompleksitasnya. Saya masih berpikir itu seharusnya tidak menjadi masalah untuk menghitung kapan titik tertentu harus dihilangkan. Pertanyaan utamanya adalah kompleksitas asimptotik seperti apa yang dibutuhkan.
Igor Brejc
Halo, ini juga masalah saya, kecuali saya harus melakukan ini dalam 3D. Apakah ada alternatif untuk pendekatan Straight Skeleton of Three-Dimensional Polyhedra yang dijelaskan dalam makalah arxiv.org/pdf/0805.0022.pdf ?
stephanmg

Jawaban:

138

Saya pikir saya mungkin secara singkat menyebutkan perpustakaan kliping dan mengimbangi poligon saya sendiri - Clipper .

Sementara Clipper terutama dirancang untuk operasi kliping poligon, Clipper juga mengimbangi. Perpustakaan adalah freeware open source yang ditulis dalam bahasa Delphi, C ++ dan C # . Ini memiliki lisensi Peningkatan yang sangat tidak terbebani yang memungkinkannya untuk digunakan dalam aplikasi freeware dan komersial tanpa biaya.

Offset poligon dapat dilakukan dengan menggunakan salah satu dari tiga gaya offset - kuadrat, bulat, dan disatukan.

Gaya offset poligon

Angus Johnson
sumber
2
Sangat keren! Di mana Anda 2 tahun lalu? :) Pada akhirnya saya harus mengimplementasikan logika pengimbang saya sendiri (dan kehilangan banyak waktu untuk itu). Algoritma apa yang Anda gunakan untuk offset poligon, BTW? Saya menggunakan api rumput. Apakah Anda menangani lubang di poligon?
Igor Brejc
2
2 tahun yang lalu saya sedang mencari solusi yang layak untuk kliping poligon yang tidak dibebani dengan masalah lisensi rumit :). Offsetting edge dicapai dengan menghasilkan unit normals untuk semua edge. Gabungan tepi dirapikan oleh clipper poligon saya karena orientasi persimpangan yang tumpang tindih ini berlawanan dengan orientasi poligon. Lubang-lubang yang paling pasti ditangani seperti halnya poligon berpotongan sendiri dll. Tidak ada batasan untuk jenis atau nomor mereka. Lihat juga "Mengimbangi Polygon dengan Menghitung Bilangan Berliku" di sini: me.berkeley.edu/ ~ mcmains
Angus Johnson
Wah! Jangan sejenak berpikir pertanyaan ini "dilupakan"! Saya melihat di sini minggu lalu - saya tidak berharap untuk kembali ke ini! Terima kasih banyak!
Chris Burt-Brown
Dokumen Clipper tentang penyangga poli ada di sini: angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/…
Drew Noakes
5
Bagi siapa pun yang ingin melakukan ini, alternatif lain adalah menggunakan GEOS, dan jika Anda menggunakan python, pembungkus GEOS, Shapely. Sebuah contoh yang sangat cantik: toblerity.github.com/shapely/manual.html#object.buffer
pelson
40

Poligon yang Anda cari disebut polygon offset ke dalam / luar dalam geometri komputasi dan terkait erat dengan kerangka lurus .

Ini adalah beberapa poligon offset untuk poligon yang rumit:

Dan ini adalah kerangka lurus untuk poligon lain:

Seperti yang ditunjukkan dalam komentar lain, juga, tergantung pada seberapa jauh Anda berencana untuk "mengembang / mengempis" poligon Anda, Anda dapat berakhir dengan konektivitas yang berbeda untuk output.

Dari sudut pandang komputasi: begitu Anda memiliki kerangka lurus, Anda harus dapat membuat poligon offset dengan relatif mudah. Sumber terbuka dan (gratis untuk non-komersial) perpustakaan CGAL memiliki paket yang menerapkan struktur ini. Lihat contoh kode ini untuk menghitung poligon offset menggunakan CGAL.

The pengguna paket harus memberikan yang baik sebuah titik awal tentang bagaimana membangun struktur ini bahkan jika Anda tidak akan menggunakan CGAL, dan berisi referensi ke kertas dengan definisi matematika dan sifat:

Manual CGAL: Kerangka Lurus 2D dan Pengimbangan Poligon

balint.miklos
sumber
12

Untuk hal-hal semacam ini saya biasanya menggunakan JTS . Untuk tujuan demonstrasi saya membuat jsFiddle ini yang menggunakan JSTS (JavaScript port of JTS). Anda hanya perlu mengonversi koordinat yang Anda miliki ke koordinat 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;
}

Hasilnya kira-kira seperti ini:

masukkan deskripsi gambar di sini

Info tambahan : Saya biasanya menggunakan inflating / deflating jenis ini (sedikit dimodifikasi untuk keperluan saya) untuk menetapkan batas dengan jari-jari pada poligon yang digambar di peta (dengan Leaflet atau Google maps). Anda hanya mengonversi pasangan (lat, lng) ke koordinat JSTS dan yang lainnya sama. Contoh:

masukkan deskripsi gambar di sini

Marko Letic
sumber
9

Kedengarannya seperti apa yang Anda inginkan:

  • Mulai dari titik, hadapi berlawanan arah jarum jam di sepanjang tepi yang berdekatan.
  • Ganti tepi dengan tepi paralel baru yang ditempatkan pada jarak dke "kiri" yang lama.
  • Ulangi untuk semua ujung.
  • Temukan persimpangan tepi baru untuk mendapatkan simpul baru.
  • Mendeteksi jika Anda telah menjadi poligon silang dan memutuskan apa yang harus dilakukan tentang hal itu. Mungkin menambahkan titik baru di titik persimpangan dan menyingkirkan beberapa yang lama. Saya tidak yakin apakah ada cara yang lebih baik untuk mendeteksi ini daripada hanya membandingkan setiap pasangan tepi yang tidak berdekatan untuk melihat apakah perpotongan mereka terletak di antara kedua pasang simpul.

Poligon yang dihasilkan terletak pada jarak yang diperlukan dari poligon lama "cukup jauh" dari simpul. Di dekat titik, himpunan titik pada jarak ddari poligon lama, seperti yang Anda katakan, bukan poligon, sehingga persyaratan yang dinyatakan tidak dapat dipenuhi.

Saya tidak tahu apakah algoritma ini memiliki nama, kode contoh di web, atau optimasi jahat, tapi saya pikir itu menjelaskan apa yang Anda inginkan.

Steve Jessop
sumber
5

Setiap garis harus membagi bidang menjadi "bagian dalam" dan "garis besar"; Anda dapat menemukan ini menggunakan metode produk dalam biasa.

Pindahkan semua garis keluar dengan jarak tertentu.

Pertimbangkan semua pasangan garis tetangga (garis, bukan segmen garis), temukan persimpangan. Ini adalah simpul baru.

Bersihkan verteks baru dengan menghapus bagian berpotongan. - Kami punya beberapa kasus di sini

(a) Kasus 1:

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

jika Anda mengeluarkannya satu per satu, Anda dapat:

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

7 dan 4 tumpang tindih .. jika Anda melihat ini, Anda menghapus titik ini dan semua titik di antaranya.

(B) kasus 2

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

jika Anda menghabiskannya dengan dua, Anda mendapatkan ini:

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

untuk menyelesaikan ini, untuk setiap segmen garis, Anda harus memeriksa apakah tumpang tindih dengan segmen yang terakhir.

(c) kasus 3

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

pengeluaran oleh 1. ini adalah kasus yang lebih umum untuk kasus 1.

(d) kasus 4

sama seperti case3, tetapi menghabiskan dua.

Sebenarnya, jika Anda dapat menangani case 4. Semua case lainnya hanya case khusus dengan beberapa garis atau vertex yang tumpang tindih.

Untuk melakukan kasus 4, Anda menyimpan setumpuk titik .. Anda mendorong ketika Anda menemukan garis tumpang tindih dengan baris terakhir, pop ketika Anda mendapatkan baris terakhir. - Persis seperti apa yang Anda lakukan di cembung.

J-16 SDiZ
sumber
apakah Anda tahu algoritma psedo untuk ini.
EmptyData
5

Ini adalah solusi alternatif, lihat apakah Anda lebih suka ini.

  1. Lakukan triangulasi , tidak harus delaunay - triangulasi apa pun akan berhasil.

  2. Mengembang setiap segitiga - ini harus sepele. jika Anda menyimpan segitiga dalam urutan anti-searah jarum jam, cukup pindahkan garis ke sisi kanan dan lakukan persimpangan.

  3. Gabungkan mereka menggunakan algoritma kliping Weiler-Atherton yang dimodifikasi

J-16 SDiZ
sumber
bagaimana Anda mengembang segitiga sebenarnya? Apakah output Anda bergantung pada triangulasi? Dengan pendekatan ini, bisakah Anda menangani kasing saat menyusutkan poligon?
balint.miklos
Apakah Anda yakin pendekatan ini benar-benar berfungsi untuk inflasi poligon? Apa yang terjadi ketika bagian cekung poligon dipompa sedemikian rupa sehingga beberapa simpul harus dihilangkan. Masalahnya adalah: ketika Anda melihat apa yang terjadi pada segitiga setelah poli. inflasi, segitiga tidak meningkat, melainkan terdistorsi.
Igor Brejc
1
Igor: Algoritme kliping Weiler-Atherton dapat menangani kasus "beberapa simpul harus dihilangkan" dengan benar;
J-16 SDiZ
@balint: mengembang segitiga adalah sepele: jika Anda menyimpan vertrex dalam urutan normal, sisi kanan selalu "keluar". Perlakukan saja segmen garis tersebut sebagai garis, gerakkan ke luar, dan temukan interaksinya - mereka adalah simpul baru. Untuk triangulasi itu sendiri, pada pemikiran kedua, triangulasi delaunay dapat memberikan hasil yang lebih baik.
J-16 SDiZ
4
Saya pikir pendekatan ini dapat dengan mudah memberikan hasil yang buruk. Bahkan untuk contoh sederhana seperti quad triangulasi menggunakan diagonal. Untuk dua segitiga yang diperbesar, Anda dapatkan: img200.imageshack.us/img200/2640/counterm.png dan penyatuan mereka tidak seperti yang Anda cari. Saya tidak melihat bagaimana metode ini berguna.
balint.miklos
3

Terima kasih banyak kepada Angus Johnson untuk perpustakaan guntingnya. Ada contoh kode yang baik untuk melakukan hal-hal kliping di beranda clipper di http://www.angusj.com/delphi/clipper.php#code tapi saya tidak melihat contoh untuk offset poligon. Jadi saya pikir mungkin itu berguna bagi seseorang jika saya memposting kode saya:

    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;
    }
PainElemental
sumber
2

Satu opsi lebih lanjut adalah menggunakan boost :: polygon - dokumentasi agak kurang, tetapi Anda harus menemukan metode resizedan bloat, dan juga +=operator kelebihan beban , yang sebenarnya menerapkan buffering. Jadi misalnya meningkatkan ukuran poligon (atau satu set poligon) dengan beberapa nilai dapat sesederhana:

poly += 2; // buffer polygon by 2
Paul R
sumber
Saya tidak mengerti bagaimana Anda seharusnya melakukan sesuatu dengan boost :: poligon karena hanya mendukung koordinat bilangan bulat? Katakanlah saya memiliki poligon umum (koordinat titik apung) dan saya ingin mengembangkannya - apa yang akan saya lakukan?
David Doria
@ Davidvidoria: tergantung pada resolusi / akurasi dan rentang dinamis apa yang Anda butuhkan untuk koordinat Anda, tetapi Anda bisa menggunakan int 32 bit atau 64 bit dengan faktor penskalaan yang sesuai. Kebetulan saya (secara tidak sengaja) menggunakan boost :: polygon dengan koordinat float di masa lalu dan tampaknya berfungsi dengan baik, tetapi mungkin tidak 100% kuat (dokumen memperingatkan hal itu!).
Paul R
Saya akan baik-baik saja dengan "itu akan berhasil sebagian besar waktu" :). Saya mencoba ini: ideone.com/XbZeBf tetapi tidak dikompilasi - ada pikiran?
David Doria
Saya tidak melihat sesuatu yang jelas salah, tetapi dalam kasus saya, saya menggunakan spesialisasi bujursangkar (polygon_90) jadi saya tidak tahu apakah itu membuat perbedaan. Sudah beberapa tahun sejak saya bermain-main dengan ini.
Paul R
OK - itu kembali kepada saya sekarang - Anda hanya dapat menggunakan +=dengan satu set poligon , bukan dengan masing-masing poligon. Cobalah dengan std :: vektor poligon. (Tentu saja vektor hanya perlu mengandung satu poligon).
Paul R
1

Berdasarkan saran dari @ JoshO'Brian, tampaknya rGeospaket dalam Rbahasa mengimplementasikan algoritma ini. Lihat rGeos::gBuffer.

Carl Witthoft
sumber
0

Saya menggunakan geometri sederhana: Vektor dan / atau Trigonometri

  1. Di setiap sudut temukan vektor tengah, dan sudut tengah. Vektor tengah adalah rata-rata aritmatika dari dua vektor satuan yang ditentukan oleh tepi sudut. Mid Angle adalah setengah dari sudut yang ditentukan oleh tepi.

  2. Jika Anda perlu memperluas (atau mengontrak) poligon Anda dengan jumlah d dari setiap sisi; Anda harus keluar (dalam) dengan jumlah d / sin (midAngle) untuk mendapatkan titik sudut baru.

  3. Ulangi ini untuk semua sudut

*** Berhati-hatilah dengan arahmu. Lakukan Uji CounterClockWise menggunakan tiga titik yang menentukan sudut; untuk mencari tahu jalan mana yang keluar, atau masuk

pengguna2800464
sumber