Come funzionano le forme (rettangoli) negli alberi quad?


10

Mi è stato detto che un albero quad è la struttura dati ideale per il mio gioco, ma ho difficoltà a capire come funzionano esattamente le forme all'interno degli alberi quad.

Lo sto facendo in JavaScript, ma penso che queste domande potrebbero applicarsi ai quad alberi in qualsiasi lingua.

Credo soprattutto capire come (x, y) di base punti e inserimento punto opere in alberi quad, e che ho potuto farlo su carta.

Ecco un JSfiddle della mia sperimentazione di punti.

punti albero quad

A parte un caso, i miei test con i punti funzionano come previsto.

Ma la mia confusione inizia quando sono coinvolte forme come rettangoli. Quando stai recuperando da un albero quad con forme, controlla ogni punto della forma e in quali nodi rientrano? E come funzionano anche gli inserimenti di forme, quando accetta i parametri (x, y, larghezza, altezza) per ogni forma? Usa la larghezza / altezza dal punto iniziale per calcolare altri punti d'angolo, che vengono quindi distribuiti in nodi appropriati? Se una forma inserita si estende in quattro nodi, i dati di quella forma vengono salvati in tutti e quattro i nodi?

E quando un metodo di recupero accetta una forma come parametro (x, y, larghezza, altezza), cosa sta realmente succedendo? Sta prima vedendo a quali nodi si estenderebbe la forma se dovesse essere inserita, e quindi recupererà tutti gli oggetti di quei nodi?

Ho un JSfiddle che lavora con le forme , ma sono completamente confuso sui risultati dei miei test. Ricevo oggetti duplicati restituiti!

forme quadrate

Ad esempio, il quadrato rosso è un equivalente disegnato dei parametri che sto inserendo nel mio metodo di recupero. Penserei che, dato che questo quadrato rosso si estende su tutti e quattro i nodi, dovrebbe restituire ogni oggetto nel quad! Ma non è così e ho problemi a razionalizzare ciò che restituisce. Ho una serie di altri test che sono attualmente commentati, ma è possibile annullare il commento ed eseguirli per vedere risultati più confusi!

Come dire, se voglio restituire tutti i punti in un albero quad, come potrei farlo? Un metodo di recupero che utilizza una forma dell'intera dimensione dei limiti? Ad esempio, recuperare (0, 0, canvas.width, canvas.height)?

La libreria JavaScript QuadTree che sto usando è stata citata da varie altre fonti, quindi presumo che l'implementazione effettiva sia corretta e affidabile.

Penso che molta della mia confusione possa derivare da un malinteso sulla terminologia dei quad tree. Ad esempio, perché dicono limiti anziché dimensioni, quando un "punto" ha anche parametri larghezza / altezza? È una questione di convenzione / di breve durata o sono concetti completamente diversi?

Grazie per il tuo tempo!


Sono memorizzati nell'albero quad normalmente, in base alla loro posizione. In genere quelli sono al centro, ma possono essere nell'angolo come hai definito. La tua domanda potrebbe essere un duplicato di questo: QuadTree: memorizzare solo punti o regioni?
MichaelHouse

Ho letto quella domanda, ma non capisco ancora completamente le risposte :(. "Devi memorizzarlo nel nodo più piccolo che lo contiene completamente - anche se questo supera la capacità (usa un contenitore ridimensionabile)." - Quando ha dice che il nodo più piccolo che COMPLETAMENTE lo contiene, se l'oggetto è molto grande, quel nodo non sarebbe spesso un nodo non foglia? Come in un nodo superiore che consiste solo di altri nodi? Non sembra giusto io, dal momento che ciò significherebbe che il nodo contenente avrebbe 1 foglia e 4 nodi più piccoli al suo interno.
user2736286

1
@ user2736286 Se guardi il codice per la libreria quadtree (non è molto lungo), puoi vedere che memorizza rettangoli in nodi di livello superiore per impedirgli di cavalcare i bordi dei nodi. Vedi i riferimenti al _stuckChildrencampo nel codice. Puoi anche vedere questo nell'esempio "recupero di elementi con limiti": evidenzia sempre in rosso i nodi che si trovano a cavallo dei bordi dei nodi in cui hai fatto clic, fino al nodo principale.
Nathan Reed,

@ user2736286 Inoltre, la libreria quadtree sembra avere un bug nel recupero di elementi con limiti - se gli dai un rect di query che si trova a cavallo di alcuni bordi del nodo, non restituisce tutte le rettifiche in nodi toccate dalla query. Puoi vederlo facilmente anche nel "recupero di elementi con limiti", facendo clic vicino ai bordi del nodo.
Nathan Reed,

@NathanReed In precedenza ho cercato di comprendere il codice, ma non sono abbastanza abile da capirlo senza una base concettuale. Grazie allo pseudo-codice di John McDonald ora capisco come vengono inseriti i rettangoli nei nodi, ma penso di non essere ancora chiaro su come funzioni il recupero. Per quanto riguarda il "recupero di elementi con limiti", sono completamente confuso dall'esempio. Ad esempio, quando faccio clic su un rettangolo che si adatta perfettamente a uno dei nodi più piccoli, perché vengono evidenziati anche tutti questi altri rect al di fuori di quel nodo?
user2736286,

Risposte:


10

I quadricipiti in genere archiviano e recuperano rettangoli. Un punto è un caso specifico in cui larghezza e altezza sono zero. La seguente logica viene utilizzata per trovare home per i nuovi rettangoli nella struttura, a partire dal nodo radice:

void Store(Rectangle rect)
{
    if(I have children nodes)
    {
        bool storedInChild = false;
        foreach(Node childNode in nodes)
        {
            if(this rectangle fits entirely inside the childNode bounds)
            {
                childNode.Store(rect);   // Go deeper into the tree
                storedInChild = true;
                break;
            }
        }
        if(not storedInChild)
        {
            Add this rectangle to the current node
        }
    }
    else
    {
        Add this rectangle to the current node
    }
}

Si noti che i rettangoli possono essere memorizzati a qualsiasi profondità, non deve essere un quadrifoglio. Se il rettangolo si trova a cavallo del limite a livello di radice, il quadrupolo di radice memorizzerà il rettangolo.

Ad esempio, il quadrato rosso è un equivalente disegnato dei parametri che sto inserendo nel mio metodo di recupero. Penserei che, dato che questo quadrato rosso si estende su tutti e quattro i nodi, dovrebbe restituire ogni oggetto nel quad! Ma non lo è, e ho problemi a razionalizzare ciò che restituisce.

Quando si esegue una query su quadtree, verranno restituiti solo rettangoli contenuti nella query. Nel tuo esempio, imponi a ciascuno dei 4 quadratini figlio di essere esaminato in modo più dettagliato perché il rettangolo di query rosso interseca ogni quadratino figlio. Dal momento che i bambini non hanno altri figli, ciascuno dei rettangoli in questi quadratini figlio verrà confrontato con il rettangolo rosso. Poiché nessuno dei rettangoli dell'albero si scontra con il rettangolo rosso della query, non è necessario restituire nulla.

Inizi davvero a vedere i vantaggi dei quadrifici quando hai molti oggetti e un sacco di spazio per gli oggetti rispetto alle aree di query. In questi casi, una piccola area di query può eliminare rapidamente grandi porzioni del quad tree. Solo i quad che si intersecano con il rettangolo della query verranno mai esaminati in modo più dettagliato.

MODIFICARE

Il recupero viene solitamente eseguito come segue:

List<Rectangle> Query(Rectangle queryRect)
{
    List<Rectangle> results;
    foreach(Node childNode in children)
    {
        if(queryRect intersects with childNode bounds)
        {
            results += childNode.Query(queryRect);   // Dig deeper into the tree
        }
    }

    foreach(Rectangle I have stored in this quad)
    {
        if(queryRect intersects with rect)  // Your library doesn't do this
        {
            results += rect;
        }
    }

    return results;
}

Nella tua libreria, tuttavia, non sembra verificare se i rettangoli che sta tornando si intersecano con la query, quindi dovrai aggiungerlo tu stesso.


Dopo aver esaminato la libreria in modo più dettagliato, la libreria restituisce un elenco di tutti i possibili candidati alla collisione per il rettangolo di query specificato. Sembra che sia il tuo compito confrontare il rettangolo della query con ciascun candidato per vedere se c'è una collisione effettiva. La maggior parte delle librerie fa l'ultimo passo per te e restituisce le collisioni effettive con l'area della query. Quindi, tutto ciò che devi fare è aggiungere un po 'di logica nel tuo retrievemetodo per eliminare l'elenco. Puoi vedere cosa sta facendo un po 'più chiaramente qui: mikechambers.com/html5/javascript/QuadTree/examples/…
John McDonald

1
@ user2736286 Nel caso in cui non lo avessi rilevato, è importante prendere nota della ricorsione nelle funzioni Query e Store scritta da JohnMcDonald. Non avrà molto senso se non ottieni quella parte. Per entrambi, ogni ricorsione va più in profondità nell'albero, cioè sui rami e infine nelle foglie.
TASagent,

Grazie John, il tuo pseudo-codice è molto utile. Quando dici "if (queryRect si interseca con i limiti childNode)", ciò significa fondamentalmente se queryRect è contenuta nei limiti - parzialmente o completamente? Voglio solo essere chiaro al 100%. Inoltre, nell'esempio "recupera oggetto per limiti" in discussione, ho un'immagine del risultato del mio clic. Pic Il punto blu è dove ho cliccato. Quindi perché non sono evidenziati solo i due rettangoli all'interno di quel nodo? Perché anche i rettangoli lontani da esso sono evidenziati? Penso che sia quello che mi sta davvero confondendo.
user2736286,

Parte o pieno. Se i due toccano, o uno dei due è completamente contenuto dall'altro. Vuoi leggere la wiki su Quadtrees , ma ho scattato la tua foto e l'ho codificata a colori per rappresentare i diversi confini del bambino. Tutti i rettangoli blu si intersecano con i confini dei primi quadratini secondari, quindi il magenta è uno strato più profondo e uno verde più profondo ancora. I rettangoli rossi si intersecano con il quadrato cliccato. La tua libreria restituisce tutti i rects sui confini secondari più tutti contenuti nel quad secondario
John McDonald

Ok, quindi ho fatto del mio meglio per esaminare il codice sorgente della lib e finalmente capisco il comportamento di stuckChildren, e che tutti quei retti extra nella mia foto sono semplicemente i chuckren bloccati. Inizialmente pensavo che qualsiasi rects che si estende su più nodi avrebbe solo i suoi dati duplicati e inseriti in ciascun nodo più piccolo. Ora mi rendo conto che StuckChildren non viene inserito nei nodi che si estende, ma rimane semplicemente nell'un nodo che lo contiene, motivo per cui devo controllare anche tutti i nodi padre e non solo il nodo più piccolo contenente la mia query rect. Grazie per l'immagine; ora ha molto più senso :)
user2736286
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.