Çokgenleri şişirmek / söndürmek için bir algoritma (ofsetleme, tamponlama)


203

Bir çokgeni nasıl "şişiririm"? Yani, buna benzer bir şey yapmak istiyorum:

alternatif metin

Gereksinim, yeni (şişirilmiş) çokgenin kenarlarının / noktalarının eski (orijinal) çokgeninle aynı sabit mesafede olması (örnek resimde değiller, çünkü o zaman şişirilmiş köşeler için yaylar kullanması gerekir, ancak Şimdilik unutun;)).

Aradığım şeyin matematiksel terimi aslında içe / dışa çokgen dengelemesidir . Bunu işaret ettiği için +1. Alternatif adlandırma çokgen arabelleğe almadır .

Aramamın sonuçları:

İşte bazı bağlantılar:


17
Bu hiç de önemsiz bir soru değil: deflasyon / enflasyon küçükse, ciddi bir şey olmaz, ancak bir noktada köşeler kaybolur. Muhtemelen bu daha önce yapılmıştı, bu yüzden şunu söyleyebilirim: başkasının algoritmasını kullanın, kendinizinkini oluşturma.
Martijn

1
Gerçekten de, eğer çokgeniniz başlamak için içbükeyse (yukarıdaki örnekte olduğu gibi), saf algoritmanın kendiliğinden kesişen bir 'çokgen' yapmak istediği noktada ne olacağına karar
vermelisiniz

Evet, asıl sorun çokgenin içbükey kısımlarıdır, burası karmaşıklığın yattığı yerdir. Hala belirli bir tepe noktasının ortadan kaldırılması gerektiğinde hesaplamanın böyle bir sorun olmaması gerektiğini düşünüyorum. Ana soru bunun ne tür bir asimptotik karmaşıklık gerektireceğidir.
Igor Brejc

Merhaba, bu da benim sorunum, ancak bunu 3D olarak yapmam gerekiyor. Arxiv.org/pdf/0805.0022.pdf makalesinde açıklanan Üç Boyutlu Çokyüzlü Düz Iskelet yaklaşımına bir alternatif var mı ?
stephanmg

Yanıtlar:


139

Kendi çokgen kırpma ve ofset kütüphanemden kısaca bahsedebileceğimi düşündüm - Clipper .

Clipper öncelikle çokgen kırpma işlemleri için tasarlanmış olsa da, çokgen ofseti de yapar. Kütüphane Delphi, C ++ ve C # ile yazılmış açık kaynaklı ücretsiz bir yazılımdır . Hem ücretsiz hem de ticari uygulamalarda ücretsiz olarak kullanılmasına izin veren çok numarasız bir Boost lisansına sahiptir .

Çokgen ofseti, kare, yuvarlak ve hafif olmak üzere üç ofset stilinden biri kullanılarak gerçekleştirilebilir.

Çokgen ofsetleme stilleri


2
Çok havalı! 2 yıl önce neredeydin? :) Sonunda kendi dengeleme mantığımı uygulamak zorunda kaldım (ve çok fazla zaman kaybettim). Poligon dengelemesi için hangi algoritmayı kullanıyorsunuz, BTW? Çim ateşi kullandım. Çokgenlerde deliklerle baş ediyor musunuz?
Igor Brejc

2
2 yıl önce çokgen lisans sorunlarına yol açmayan çokgen kırpmaya iyi bir çözüm arıyordum :). Kenar ofseti, tüm kenarlar için birim normaller oluşturularak elde edilir. Kenar birleşimleri çokgen kesicim tarafından toplanır, çünkü bu üst üste binen kavşakların yönelimleri çokgenlerin yöneliminin tersidir. Delikler kesinlikle kesişen çokgenler gibi ele alınır. Türleri veya sayıları için herhangi bir kısıtlama yoktur. Ayrıca bkz. "Sargı Numaralarını Hesaplayarak
Angus Johnson

Oha! Bir an için bu sorunun "unutulmuş" olduğunu düşünmeyin! Geçen hafta buraya baktım - buna geri gelmesini beklemiyordum! Çok teşekkürler!
Chris Burt-Brown


5
Bunu yapmak isteyen herkes için başka bir alternatif GEOS'u kullanmaktır ve eğer python kullanıyorsanız, GEOS'un sarıcısı Shapely'dir. Gerçekten güzel bir örnek: toblerity.github.com/shapely/manual.html#object.buffer
pelson

40

Aradığınız çokgene hesaplama geometrisinde içe / dışa ofset çokgen denir ve düz iskeletle yakından ilişkilidir .

Bunlar karmaşık bir çokgen için birkaç ofset çokgenlerdir:

Ve bu başka bir çokgenin düz iskeleti:

Diğer yorumlarda da belirtildiği gibi, çokgeninizi ne kadar "şişirmeyi / söndürmeyi" planladığınıza bağlı olarak, çıktı için farklı bağlantılarla sonuçlanabilir.

Hesaplama açısından: düz iskelete sahip olduğunuzda, ofset çokgenleri nispeten kolay bir şekilde inşa edebilmelidir. Açık kaynak ve (ticari olmayanlar için ücretsiz) CGAL kütüphanesi bu yapıları uygulayan bir pakete sahiptir. CGAL kullanarak ofset çokgenlerini hesaplamak için bu kod örneğine bakın .

Paket manuel Eğer CGAL kullanacağız olmasa bile bu yapıları inşa etmek konusunda başlangıç noktası size iyi vermek ve matematiksel tanımları ve özellikleri olan kağıtlar referanslar içeriyor olmalıdır:

CGAL kılavuzu: 2D Düz İskelet ve Çokgen Dengeleme


12

Bu tür şeyler için genellikle JTS kullanıyorum . Gösteri amacıyla JSTS ( JTS'nin JavaScript bağlantı noktası) kullanan bu jsFiddle'ı oluşturdum . Sadece sahip olduğunuz koordinatları JSTS koordinatlarına dönüştürmeniz gerekir:

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;
}

Sonuç şuna benzer:

resim açıklamasını buraya girin

Ek bilgi : Genellikle bu tür bir şişirme / söndürme türünü (benim amacım için biraz değiştirilmiş), bir haritaya çizilen çokgenler üzerinde yarıçaplı sınırlar ayarlamak için kullanıyorum (Leaflet veya Google haritaları ile). Sadece (lat, lng) çiftlerini JSTS koordinatlarına dönüştürürsünüz ve diğer her şey aynıdır. Misal:

resim açıklamasını buraya girin


9

Bana istediğin gibi geliyor:

  • Bir tepe noktasından başlayarak, bitişik bir kenar boyunca saat yönünün tersine bakın.
  • Kenarı d, eskisinin "sol " una mesafeye yerleştirilmiş yeni, paralel bir kenarla değiştirin .
  • Tüm kenarlar için tekrarlayın.
  • Yeni köşeleri elde etmek için yeni kenarların kesişimlerini bulun.
  • Çapraz çokgen olup olmadığınızı tespit edin ve bu konuda ne yapacağınıza karar verin. Muhtemelen geçiş noktasına yeni bir tepe noktası ekleyin ve bazı eski noktalardan kurtulun. Bunu saptamanın her iki köşe kenarı arasında kesişip kesişmediğini görmek için her bitişik olmayan kenar çiftini karşılaştırmaktan daha iyi bir yol olup olmadığından emin değilim.

Ortaya çıkan çokgen, köşelerden "yeterince uzakta" olan eski çokgenden gerekli mesafede bulunur. Bir tepe noktasının dyakınında, eski çokgene uzaktaki nokta kümesi, dediğiniz gibi, bir çokgen değildir, bu nedenle belirtilen gereksinim karşılanamaz.

Bu algoritmanın bir adı, web'de örnek kod veya şeytani bir optimizasyon olup olmadığını bilmiyorum, ancak ne istediğinizi açıkladığını düşünüyorum.



5

Her çizgi düzlemi "iç" ve "dış hatlara" ayırmalıdır; bunu her zamanki iç ürün yöntemini kullanarak bulabilirsiniz.

Tüm çizgileri bir miktar dışarıya doğru hareket ettirin.

Tüm komşu çizgileri (çizgi segmenti değil, çizgiler) düşünün, kavşağı bulun. Bunlar yeni tepe noktası.

Kesişen parçaları çıkararak yeni tepe noktasını temizleyin. - burada birkaç vakamız var

(a) Durum 1:

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

Eğer bunu tek tek harcarsanız, bunu elde edersiniz:

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

7 ve 4 çakışır .. Bunu görürseniz, bu noktayı ve aradaki tüm noktaları kaldırırsınız.

(b) durum 2

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

Eğer ikiye harcarsanız, bunu elde edersiniz:

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

bunu çözmek için, satırın her bir segmenti için, daha sonraki segmentlerle çakışıp çakışmadığını kontrol etmeniz gerekir.

(c) durum 3

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

1. durum için daha genel bir durumdur.

(d) 4. dava

case3 ile aynı, ancak iki harcama.

Aslında, durum 4 ile başa çıkabiliyorsanız, diğer tüm durumlar sadece bir çizgi veya tepe noktası çakışmasıyla özel bir durumdur.

Durum 4'ü yapmak için, bir yığın tepe noktası tutarsınız ... ikinci çizgiyle çakışan çizgiler bulduğunuzda itersiniz, ikinci çizgiyi aldığınızda pop. - dışbükey gövdede yaptığınız gibi.


bunun için herhangi bir psedo algoritması biliyor musunuz?
EmptyData

5

İşte alternatif bir çözüm, bunu daha iyi beğenip beğenmediğinize bakın.

  1. Bir üçgenleme yap , bunun lalaunay olması gerekmez - herhangi bir üçgenleme yapardı.

  2. Her üçgeni şişirin - bu önemsiz olmalıdır. üçgeni saat yönünün tersine yerleştirirseniz, çizgileri sağ tarafa hareket ettirin ve kesişim yapın.

  3. Değiştirilmiş bir Weiler-Atherton kırpma algoritması kullanarak birleştirin


üçgenleri tam olarak nasıl şişirirsiniz? Çıktınız nirengi durumuna mı bağlı? Bu yaklaşımla, çokgeni daraltırken davayı halledebilir misiniz?
balint.miklos

Bu yaklaşımın çokgen enflasyonu için gerçekten işe yaradığından emin misiniz? Çokgenin içbükey kısımları, bazı köşelerin giderilmesi gerektiği ölçüde şişirildiğinde ne olur? Şey: baktığınızda poli sonrası üçgenlere ne olur. enflasyon, üçgenler şişirilmiş değil, çarpıtılmış.
Igor Brejc

1
Igor: Weiler-Atherton kırpma algoritması "bazı köşelerin ortadan kaldırılması gerekiyor" durumunu doğru bir şekilde işleyebilir;
J-16 SDiZ

@balint: bir üçgeni şişirmek önemsizdir: vertrex'i normal sırada saklarsanız, sağ taraf her zaman "dışa doğru" olur. Bu çizgi segmentine çizgiler gibi davranın, onları dışarı doğru hareket ettirin ve etkileşimi bulun - bunlar yeni tepe noktasıdır. Nirengi için, ikinci bir düşüncede delaunay üçgenleme daha iyi sonuç verebilir.
J-16 SDiZ

4
Bu yaklaşımın kolayca kötü sonuçlar verebileceğini düşünüyorum. Dörtgen gibi basit bir örnek için bile köşegen kullanılarak üçgenleme. İki genişletilmiş üçgen için: img200.imageshack.us/img200/2640/counterm.png ve onların birliği aradığınız şey değil. Bu yöntemin nasıl faydalı olduğunu anlamıyorum.
balint.miklos

3

Kesme kütüphanesi için Angus Johnson'a çok teşekkürler. Http://www.angusj.com/delphi/clipper.php#code adresinde kırpma ana sayfasında kırpma şeyler yapmak için iyi kod örnekleri vardır, ancak çokgen ofsetleme için bir örnek görmedim. Bu yüzden kodumu gönderirsem belki birisi için yararlı olduğunu düşündüm:

    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

Başka bir seçenek de boost :: polygon kullanmaktır - dokümantasyon biraz eksiktir, ancak aslında tamponlamayı uygulayan yöntemlerin resizeve bloataşırı yüklenmiş +=operatörün olduğunu bulmalısınız . Örneğin, bir poligonun (veya bir çokgenler kümesinin) bir değere göre arttırılması şu kadar basit olabilir:

poly += 2; // buffer polygon by 2

Sadece tamsayı koordinatlarını desteklediğinden boost :: polygon ile nasıl bir şey yapmanız gerektiğini anlamıyorum? Genel (kayan nokta koordinatları) çokgenim olduğunu ve onu genişletmek istediğimi söyleyin - ne yapmalıyım?
David Doria

@DavidDoria: koordinatlarınız için hangi çözünürlük / doğruluk ve dinamik aralığa ihtiyacınız olduğuna bağlıdır, ancak uygun bir ölçeklendirme faktörü ile 32 bit veya 64 bit int kullanabilirsiniz. Bu arada (yanlışlıkla) geçmişte float koordinatlarıyla boost :: polygon kullandım ve iyi çalışıyor gibi görünüyor , ancak% 100 sağlam olmayabilir (dokümanlar buna karşı uyar!).
Paul R

"Çoğu zaman işe yarayacak" :) ile iyi olurdu. Bunu denedim: ideone.com/XbZeBf ama derlemiyor - herhangi bir düşünce?
David Doria

Açıkçası yanlış bir şey görmüyorum, ama benim durumumda doğrusal uzmanlık (polygon_90) kullanıyordum, bu yüzden bir fark yaratıp yaratmadığını bilmiyorum. Bununla birlikte oynadığımdan bu yana birkaç yıl geçti.
Paul R

Tamam - şimdi bana geri dönüyor - tek tek çokgenlerle değil , yalnızca +=bir çokgen kümesiyle kullanabilirsiniz . Bir std :: çokgen vektörü ile deneyin. (Tabii ki vektör sadece bir çokgen içermelidir).
Paul R

1

@ JoshO'Brian'ın tavsiyelerine dayanarak rGeos, Rdilde paketin bu algoritmayı uyguladığı anlaşılıyor . Bkz rGeos::gBuffer.


0

Biri kullanabileceğiniz birkaç kütüphane vardır (3D veri setleri için de kullanılabilir).

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

Bir de algoritmaları daha ayrıntılı olarak anlamak için bu kütüphaneler için ilgili yayınları bulabilirsiniz.

Sonuncusu en az bağımlılığa sahiptir ve bağımsızdır ve .obj dosyalarında okuyabilir.

En iyi dileklerimle, Stephan


0

Basit geometri kullanıyorum: Vektörler ve / veya Trigonometri

  1. Her köşede orta vektörü ve orta açıyı bulun. Orta vektör, köşenin kenarları tarafından tanımlanan iki birim vektörün aritmetik ortalamasıdır. Orta Açı, kenarlar tarafından tanımlanan açının yarısıdır.

  2. Çokgeninizi her kenardan d miktarına genişletmeniz (veya daraltmanız) gerekiyorsa; yeni köşe noktasını elde etmek için d / sin (midAngle) miktarıyla (in) dışarı çıkmalısınız.

  3. Bunu tüm köşeler için tekrarlayın

*** Yönünüze dikkat edin. Köşeyi tanımlayan üç noktayı kullanarak CounterClockWise Testi yapın; Hangi yolun dışarıda veya dışarıda olduğunu bulmak için.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.