Algoritmo per la diffusione di etichette in modo visivamente accattivante e intuitivo


24

Versione breve

Esiste un modello di progettazione per distribuire le etichette dei veicoli in modo non sovrapposto, posizionandole il più vicino possibile al veicolo a cui si riferiscono? In caso contrario, uno dei metodi che suggerisco è praticabile? Come lo implementeresti tu stesso?

Versione estesa

Nel gioco che sto scrivendo ho una visione a volo d'uccello dei miei veicoli aerei. Ho anche accanto a ciascuno dei veicoli una piccola etichetta con i dati chiave sul veicolo. Questo è uno screenshot reale:

Due veicoli con le loro etichette

Ora, poiché i veicoli potrebbero volare a diverse quote, le loro icone potrebbero sovrapporsi. Tuttavia, non vorrei mai che le loro etichette si sovrapponessero (o un'etichetta del veicolo "A" si sovrapponga all'icona del veicolo "B").

Attualmente, posso rilevare le collisioni tra gli sprite e spingo semplicemente via l'etichetta offensiva in una direzione opposta allo sprite altrimenti sovrapposto . Funziona nella maggior parte dei casi, ma quando lo spazio aereo è affollato, l'etichetta può essere spinta molto lontano dal suo veicolo, anche se ci fosse un'alternativa "più intelligente" alternativa. Ad esempio ottengo:

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

dove sarebbe meglio (= etichetta più vicino al veicolo) ottenere:

          B - label
label - A
          C - label

EDIT: Va anche considerato che oltre al caso dei veicoli sovrapposti, potrebbero esserci altre configurazioni in cui le etichette dei veicoli potrebbero sovrapporsi (gli esempi di arte ASCII mostrano ad esempio tre veicoli molto vicini in cui l'etichetta di Asi sovrappone all'icona di Be C).

Ho due idee su come migliorare la situazione attuale, ma prima di passare il tempo a implementarle, ho pensato di rivolgermi alla comunità per un consiglio (dopo tutto sembra un "problema abbastanza comune" che possa esistere un modello progettuale).

Per quello che vale, ecco le due idee a cui stavo pensando:

Slot-isation dello spazio etichetta

In questo scenario, dividerei tutto lo schermo in "slot" per le etichette. Quindi, ogni veicolo dovrebbe sempre avere la sua etichetta posizionata nel vuoto più vicino (vuoto = nessun altro sprite in quella posizione.

Ricerca a spirale

Dalla posizione del veicolo sullo schermo, proverei a posizionare l'etichetta ad angoli crescenti e quindi a raggi crescenti, fino a trovare una posizione non sovrapposta. Qualcosa in fondo a:

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

1
Quanti aerei potrebbero essere sovrapposti contemporaneamente?
wangburger,

1
@wangburger - Non avrei mai pensato che fosse rilevante (sarebbe interessato a saperne di più sulla tua linea di pensiero), ma la risposta è: dipende dalla strategia di gioco del giocatore. Tecnicamente il mondo potrebbe avere 24 veicoli sovrapposti, ma una cifra realistica nella maggior parte delle condizioni di gioco è 3-4.
mac,

3
Non è più confuso avere etichette in movimento rispetto al piano che sovrapposte ma statiche per un ragionevole lasso di tempo?
Maik Semder,

3
Potresti essere interessato a en.wikipedia.org/wiki/… - questo non è un problema semplice da risolvere. Non aspettarti di trovare una soluzione perfetta.
Blecki,

2
GraphViz è una suite di strumenti per disporre i grafici in modo visivamente piacevole, con la tendenza a evitare sovrapposizioni nelle etichette. Anche se potrebbe non essere utilizzabile direttamente, potresti essere in grado di raccogliere alcune informazioni dalla loro documentazione o dal codice sorgente su quale tipo di algoritmi usano per strutturare i loro grafici. Sembrano avere sia modelli basati sull'energia che modelli basati sulla molla, per esempio.
Lars Viklund,

Risposte:


14

Essenzialmente questo problema è simile a un problema di prevenzione delle collisioni. Sì, gli aerei possono volare a diverse altitudini, ma le loro etichette sono tutte alla stessa "altitudine".

Esistono algoritmi come Unaligned Collision Avoidance , che sarebbe un passo nella giusta direzione per te. Naturalmente per la tua situazione, le etichette sono "legate" ai loro piani, quindi hanno un raggio di movimento limitato.

Se osservi il comportamento floccato, vuoi implementare la prima "regola" del floccaggio: repulsione a corto raggio. Tuttavia, invece di "sterzare" nella direzione più lontana dai vicini più vicini, utilizzerai il vettore "assente" come posizione di posizionamento per l'etichetta.

Per esempio:

inserisci qui la descrizione dell'immagine

Il grande cerchio nero rappresenta la tua area di influenza, il cerchio verde rappresenta i posizionamenti validi per l'etichetta, il punto verde centrale è il piano che stai considerando, il piccolo punto verde è il punto sul cerchio scelto per il posizionamento dell'etichetta.

Ora i punti neri potrebbero rappresentare altre etichette o altri piani. Non sono sicuro di quale funzionerebbe meglio, potresti evitare meglio se fossero altre etichette, ma non ne sono sicuro. Ovviamente le frecce "forza" sono i vettori di direzione tra il tuo piano attuale e gli "oggetti di influenza". Infine, la scatola è l'etichetta.

Quindi, usando il tuo esempio sopra, penso che questo produrrebbe qualcosa del tipo:

            - label
           / 
          B 
label - A
          C 
           \
            - label

Usando questo metodo ci sono alcune situazioni in cui dovrai creare casi speciali, come tre piani allineati verticalmente:

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

Tutte e tre le etichette potrebbero capovolgere da destra a sinistra, a seconda di come sono definiti gli angoli delle etichette. Fondamentalmente, dovrai solo fare attenzione agli angoli attorno al cerchio in cui le tue etichette possono cambiare l'angolo da cui sono disegnate: 0, 90, 180, 270.

inserisci qui la descrizione dell'immagine

Penso che alla fine sembrerebbe un po 'pulito, osservando le etichette evitarsi a vicenda. Se diventa troppo distratto, forse puoi arrotondare ai 10 gradi più vicini per movimenti meno frequenti.

Ci scusiamo per i dettagli strani, la maggior parte di queste cose a cui ho pensato quando stavo realizzando un menu radiale per il mio gioco, ma penso che in questa forma "dinamica" funzionerebbe abbastanza bene.


1
In realtà il mio esempio non ha nemmeno preso in considerazione i piani che sono direttamente uno sopra l'altro, poiché le loro coordinate x e z corrispondono esattamente (ma se stai usando i float probabilmente questo non accadrà). Gli esempi che ho dato sono di aerei vicini l'uno all'altro. Inoltre, come ho detto, potresti pensare ai punti neri nell'immagine sopra come altre etichette.
MichaelHouse

1
Oh, sembra che hai rimosso il tuo commento?
MichaelHouse

1
Ciò che intendevo con il mio precedente commento [scarsamente formulato e quindi ora rimosso], era che potresti avere uno scenario in cui un'etichetta è "intrappolata / circondata" da altri sprite non mobili (cioè veicoli). In questo caso l'etichetta dovrebbe forse "saltare" al di fuori del cerchio racchiuso, ma non mi è esattamente chiaro come dovrebbe accadere. A proposito: inizialmente pensavo che il punto verde della tua foto fosse composto da diversi aeroplani sovrapposti uno sull'altro, ma dopo il tuo commento mi sono reso conto che mi sbagliavo ... scusa!).
mac,

1
Ah, capisco. Quindi tratteresti i punti neri come entrambi i piani E le etichette allora. Se la posizione trovata dalla prima iterazione non è buona, raddoppia il raggio e ricontrolla. Oppure hai già un vettore che punta lontano dalla maggior parte della folla, potresti seguirlo fino a trovare un posto che funzioni. Tuttavia, penso che il primo metodo avrebbe risultati migliori.
MichaelHouse

9

Dopo qualche riflessione, ho finalmente deciso di implementare il metodo di ricerca a spirale che ho brevemente descritto nella domanda originale.

La logica è che il metodo Byte56 ha bisogno di un trattamento speciale per determinate condizioni, mentre la ricerca a spirale non lo fa, e codifica in un modo davvero compatto . Inoltre, la ricerca sfrenata enfatizza la ricerca del punto più vicino al veicolo per posizionare l'etichetta, che l'IMO è il fattore principale nel rendere leggibile la mappa.

Tuttavia, continua a votare la sua risposta, poiché non è solo utile, è anche molto ben scritto!

Ecco uno screenshot del risultato ottenuto con il codice a spirale:

inserisci qui la descrizione dell'immagine

Ed ecco il codice che - sebbene non sia autonomo - dà un'idea di quanto sia semplice l'implementazione:

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

Nota 1 : tag.place()restituisce True se il tag è interamente nell'area visibile dello schermo / radar. Quindi quella riga recita come "continua ad andare in loop se il tag è fuori dal radar o si sovrappone a qualcos'altro ..."

Nota 2 : tag.connector.updateè il metodo che traccia la linea che collega l'icona dell'aeroplano all'etichetta / tag con le informazioni di testo.


Ben fatto, è davvero molto compatto. Grazie per gli elogi. Inoltre, grazie per la pubblicazione di ciò che hai finito per fare, è sempre utile per le persone che cercano risposte in seguito. Dallo screenshot sembra che funzioni molto bene! Assicurati di accettare la tua risposta, poiché è quello con cui sei finito.
MichaelHouse

@ Byte56 - Grazie per il "retro-elogio";) Sto aspettando di selezionare la risposta come accettata perché vorrei ancora codificare anche la tua soluzione e confrontare il risultato. Per prima cosa, sospetto che la tua soluzione potrebbe comportare l' esecuzione di un codice più lungo ma anche più veloce . Inoltre, vorrei vedere come i due si confrontano in spazi aerei molto stipati ... quindi c'è ancora una possibilità che io possa rilasciare il codice con il tuo algoritmo. Guarda questo spazio! ;)
mac,

@ Byte56 - Ho provato a implementare il tuo algoritmo. Ha funzionato bene e velocemente a bassa densità, ma non appena lo spazio è stato riempito, ho avuto problemi nel trovare un'implementazione semplice che gestisse situazioni specifiche in cui un'etichetta dovrebbe "saltare" un blocco di altre etichette o essere intrappolata da non Sprite mobili (ovvero icona piana). Segnerò questa risposta come selezionata, ma di nuovo: grazie mille per il tempo e il contributo! :)
mac,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.