Comment fonctionnent les formes (rectangles) dans les arbres quadruples?

10

On m'a dit qu'un arbre quadruple est la structure de données idéale pour mon jeu, mais j'ai du mal à comprendre comment fonctionnent exactement les formes dans les arbres quadruples.

Je fais cela en JavaScript, mais je pense que ces questions pourraient s'appliquer aux arbres quadruples dans n'importe quelle langue.

Je pense que je comprends surtout comment les points de base (x, y) et l' insertion de points fonctionnent dans les arbres quadruples, et que je pourrais le faire sur papier.

Voici un JSfiddle de mon expérience avec les points.

points d'arbre quad

Mis à part un cas, mes tests avec des points fonctionnent comme prévu.

Mais ma confusion commence lorsque des formes comme des rectangles sont impliquées. Lorsque vous récupérez à partir d'un arbre quadruple avec des formes, vérifie-t-il chaque point de la forme et dans quels nœuds ils tombent? Et comment fonctionnent les insertions de formes, même si elles acceptent les paramètres (x, y, largeur, hauteur) pour chaque forme? Utilise-t-il la largeur / hauteur du point de départ pour calculer d'autres points d'angle, qui sont ensuite distribués dans les nœuds appropriés? Si une forme insérée s'étend sur quatre nœuds, les données de cette forme sont-elles enregistrées dans les quatre nœuds?

Et lorsqu'une méthode de récupération accepte une forme comme paramètre (x, y, largeur, hauteur), que se passe-t-il réellement? Est-ce qu'il voit d'abord à quels nœuds la forme s'étendrait s'il devait être inséré, puis récupère tous les objets de ces nœuds?

J'ai un JSfiddle qui travaille avec des formes , mais je suis complètement confus sur les résultats de mes tests. Je reçois des objets en double retournés!

formes d'arbre quad

Par exemple, le carré rouge est un équivalent dessiné des paramètres que j'entre dans ma méthode de récupération. Je pense que puisque ce carré rouge s'étend sur les quatre nœuds, il devrait renvoyer chaque objet dans l'arbre quadruple! Mais ce n'est pas le cas, et j'ai du mal à rationaliser ce qu'il retourne. J'ai un certain nombre d'autres tests qui sont actuellement commentés, mais vous pouvez annuler le commentaire et les exécuter pour voir des résultats plus confus!

Comme dire, si je veux retourner tous les points dans un arbre quadruple, comment ferais-je cela? Une méthode de récupération utilisant une forme de toute la taille des limites? Par exemple, récupérer (0, 0, canvas.width, canvas.height)?

La bibliothèque JavaScript QuadTree que j'utilise a été mentionnée par diverses autres sources, donc je suppose que la mise en œuvre réelle est correcte et réputée.

Je pense qu'une grande partie de ma confusion peut provenir d'un malentendu de la terminologie de l'arbre quadruple. Par exemple, pourquoi disent-ils des limites plutôt que des dimensions, lorsqu'un "point" a également des paramètres largeur / hauteur? Est-ce une question de convention / raccourci, ou s'agit-il de concepts complètement différents?

Merci pour votre temps!

user2736286
la source
Ils sont stockés dans l'arbre quadruple comme d'habitude, par leur position. Ceux-ci sont généralement au centre, mais peuvent être dans le coin comme vous l'avez défini. Votre question peut être un double de celle-ci: QuadTree: stocker uniquement des points ou des régions?
MichaelHouse
J'ai lu cette question, mais je ne comprends toujours pas complètement les réponses :(. "Vous devez le stocker dans le plus petit nœud qui le contient complètement - même si cela dépasse la capacité (utilisez un conteneur redimensionnable)." - Quand il dit que le plus petit nœud qui le contient COMPLÈTEMENT, si l'objet est très grand, ce nœud ne serait-il pas souvent un nœud non feuille? Comme dans un nœud supérieur qui se compose uniquement d'autres nœuds? Cela ne semble pas juste moi, car cela signifierait que le nœud contenant aurait 1 feuille et les 4 nœuds plus petits en son sein
user2736286
1
@ user2736286 Si vous regardez le code de la librairie quadtree (ce n'est pas très long), vous pouvez voir qu'il stocke des rectangles dans des nœuds de niveau supérieur pour les empêcher de chevaucher les frontières des nœuds. Voir les références au _stuckChildrenchamp dans le code. Vous pouvez également le voir dans l'exemple "récupération des éléments avec des limites" - il met toujours en surbrillance en rouge les nœuds qui chevauchent les bordures des nœuds sur lesquels vous avez cliqué, jusqu'au nœud racine.
Nathan Reed
@ user2736286 En outre, la bibliothèque quadtree semble avoir un bogue dans la récupération des éléments avec des limites - si vous lui donnez un rect de requête qui chevauche certaines bordures de nœuds, il ne renvoie pas tous les rects dans les nœuds que la requête touche. Vous pouvez également le voir facilement dans la "récupération des éléments avec des limites", en cliquant près des bordures des nœuds.
Nathan Reed
@NathanReed Plus tôt, j'ai essayé de comprendre le code, mais je ne suis pas assez qualifié pour le comprendre sans fondement conceptuel. Grâce au pseudo-code de John McDonald, je comprends maintenant comment les rectangles sont insérés dans les nœuds, mais je pense que je ne sais toujours pas comment fonctionne la récupération. Quant à la "récupération des éléments avec des limites" - je suis complètement confus par l'exemple. Par exemple, lorsque je clique sur un rect qui s'inscrit proprement dans l'un des plus petits nœuds, pourquoi tous ces autres rects en dehors de ce nœud sont-ils également mis en évidence?
user2736286

Réponses:

10

Les Quadtrees stockent et récupèrent généralement des rectangles. Un point est un cas spécifique où la largeur et la hauteur sont nulles. La logique suivante est utilisée pour trouver la maison de nouveaux rectangles dans l'arborescence, en commençant par le nœud racine:

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

Notez que les rectangles peuvent être stockés à n'importe quelle profondeur, il ne doit pas nécessairement s'agir d'un quadrilatère. Si le rectangle chevauche la frontière au niveau racine, le quadruple racine stockera le rectangle.

Par exemple, le carré rouge est un équivalent dessiné des paramètres que j'entre dans ma méthode de récupération. Je pense que puisque ce carré rouge s'étend sur les quatre nœuds, il devrait renvoyer chaque objet dans l'arbre quadruple! Mais ce n'est pas le cas, et j'ai du mal à rationaliser ce qu'il retourne.

Lorsque vous interrogez le quadtree, il ne renvoie que des rectangles contenus dans la requête. Dans votre exemple, vous forcez chacun des 4 quadrants enfants à être examiné plus en détail car le rectangle de requête rouge coupe chaque quadruple enfant. Étant donné que les enfants n'ont plus d'autres enfants, chacun des rectangles de ces quads enfants sera comparé au rectangle rouge. Étant donné qu'aucun des rectangles de l'arborescence n'entre en collision avec le rectangle de requête rouge, rien ne doit être retourné.

Vous commencez vraiment à voir les avantages des arbres quadruples lorsque vous avez beaucoup d'objets et beaucoup d'espace pour que les objets existent par rapport aux zones de requête. Dans ces cas, une petite zone de requête peut rapidement éliminer de vastes portions de l'arbre quadruple. Seuls les quadrilatères qui coupent le rectangle de requête seront examinés plus en détail.

ÉDITER

La récupération se fait généralement comme suit:

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

Cependant, dans votre bibliothèque, il ne semble pas vérifier si les rectangles renvoyés se croisent avec la requête, vous devrez donc l'ajouter vous-même.

John McDonald
la source
Après avoir examiné votre bibliothèque plus en détail, votre bibliothèque retourne une liste de tous les candidats à la collision possibles pour le rectangle de requête que vous spécifiez. Il semble que c'est votre travail de comparer votre rectangle de requête à chaque candidat pour voir s'il y a une collision réelle. La plupart des bibliothèques effectuent cette dernière étape pour vous et renvoient simplement les collisions réelles avec la zone de requête. Donc, tout ce que vous devez faire est d'ajouter de la logique dans votre retrieveméthode pour élaguer la liste. Vous pouvez voir ce que cela fait un peu plus clairement ici: mikechambers.com/html5/javascript/QuadTree/examples/…
John McDonald
1
@ user2736286 Au cas où vous ne l'auriez pas détecté, il est important de noter la récursivité dans les fonctions de requête et de stockage que JohnMcDonald a écrites. Cela n'aura pas beaucoup de sens si vous n'obtenez pas cette partie. Pour les deux, chaque récursion va plus loin dans l'arbre, c'est-à-dire sur les branches et enfin dans les feuilles.
TASagent
Merci John, votre pseudo-code est très utile. Lorsque vous dites "si (queryRect intersecte avec les limites childNode)", cela signifie-t-il essentiellement si le queryRect est contenu dans les limites - partiellement ou entièrement? Je veux juste être clair à 100%. De plus, dans l'exemple "récupérer l'élément par limites" en cours de discussion, j'ai une image du résultat de mon clic. Pic Le point bleu est l'endroit où j'ai cliqué. Alors, pourquoi les deux rectangles à l'intérieur de ce nœud ne sont-ils pas mis en évidence? Pourquoi les rectangles loin de là sont-ils également mis en évidence? Je pense que c'est ce qui me dérange vraiment.
user2736286
Partielle ou complète. Si les deux se touchent, ou l'un ou l'autre est complètement contenu par l'autre. Vous voulez lire le wiki sur Quadtrees , mais j'ai pris votre photo et je l'ai codée par couleur pour représenter les différentes limites de l'enfant. Tous les rectangles bleus se croisent avec les limites des premiers quads enfants, puis le magenta est un calque plus profond et le vert un calque plus profond encore. Les rectangles rouges coupent le quadruple cliqué. Votre bibliothèque retourne tous les rects sur les limites des enfants, plus tous ceux contenus dans le quadruple enfant final (rouge): i.imgur.com/InB8KBj.png
John McDonald
Ok, j'ai donc fait de mon mieux pour parcourir le code source de la bibliothèque et je comprends enfin le comportement de stuckChildren, et que tous ces rects supplémentaires dans ma photo sont simplement les stuckChildren. À l'origine, je pensais que tout rect couvrant plusieurs nœuds aurait simplement ses données dupliquées et insérées dans chaque nœud plus petit. Maintenant, je me rends compte que stuckChildren n'est pas inséré dans les nœuds qu'il couvre, mais reste simplement dans le nœud qui le contient, c'est pourquoi je dois également vérifier tous les nœuds parents, et pas seulement le plus petit nœud contenant ma requête rect. Merci pour la photo; cela a beaucoup plus de sens maintenant :)
user2736286