Jak kształty (prostokąty) działają na poczwórnych drzewach?

10

Powiedziano mi, że drzewo quad jest idealną strukturą danych dla mojej gry, ale mam problem ze zrozumieniem, jak dokładnie kształty działają w drzewach quad.

Robię to w JavaScript, ale myślę, że te pytania mogą dotyczyć drzew quadów w dowolnym języku.

Myślę, że głównie rozumiem, jak podstawowe (x, y) punkty i wstawianie punktów działają w drzewach quad i że mogę to zrobić na papierze.

Oto JSfiddle mojego eksperymentowania z punktami.

punkty drzewa quad

Oprócz jednego przypadku moje testy z punktami działają zgodnie z oczekiwaniami.

Ale moje zamieszanie zaczyna się, gdy w grę wchodzą kształty takie jak prostokąty. Czy podczas pobierania z drzewa quad z kształtami sprawdza on każdy punkt kształtu i do jakich węzłów się one mieszczą? Jak działają wstawki kształtów, jeśli akceptują parametry (x, y, szerokość, wysokość) dla każdego kształtu? Czy używa szerokości / wysokości od punktu początkowego do obliczania innych punktów narożnych, które są następnie rozdzielane na odpowiednie węzły? Jeśli wstawiony kształt rozciąga się na cztery węzły, czy dane tego kształtu są zapisywane we wszystkich czterech węzłach?

A kiedy metoda pobierania przyjmuje kształt jako parametr (x, y, szerokość, wysokość), co się właściwie dzieje? Czy najpierw widzi, do jakich węzłów byłby kształt, gdyby miał zostać wstawiony, a następnie pobiera wszystkie obiekty tych węzłów?

Mam JSfiddle pracujący z kształtami , ale jestem całkowicie zdezorientowany wynikami moich testów. Dostaję zwracane duplikaty obiektów!

kształty drzewa quad

Na przykład czerwony kwadrat jest narysowanym odpowiednikiem parametrów, które wprowadzam do mojej metody wyszukiwania. Myślę, że skoro ten czerwony kwadrat obejmuje wszystkie cztery węzły, powinien zwrócić każdy obiekt w drzewie quadów! Ale tak nie jest i mam problem z racjonalizacją tego, co powraca. Mam wiele innych testów, które są obecnie komentowane, ale możesz cofnąć komentarz i uruchomić je, aby zobaczyć bardziej mylące wyniki!

Jak powiedzmy, jeśli chcę zwrócić wszystkie punkty z drzewa quadów, jak miałbym to zrobić? Metoda pobierania wykorzystująca kształt całego rozmiaru granic? Na przykład odzyskać (0, 0, canvas.width, canvas.height)?

Biblioteka JavaScript QuadTree Używam została przekazana przez różnych innych źródeł, więc zakładam, rzeczywista implementacja jest poprawna i godna zaufania.

Myślę, że wiele moich nieporozumień może wynikać z niezrozumienia terminologii quad tree. Na przykład, dlaczego mówią granice zamiast wymiarów, skoro „punkt” ma również parametry szerokości / wysokości? Czy to kwestia konwencji / krótkiego układu, czy też są to zupełnie inne pojęcia?

Dziękuję za Twój czas!

użytkownik2736286
źródło
Są one normalnie przechowywane w drzewie quadów, według ich pozycji. Zazwyczaj są one w centrum, ale można je zdefiniować w rogu. Twoje pytanie może być duplikatem tego: QuadTree: przechowujesz tylko punkty lub regiony?
MichaelHouse
Przeczytałem to pytanie, ale nadal nie rozumiem do końca odpowiedzi :(. „Musisz go przechowywać w najmniejszym węźle, który całkowicie go zawiera - nawet jeśli przekracza to pojemność (użyj pojemnika o zmiennym rozmiarze).” - Kiedy on mówi, że najmniejszy węzeł CAŁKOWICIE go zawiera, jeśli obiekt jest bardzo duży, czy ten węzeł często nie jest węzłem innym niż liść? Jak w węźle wyższym, który składa się tylko z innych węzłów? ja, ponieważ oznaczałoby to, że zawierający węzeł miałby 1 liść i 4 mniejsze węzły w nim
user2736286
1
@ user2736286 Jeśli spojrzysz na kod biblioteki quadtree (nie jest zbyt długi), zobaczysz, że przechowuje prostokąty w węzłach wyższego poziomu, aby uchronić je przed granicami węzłów. Zobacz odniesienia do _stuckChildrenpola w kodzie. Można to również zobaczyć w próbce „pobieranie elementów z granicami” - zawsze podświetla na czerwono węzły, które znajdują się na granicach klikniętych węzłów, aż do węzła głównego.
Nathan Reed
@ user2736286 Ponadto wydaje się, że biblioteka quadtree ma błąd w pobieraniu elementów z granicami - jeśli podasz jej zapytanie, które otacza niektóre granice węzłów, nie zwróci wszystkich poprawek w węzłach, których dotyka zapytanie. Możesz to łatwo zobaczyć również w „pobieraniu elementów z granicami”, klikając w pobliżu granic węzłów.
Nathan Reed
@NathanReed Wcześniej próbowałem zrozumieć kod, ale nie mam wystarczających umiejętności, aby zrozumieć go bez podstaw koncepcyjnych. Dzięki pseudokodowi Johna McDonalda rozumiem teraz, w jaki sposób prostokąty są wstawiane do węzłów, ale myślę, że nadal nie jestem pewien, jak działa pobieranie. Jeśli chodzi o „odzyskiwanie przedmiotów z granicami” - jestem zupełnie zdezorientowany przykładem. Na przykład, kiedy klikam prostokąt, który czysto pasuje do jednego z najmniejszych węzłów, dlaczego wszystkie te inne prostokąty poza tym węzłem są również podświetlone?
user2736286

Odpowiedzi:

10

Czworokąty zazwyczaj przechowują i pobierają prostokąty. Punkt jest szczególnym przypadkiem, w którym szerokość i wysokość wynoszą zero. Poniższa logika służy do znalezienia domu dla nowych prostokątów w drzewie, zaczynając od węzła głównego:

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

Pamiętaj, że prostokąty można przechowywać na dowolnej głębokości, nie musi to być kwadratowy liść. Jeśli prostokąt styka się z granicą bezpośrednio na poziomie głównym, poczwórny kwadrat zapisze prostokąt.

Na przykład czerwony kwadrat jest narysowanym odpowiednikiem parametrów, które wprowadzam do mojej metody wyszukiwania. Myślę, że skoro ten czerwony kwadrat obejmuje wszystkie cztery węzły, powinien zwrócić każdy obiekt w drzewie quadów! Ale tak nie jest i mam problem z racjonalizacją tego, co powraca.

Podczas zapytania do kwadratu zwraca tylko prostokąty zawarte w zapytaniu. W twoim przykładzie wymuszasz bardziej szczegółowe przeglądanie każdego z 4 podrzędnych quadów, ponieważ czerwony prostokąt zapytania przecina każdy z podrzędnych quadów. Ponieważ dzieci nie mają kolejnych dzieci, każdy z prostokątów w tych dziecięcych quadach zostanie porównany z czerwonym prostokątem. Ponieważ żaden z prostokątów w drzewie nie koliduje z czerwonym prostokątem zapytania, nic nie powinno być zwracane.

Naprawdę zaczynasz dostrzegać zalety czworokątów, gdy masz dużo obiektów i dużo miejsca na istnienie obiektów w porównaniu z obszarami zapytań. W takich przypadkach mały obszar zapytań może szybko wyeliminować rozległe fragmenty drzewa quadów. Tylko quady, które przecinają się z prostokątem zapytania, będą kiedykolwiek oglądane bardziej szczegółowo.

EDYTOWAĆ

Pobieranie odbywa się zwykle w następujący sposób:

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

W twojej bibliotece nie wydaje się jednak sprawdzać, czy zwracane prostokąty przecinają się z zapytaniem, więc musisz to dodać samodzielnie.

John McDonald
źródło
Po dokładniejszym przyjrzeniu się bibliotece biblioteka zwraca listę wszystkich możliwych kandydatów na kolizję dla określonego przez ciebie prostokąta zapytania. Wygląda na to, że Twoim zadaniem jest porównanie prostokąta zapytania z każdym kandydatem, aby sprawdzić, czy rzeczywiście występuje kolizja. Większość bibliotek robi dla ciebie ten ostatni krok i po prostu zwraca rzeczywiste kolizje z obszarem zapytania. Wszystko, co musisz zrobić, to dodać logiki retrievedo metody przycinania listy. Możesz zobaczyć, co robi to nieco jaśniej tutaj: mikechambers.com/html5/javascript/QuadTree/examples/…
John McDonald
1
@ user2736286 Jeśli jeszcze tego nie zauważyłeś, ważne jest, aby zwrócić uwagę na rekurencję w funkcjach Query and Store napisaną przez JohnMcDonald. To nie będzie miało sensu, jeśli nie dostaniesz tej części. W obu przypadkach każda rekurencja wchodzi głębiej w drzewo, tj. Na gałęzie, a na końcu w liście.
TASagent
Dzięki John, twój pseudo-kod jest bardzo pomocny. Kiedy powiesz „if (queryRect przecina się z granicami childNode)”, czy w gruncie rzeczy oznacza to, czy queryRect jest zawarty w granicach - częściowo czy całkowicie? Po prostu chcę być w 100% czysty. Ponadto w omawianym przykładzie „pobierz element według granic” mam obraz wyniku mojego kliknięcia. Pic Niebieska kropka to miejsce, w którym kliknąłem. Dlaczego więc nie wyróżniono tylko dwóch prostokątów w tym węźle? Dlaczego podświetlone są również prostokąty? Myślę, że to mnie naprawdę dezorientuje.
user2736286
Częściowe lub pełne. Jeśli dwa dotykają lub jedno z nich jest całkowicie zawarte w drugim. Chcesz przeczytać wiki na poczwórnych drzewach , ale zrobiłem ci zdjęcie i zakodowałem je kolorami, aby reprezentowały różne granice podrzędne. Wszystkie niebieskie prostokąty przecinają się z granicami pierwszych dziecięcych quadów, następnie magenta jest o jedną warstwę głębiej, a zielona o jedną warstwę jeszcze głębiej. Czerwone prostokąty przecinają kliknięty kwadrat. Twoja biblioteka zwraca wszystkie poprawki na granicach potomnych oraz wszystkie zawarte w ostatnim kwadracie potomnym (czerwony): i.imgur.com/InB8KBj.png
John McDonald
Ok, więc starałem się przeoczyć kod źródłowy lib i w końcu rozumiem zachowanie utkniętych Dzieci, a wszystkie te dodatkowe błędy na moim zdjęciu to po prostu utknięte Dzieci. Początkowo myślałem, że wszelkie rektyfikacje obejmujące wiele węzłów po prostu zostaną skopiowane i wstawione do każdego mniejszego węzła. Teraz zdaję sobie sprawę, że StuckChildren nie wstawia się do węzłów, które obejmuje, ale po prostu pozostaje w jednym zawierającym go węźle, dlatego muszę również sprawdzić wszystkie węzły nadrzędne, a nie tylko najmniejszy węzeł zawierający moje zapytanie. Dzięki za zdjęcie; teraz ma to o wiele więcej sensu :)
user2736286