Etiketleri görsel olarak çekici ve sezgisel bir şekilde yaymak için algoritma


24

Kısa versiyon

Araç etiketlerini üst üste gelmeyen bir şekilde dağıtmak, atıfta bulundukları araca mümkün olduğunca yakın yerleştirmek için bir tasarım deseni var mı? Eğer değilse, önerdiğim yöntemlerden herhangi biri uygulanabilir mi? Bunu kendin nasıl uygularsın?

Genişletilmiş versiyon

Yazdığım oyunda, havadan geçen araçlarımın kuşbakışı bir vizyonu var. Ayrıca, araçların her birinin yanında araçla ilgili anahtar veriler içeren küçük bir etiket var. Bu gerçek bir ekran görüntüsü:

Etiketli iki araç

Şimdi, araçlar farklı irtifalarda uçabildiklerinden, simgeleri üst üste gelebilirdi. Bununla birlikte, etiketlerinin hiçbir zaman üst üste binmesini istemem (veya 'A' aracından bir etiket 'araç' B simgesinin üzerine geldiğinde).

Şu anda, sprite arasındaki çarpışmayı tespit edebiliyorum ve rahatsız edici etiketi aksi halde üst üste gelen sprite ile ters yönde itiyorum . Bu, çoğu durumda işe yarar , ancak hava sahası kalabalıklaştığında, alternatif bir "daha akıllı" alternatif olsa bile , etiket araçtan çok uzağa itilebilir . Örneğin alıyorum:

  B - label
A -----------label
  C - label

aşağıdakileri elde etmenin daha iyi olacağı (= araca daha yakın bir etiket):

          B - label
label - A
          C - label

EDIT: Örtüşen araçlar kasasının yanı sıra, araç etiketlerinin Aüst üste gelebileceği başka yapılandırmalar olabileceği de göz önünde bulundurulmalıdır (ASCII-sanat örnekleri, örneğin etiketinin simgesiyle çakıştığı üç çok yakın aracı göstermektedir . Bve C).

Mevcut durumun nasıl iyileştirileceği hakkında iki fikrim var, ancak bunları uygulamak için zaman harcamadan önce, tavsiye almak için topluma dönmeyi düşündüm (sonuçta bunun için "yeterince yaygın bir sorun" gibi görünüyor).

Buna değer, işte düşündüğüm iki fikir:

Etiket boşluğunun yuvalanması

Bu senaryoda, tüm ekranı etiketler için "yuvalara" bölerdim. Daha sonra, her araç kendi etiketini daima en yakın boş olana yerleştirirdi (boş = o yerde başka bir sprite yok).

Spiral arama

Aracın ekran üzerindeki konumundan, etiketi üst üste bindirmeyecek bir konum bulunana kadar artan açılarda ve sonra artan yarıçaplarda yerleştirmeye çalışacağım. Çizginin aşağısındaki bir şey:

try 0°, 10px
try 10°, 10px
try 20°, 10px
...
try 350°, 10px
try 0°, 20px
try 10°, 20px
...

1
Bir seferde kaç uçak çakışıyor olabilir?
wangburger

1
@wangburger - Asla bunun alakalı olacağını düşünmedim (düşünce hattınız hakkında daha fazla bilgi edinmek isterdim), ancak cevap şu: oyuncunun oyun stratejisine bağlı. Teknik olarak dünya 24 araç üst üste binebilir, ancak çoğu oyun koşulunda gerçekçi bir rakam 3-4.
mac

3
Düzlemle ilgili hareketli etiketlerin üst üste binmekten ziyade, makul bir süre boyunca statik olanlara göre daha kafa karıştırıcı değil mi?
Maik Semder

3
En.wikipedia.org/wiki/… ile ilgilenebilirsiniz - bu çözülmesi kolay bir sorun değil. Mükemmel bir çözüm bulmayı beklemeyin.
Blecki

2
GraphViz , grafikleri görsel olarak hoş bir şekilde yerleştirmek için ve etiketlerin üst üste binmesini önleme eğilimi gösteren bir araç takımıdır . Doğrudan kullanılamamasına rağmen, grafiklerini yerleştirmek için ne tür algoritmalar kullandıklarını belgelemelerinden veya kaynak kodlarından bazı bilgiler toplayabilirsiniz. Örneğin, hem enerji bazlı modeller hem de yay bazlı modeller var gibi gözüküyor.
Lars Viklund

Yanıtlar:


14

Temel olarak bu konu çarpışmadan kaçınma sorununa benzer. Evet, uçaklar farklı yüksekliklerde uçabilir, ancak etiketlerinin hepsi aynı "rakımda" bulunur.

Atanmamış Çarpışma Önleme gibi , sizin için doğru yönde bir adım olacak algoritmalar vardır . Elbette, durumunuz için, etiketler uçaklarına “bağlı”, bu nedenle sınırlı bir hareket yelpazesine sahipler.

Akın Davranışına bakarsanız, akın ilk "kuralını" uygulamak istersiniz: kısa menzilli itme. Bununla birlikte, en yakın komşulardan uzakta bir yöne “yönlendirme” yerine, “uzak” vektörünü etiketiniz için yerleşim yeri olarak kullanacaksınız.

Örneğin:

görüntü tanımını buraya girin

Büyük siyah daire etki alanınızı, yeşil daire etiket için geçerli yerleşimleri temsil eder, orta yeşil nokta şu anda düşündüğünüz düzlemdir, küçük yeşil nokta etiket yerleşimi için seçilen daire üzerindeki noktadır.

Şimdi siyah noktalar diğer etiketleri veya diğer düzlemleri temsil edebilir. Hangisinin en iyi sonucu vereceğinden emin değilim, eğer diğer etiketler olsaydı daha iyi kaçınabilirsiniz. Açıkçası, "kuvvet" okları, geçerli düzleminiz ve "etki nesneleri" arasındaki yön vektörleridir. Son olarak, kutu etiketidir.

Bu nedenle yukarıdaki örneğinizi kullanarak bunun şöyle bir şey üreteceğini düşünüyorum:

            - label
           / 
          B 
label - A
          C 
           \
            - label

Bu yöntemi kullanarak, dikey olarak hizalanmış üç düzlemde olduğu gibi özel durumlar yapmanız gereken bazı durumlar vardır:

          - label       label -
          |                   |
          B                   B
  label - A                   A - label
          C                   C
          |                   |
          - label       label -

Üç etiketin tümü, etiket köşelerinizin nasıl tanımlandığına bağlı olarak flop'u sağdan sola çevirebilir. Temel olarak, etiketlerinizin hangi köşeden çizilebileceği dairenin etrafındaki açılara dikkat etmeniz gerekir: 0, 90, 180, 270.

görüntü tanımını buraya girin

Sonunda, bu etiketlerin birbirinden kaçınmasını izlemek için biraz zarif görüneceğini düşünüyorum. Çok rahatsız edici olursa, daha az sıklıkta hareket etmek için en yakın 10 dereceye yuvarlayabilirsiniz.

Garip ayrıntılar için üzgünüm, bu şeylerin çoğu oyunum için radyal bir menü yaparken düşündüm ama bu "dinamik" formda oldukça iyi sonuç vereceğini düşünüyorum.


1
Aslında benim örneğim, x ve z koordinatlarının tam olarak eşleştiği gibi doğrudan üst üste binen uçakları bile hesaba katmadı (ancak yüzer kullanıyorsanız bu büyük olasılıkla gerçekleşmeyecek). Verdiğim örnekler birbirine yakın uçaklardır. Ek olarak, dediğim gibi, yukarıdaki resimde bulunan siyah noktaları diğer etiketler olarak düşünebilirsiniz.
MichaelHouse

1
Görünüşe göre yorumunuzu kaldırdınız?
MichaelHouse

1
Önceki [kötü formüle edilmiş ve bu nedenle şimdi kaldırılmış] yorumumla kastettiğim, bir etiketin diğer hareketli olmayan (yani araçlar) sprite'ları tarafından "sıkışıp / çevrilmiş" olduğu bir senaryoya sahip olmanızdı. Bu durumda etiketin etrafını saran dairenin dışına "atlaması" gerekir, ancak bunun nasıl olması gerektiği tam olarak belli değil. BTW: Resminizdeki yeşil noktanın üst üste yığılmış birkaç uçak olmasına rağmen, yorumunuzdan sonra yanıldığımı farkettim ... üzgünüm!).
mac,

1
Ah, anlıyorum. Öyleyse siyah noktalara hem uçak hem de etiket gibi davranırsınız. İlk yinelemede bulunan pozisyon iyi değilse yarıçapı ikiye katlayın ve tekrar kontrol edin. Ya da zaten kalabalığın çoğundan uzağa işaret eden bir vektörünüz var, çalışan bir yer bulana kadar bunu takip edebilirsiniz. Ancak, ilk yöntemin daha iyi sonuçlar alacağını düşünüyorum.
MichaelHouse

9

Bazı düşüncelerden sonra, nihayet asıl soruda kısaca bahsettiğim spiral arama yöntemini uygulamaya karar verdim .

Bunun sebebi, Byte56 yönteminin, belirli koşullar için özel muameleye ihtiyaç duymasıdır ; buna karşın, spiral arama araştırması yapmaz ve gerçekten kompakt bir şekilde kodlanır . Ayrıca, spriralling araştırması , etiketi yerleştirmek için araca daha yakın bir yer bulmayı vurguluyor ; bu da IMO'nun haritayı okunabilir hale getirmesinde ana faktör olduğunu gösteriyor.

Ancak, cevabını yükseltmeye devam edin, çünkü sadece yararlı değil, aynı zamanda çok iyi yazılmış!

Spiral koduyla elde edilen sonucun bir ekran görüntüsü:

görüntü tanımını buraya girin

Ve işte bu kod - kendi kendine yetmese de - uygulamanın ne kadar basit olduğu hakkında bir fikir veriyor:

def place_tags(self):
    for tag in self.tags:
        start_angle = tag.angle
        while not tag.place() or is_colliding(tag):  #See note n.1
            tag.angle = (tag.angle + angle_step) % 360
            if tag.angle == start_angle:
                tag.radius += radius_step
        tag.connector.update()                       #See note n.2

Not 1 - tag.place()Etiket tamamen ekranın / radarın görünür alanındaysa, True değerini döndürür. Bu satır, "etiket radarın dışındaysa ya da başka bir şeyle çakışıyorsa döngüye devam et ..." şeklinde okunuyor.

Not 2 - tag.connector.updateUçak simgesi ile metin bilgisini etikete / etikete bağlayan çizgiyi çizen yöntemdir.


Aferin, bu gerçekten çok kompakt. Övgü için teşekkürler. Ayrıca yaptıklarınızla ilgili gönderdiğiniz için teşekkür ederiz, bu daha sonra cevap arayan kişiler için her zaman yararlıdır. Ekran görüntüsünden çok iyi çalışıyor gibi görünüyor! Cevabınızı kabul ettiğinizden emin olun, çünkü işiniz sona erdi.
MichaelHouse

@ Byte56 - "Retro-praise" için teşekkürler;) Yanıtı kabul edildiği şekilde seçmeyi bekliyorum çünkü yine de çözümünüzü kodlamak ve sonucu karşılaştırmak istiyorum. Birincisi, çözümünüzün daha uzun, ancak aynı zamanda daha hızlı kod çalıştırmasıyla sonuçlanabileceğinden şüpheleniyorum . Ayrıca, ikisinin çok tıkışmış hava alanlarında nasıl karşılaştığını görmek isterim ... bu yüzden hala algoritmanızı algoritma ile serbest bırakma şansım var. Bu alanı izle! ;)
mac

@ Byte56 - Algoritmanızı uygulamaya koyuldum. Düşük yoğunlukta iyi ve hızlı bir şekilde çalıştı, ancak alan toparlanır toplanmaz, bir etiketin başka bir etiket bloğunu "atlaması" veya özel olmayan bir şirket tarafından tutulması gereken özel durumları yönetecek basit bir uygulama bulma konusunda sorun yaşadım. hareketli (yani uçak simgesi) sprite. Bu cevabı seçilen şekilde işaretliyorum, fakat tekrar ediyorum: zaman ve katkılarınız için çok teşekkürler! :)
mac
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.