Как найти соседей узла октодерева, когда дерево упорядочено по коду Мортона

У меня есть октодерево, которое я хочу использовать для поиска пути в трехмерном пространстве. Я использую коды Мортона для упорядочения узлов, чтобы иметь возможность легко находить узлы рядом с любым заданным узлом. Кажется, он отлично работает; любое подмножество узлов, которые я захватываю, сгруппировано относительно близко друг к другу. Где я борюсь, так это в том, чтобы найти эффективный способ получить непосредственных соседей узла.

В более ранней версии дерева оно подразделяло дерево до тех пор, пока все листья не были узлами одинакового размера с размером, который я хотел. Это упростило поиск соседей, поскольку между каждым узлом было постоянное смещение, поэтому я мог просто пересчитать код Мортона в той позиции, в которой я ожидал найти узел, а затем получить узел на основе кода. Но я пытаюсь работать с деревом более эффективно, не разбивая его так сильно в областях, где не так много геометрии (например, где я получил бы 8 пустых дочерних элементов). В результате я больше не знаю, сколько соседей может иметь данный узел, ни размер любого из соседей (кроме степени числа 2).

Моя первоначальная мысль состояла в том, чтобы искать близлежащие узлы в любом направлении в массиве, что сработало бы, но мне пришлось бы использовать слишком широкую сеть, чтобы гарантировать получение всех соседей, и я бы потратил время на оценку тонны узлов. что мне не нужно. Быстрый тест показывает, что даже 50 узлов в любом направлении в массиве не гарантируют получение всех соседей.

Дерево статично, поэтому я мог бы, возможно, сделать однократную генерацию соседей узла, используя описанный выше метод грубой силы, и сохранить соседей; но я боюсь, что это может увеличить начальное время загрузки программы сверх допустимого уровня. Есть ли алгоритм, который может найти соседей в режиме реального времени?

Изменить: вопрос изменен, чтобы уточнить, что я ищу и что пробовал.


person user1265215    schedule 02.11.2016    source источник


Ответы (1)


да.

Не применяйте грубую силу, идите прямо к нужной плитке.

При условии, что у вас есть:

  • Функция Tile toMorton(Point3D location), которая предоставляет плитку с учетом ее реального местоположения.
  • Функция Point3D fromMorton(Tile t), предоставляющая реальные координаты плитки (оба доступны в Википедии). Я верю)

Это простые операции в режиме реального времени.

Наивный подход

Просто конвертируйте в реальный мир, сместите туда и обратно в пространство Мортона:

Tile originalTile = ...
Point3D originalLocation = fromMorton(original);
Point3D offsetLocation = new Point3D(offsetLocation.x + 1, offsetLocation.y, offsetLocation.y);
Tile offsetTile = toMorton(offsetLocation)

Для этого требуется всего несколько арифметических операций, поэтому он уже должен быть пригоден для реального времени (в большей степени, чем для грубой силы).

Оптимизированный подход

Быстрый поиск в Google по району кода Мортона привел меня к очень хорошо написанной записи в блоге от громкость отключена.

Я предоставлю лучшие биты здесь:

для данного (x, y, z), который имеет позицию Мортона p; если мы хотим взглянуть на (x+1,y,z), то величина, на которую p должно увеличиться, Δp, зависит только от x и не зависит от y и z. Та же логика применима и для просмотра y и z.

Это означает, что нам не нужно toMorton / fromMorton все три координаты

Кроме того (хотя ни блог, ни его источник резервного копирования), кажется, что указанное смещение может быть предварительно вычислено с помощью таблицы поиска. Это дает дополнительное ускорение для требуемых возможностей реального времени. Для меня это имеет смысл, учитывая, что алгоритм Мортона четко определен и воспроизводим, поэтому я доверяю этому предположению.

Просто предварительно вычислите таблицу смещений, а затем из начальной плитки используйте эту таблицу для смещения вашей плитки относительно ее реального соседа:

int[] offsetX = [ ... compute once ... ]
int[] offsetY = [ ... compute once ... ]
int[] offsetZ = [ ... compute once ... ]
Tile[] allTiles = ... // Init 3D world
public Tile neighbour(Tile original, Direction dir){
     switch(dir){
     case DIR_X_PLUS: return allTiles[original.chunkID + offsetX[original.chunkID];
     case DIR_X_MINUS: return allTiles[original.chunkID - offsetX[original.chunkID];
     case DIR_Y_PLUS: return allTiles[original.chunkID + offsetY[original.chunkID];
     [... etc...]
     default: return original;
     }
}

Надеюсь это поможет.

person MrBrushy    schedule 07.11.2016
comment
То, что вы описываете, это то, как я делаю это сейчас. Я говорю о том, если соседи не одного размера. Например, в настоящее время каждый узел имеет размер 2x2x2, поэтому, если я ищу соседей сверху, я получаю 9 соседей (непосредственно сверху, север, восток, юг, запад и диагонали). Мне просто нужно сместить на 2 в соответствующих направлениях. Но что, если вместо всех соседей 2x2x2 один из соседей имеет размер 8x8x8, потому что в этой области дерево не делится так сильно. Как я узнаю, что мне нужно отрегулировать смещение или насколько я могу его отрегулировать? Если я что-то упустил в этом объяснении. - person user1265215; 07.11.2016
comment
Хм, должно быть, у меня там мозги пукнули, если я это совсем пропустил. Ответ не стоит, но вы все равно можете сделать это, хотя и на каждом уровне. Для этого нам понадобятся подробности о том, как хранится каждый слой. Применяется ли функция Мортона независимо для каждого слоя? - person MrBrushy; 07.11.2016
comment
Я думаю, я понял вашу метафору «сетки»: вы делаете выборку в массиве Мортона, впереди и позади исходной плитки? Это не может работать, сосед может быть сколь угодно далеко (ну, кроме дерева ограниченного размера, но тогда предел, вероятно, будет близок ко всему массиву.) - person MrBrushy; 08.11.2016
comment
Код Мортона применяется к каждому узлу, даже к узлам, которые содержат другие узлы. Однако в настоящее время узел не имеет своего уровня подразделения. Хотя было бы достаточно просто добавить. Вы предлагаете что-то вроде выполнения смещений для каждого уровня подразделения, которое может быть соседним, и использования комбинации результатов в качестве соседей? - person user1265215; 08.11.2016
comment
Еще не на 100% понятно с подразделением. Узлы разных уровней иерархии могут иметь одно и то же местоположение в реальном мире, но быть разными. Подобно тому, как блок уровня 1 {(0,0)} будет иметь адрес Мортона 0, но так же будет и блок уровня 2 {(0,0),(0,1),(1,0),(1,1)} и т. д. Это означает, что когда вы подразделяете блоки, каждый слой должен иметь свой собственный массив Мортона для его поддержки. Это то, что вы планируете делать? Если так, то да, это то, что я предлагаю, может быть, для нового ответа. - person MrBrushy; 08.11.2016
comment
Я не рассматривал массив Мортона для каждого уровня подразделения, но это имеет смысл. Что насчет подразделения мне нужно уточнить? - person user1265215; 08.11.2016
comment
Ты только что сделал. Да, я думаю, что рекурсивный Мортон - это путь. Вам понадобятся разреженные массивы для хранения самых глубоких уровней, иначе это лишило бы смысла иметь подразделение и не хранить соседей. Мне нужно подумать об этом, но, увы, я буду отсутствовать пару недель. Не стесняйтесь, держите нас в курсе ваших собственных начинаний! - person MrBrushy; 09.11.2016