Як фігури (прямокутники) працюють у квадратиках?


10

Мені сказали, що квадратичне дерево є ідеальною структурою даних для моєї гри, але у мене виникають проблеми з розумінням того, як саме фігури працюють у квадратичних деревах.

Я роблю це в JavaScript, але я думаю, що ці питання можуть стосуватися чотирьох дерев будь-якою мовою.

Я думаю, що я здебільшого розумію, як основні (x, y) точки і вставлення точок працюють у квадратичних деревах, і що я можу це зробити на папері.

Ось JSfiddle мого експерименту з точками.

точки квадрового дерева

Крім одного випадку, мої тести з балами працюють як очікувалося.

Але моє плутанина починається, коли в них фігури, такі як прямокутники. Коли ви виймаєте з дерева квадратиків фігури, чи перевіряє він кожну точку фігури та в які вузли вони потрапляють? І як взагалі працюють вставки фігур, якщо вони приймають параметри (x, y, ширина, висота) для кожної форми? Чи використовує він ширину / висоту від початкової точки для обчислення інших кутових точок, які потім розподіляються у відповідні вузли? Якщо вставлена ​​форма охоплює чотири вузли, чи дані цієї форми зберігаються у всіх чотирьох вузлах?

І коли метод пошуку приймає фігуру як параметр (x, y, ширина, висота), що насправді відбувається? Чи спочатку бачимо, до яких вузлів поділятиметься форма, якби її вставити, а потім витягнути всі об'єкти цих вузлів?

У мене є JSfiddle, що працює з фігурами , але я сильно збентежений у результатах мого тестування. Я отримую повторювані об’єкти!

форми квадрового дерева

Наприклад, червоний квадрат - це намальований еквівалент параметрів, які я ввожу в метод пошуку. Я б подумав, що оскільки ця червона площа охоплює всі чотири вузли, вона повинна повертати кожен предмет у квадратичному дереві! Але це не так, і у мене виникають проблеми з раціоналізацією того, що він повертає. У мене є низка інших тестів, які зараз коментуються, але ви можете відміняти коментарі та запустити їх, щоб побачити більш заплутані результати!

Як сказати, якщо я хочу повернути всі точки в квадратичному дереві, як би це зробити? Метод отримання, використовуючи форму на весь розмір меж? Наприклад, отримати (0, 0, canvas.width, canvas.height)?

Бібліотеку JavaScript QuadTree, яку я використовую, згадували різні джерела, тому я вважаю, що реальна реалізація є правильною та надійною.

Я думаю, що багато моєї плутанини може бути наслідком нерозуміння термінології квадратичного дерева. Мовляв, чому вони кажуть межі замість розмірів, коли "точка" має також параметри ширини / висоти? Це питання конвенції / короткої руки чи це абсолютно різні поняття?

Дякую за ваш час!


Вони зберігаються в квадратичному дереві, як правило, за своїм положенням. Зазвичай вони знаходяться в центрі, але можуть бути в кутку, як ви визначили. Ваше питання може бути дублікатом цього: QuadTree: зберігати лише точки, або регіони?
MichaelHouse

Я читав це питання, але досі не розумію повністю відповідей :(. "Ви повинні зберігати його в найменшому вузлі, який повністю містить його, навіть якщо це перевищує ємність (використовувати контейнер, що змінює зміну)". - Коли він каже, що найменший вузол, який КОМПЛЕКТНО містить його, якщо об’єкт дуже великий, чи не буде цей вузол часто нелистовим вузлом? Як у вузлі вище, який складається лише з інших вузлів? Це не здається правильним мені, оскільки це означатиме, що вузол, що містить, міститиме 1 аркуш, а 4 менших вузла
user2736286

1
@ user2736286 Якщо ви подивитеся на код для чотирикутної вкладки (вона не дуже довга), ви можете бачити, як вона зберігає прямокутники у вузлах вищого рівня, щоб не захищати їх від меж вузла. Дивіться посилання на _stuckChildrenполе в коді. Ви також можете бачити це у зразку "вилучення елементів з межею" - він завжди виділяє червоним кольором вузли, що перетинають межі вузлів, на які ви натиснули, аж до кореневого вузла.
Натан Рід

@ user2736286 Також, схоже, в лінійці квадратури є помилка у пошуку елементів з межами - якщо ви дасте їй пряму запиту, яка обшиває деякі межі вузла, вона не повертає всіх ректів у вузлах, до яких запит торкається. Це можна легко побачити і в "витягуванні елементів з межею", клацнувши біля меж вузла.
Натан Рід

@NathanReed Раніше я намагався зрозуміти код, але я недостатньо досвідчений, щоб зрозуміти його без концептуальної основи. Завдяки псевдо-коду Джона Макдональдса я тепер розумію, як прямокутники вставляються у вузли, але я думаю, що мені все ще незрозуміло, як працює пошук. Що стосується "отримання предметів з межею", - я просто переплутаний прикладом. Наприклад, коли я клацаю на пряму, яка чітко вписується в один з найменших вузлів, чому всі ці інші прямолінійні вузли також виділяються поза цим вузлом?
користувач2736286

Відповіді:


10

Як правило, квадрати зберігають і витягують прямокутники. Точка - це конкретний випадок, коли ширина і висота дорівнюють нулю. Наступна логіка використовується для пошуку будинку для нових прямокутників на дереві, починаючи з кореневого вузла:

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

Зауважте, що прямокутники можна зберігати на будь-якій глибині, це не повинно бути листовим квадратом. Якщо прямокутник обводить кордон прямо на рівні кореня, кореневий квадратик буде зберігати прямокутник.

Наприклад, червоний квадрат - це намальований еквівалент параметрів, які я ввожу в метод пошуку. Я б подумав, що оскільки ця червона площа охоплює всі чотири вузли, вона повинна повертати кожен предмет у квадратичному дереві! Але це не так, і у мене виникають проблеми з раціоналізацією того, що він повертає.

Під час запиту квадрата воно поверне лише прямокутники, які містяться в запиті. У вашому прикладі ви змушуєте детальніше переглянути кожен 4 дочірніх квадратиків, оскільки червоний прямокутник запиту перетинає кожен дочірній квадратик. Оскільки у дітей немає подальших дітей, кожен із прямокутників у цих дочірніх квадратиках буде порівнюватися з червоним прямокутником. Оскільки жоден з прямокутників у дереві не стикається з червоним прямокутником запиту, нічого не слід повертати.

Ви дійсно починаєте бачити переваги чотиривіршів, коли у вас багато об’єктів і багато місця для об'єктів, які існують у порівнянні з областями (запитами). У цих випадках невелика область запиту може швидко усунути величезні шматки квадрового дерева з розгляду. Тільки квадрати, які перетинаються з прямокутником запиту, колись будуть розглянуті більш докладно.

EDIT

Отримання зазвичай проводиться так:

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

Однак у вашій бібліотеці, схоже, не перевіряється, чи перетинаються прямокутники, які повертаються, із запитом, тому вам потрібно буде додати їх самостійно.


Переглянувши Бібліотеку більш докладно, Ваша бібліотека повертає список усіх можливих кандидатів зіткнення для вказаного Вами прямокутника запиту. Видається, що ваша робота порівнює ваш прямокутник запиту з кожним кандидатом, щоб побачити, чи є справжнє зіткнення. Більшість бібліотек робить цей останній крок для вас і просто повертає фактичні зіткнення з областю запитів. Отже, все, що вам потрібно зробити, - це додати трохи логіки у ваш retrieveметод, щоб обрізати список. Ви можете побачити, що це робить трохи чіткіше тут: mikechambers.com/html5/javascript/QuadTree/examples/…
Джон Макдональд,

1
@ user2736286 Якщо ви цього не зробили, важливо взяти до уваги рекурсію у функціях Query and Store, про яку написав JohnMcDonald. Це не матиме великого сенсу, якщо ви не отримаєте цю частину. Для обох кожна рекурсія заглиблюється в дерево, тобто на гілки і, нарешті, в листя.
ТАСагент

Дякую Джон, ваш псевдо-код дуже корисний. Коли ви говорите "if (queryRect перетинається з дочірніми межами)", це в основному означає, якщо queryRect міститься в межах - частково або повністю? Просто хочу бути 100% чітким. Крім того, у прикладі "отримання елемента за межами", який обговорюється, у мене є зображення результату мого клацання. Pic Синя крапка - це те, де я натиснув. То чому б не виділено лише два ректанти всередині цього вузла? Чому також далеко виділяються прямокутники? Я думаю, що це нас справді бентежить.
користувач2736286

Часткова або повна. Якщо два дотику, або будь-який повністю міститься іншим. Ви хочете прочитати вікі на Quadtrees , але я взяв вашу картинку і кольором її зашифрував, щоб зобразити різні дитячі межі. Усі сині прямокутники перетинаються з межами першої дочірньої квадратики, тоді пурпурний шар на один шар глибший, а зелений ще один шар глибший. Червоні прямокутники перетинаються з натиснутою квадратиком. Бібліотека повертає всі ректи на дочірні межі плюс усі вміщені в остаточному дочірньому квадратику (червоний): i.imgur.com/InB8KBj.png
Джон Макдональд,

Гаразд, тому я постарався зробити все можливе, щоб перейти через вихідний код lib, і я нарешті зрозумів поведінку stuckChildren, і що всі ці зайві ректи на моїй картинці - це просто stuckChildren. Спочатку я думав, що у будь-яких прямолінійних колах, що охоплюють декілька вузлів, просто дублюватимуться і вставлятись у кожен менший вузол. Тепер я розумію, що stuckChildren не вставляється у вузли, які він охоплює, а просто залишається в одному вузлі, що містить його, тому я повинен також перевірити всі батьківські вузли, а не лише найменший вузол, що містить мою запит. Дякую за фото; зараз це має набагато більше сенсу :)
користувач2736286
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.