Как сгладить или проиндексировать 3D-массив в 1D-массиве?

Я пытаюсь превратить трехмерный массив в одномерный массив для системы «блоков» в моей игре. Это игра с трехмерными блоками, и в основном я хочу, чтобы система фрагментов была почти идентична системе Minecraft (однако это ни в коем случае не клон Minecraft). В моих предыдущих 2D-играх я обращался к плоскому массиву по следующему алгоритму:

Tiles[x + y * WIDTH]

Однако это явно не работает с 3D, поскольку в нем отсутствует ось Z. Понятия не имею, как реализовать подобный алгоритм в 3D-пространстве. Ширина, высота и глубина - все константы (а ширина равна высоте).

Это просто x + y*WIDTH + Z*DEPTH? Я очень плохо разбираюсь в математике, и я только начинаю программировать в 3D, так что я заблудился: |

PS. Причина этого в том, что я довольно часто зацикливаю и получаю из него материалы по индексу. Я знаю, что одномерные массивы быстрее, чем многомерные (по причинам, которые я не помню: P). Даже если в этом нет необходимости, я хочу добиться максимальной производительности :)


person Community    schedule 09.09.2011    source источник
comment
Правильно ли я говорю, что вы хотите, чтобы трехмерный массив вписывался в одномерный массив?   -  person Dominic K    schedule 10.09.2011
comment
Почему бы вам просто не использовать 3D-массив?   -  person svick    schedule 10.09.2011
comment
@DMan Да, это правильно.   -  person    schedule 10.09.2011


Ответы (10)


Алгоритм в основном такой же. Если у вас есть 3D-массив Original[HEIGHT, WIDTH, DEPTH], вы можете превратить его в Flat[HEIGHT * WIDTH * DEPTH] с помощью

Flat[x + WIDTH * (y + DEPTH * z)] = Original[x, y, z]

Кроме того, вы должны предпочесть массивы массивов многомерным массивам в .NET. Различия в производительности значительны

person Gideon Engelberth    schedule 09.09.2011
comment
Не могли бы вы указать на какой-нибудь источник, обсуждающий различия в производительности? Кроме того, вы не должны основывать свои решения только на производительности. - person svick; 10.09.2011
comment
stackoverflow.com/questions/597720/ и stackoverflow.com/questions/468832/ - person hatchet - done with SOverflow; 10.09.2011
comment
@svick: Некоторые источники можно увидеть в предоставленных ссылках. Мое выступление было лишь отрывком, а не главным предложением. Неровные массивы имеют почти идентичный синтаксис (исходный [x] [y] [z]), но для их инициализации требуется больше работы. Однако выигрыш в производительности может стать весьма заметным (ускорение в 2-5 раз) в зависимости от использования. - person Gideon Engelberth; 10.09.2011
comment
Если ВЫСОТА соответствует размеру Y, она должна быть: Flat[x + WIDTH * (y + HEIGHT * z)] = Original[x, y, z] - person Jonathan Lidbeck; 25.09.2013

Вот решение на Java, которое дает вам и то, и другое:

  • от 3D до 1D
  • от 1D до 3D

Ниже приведена графическая иллюстрация пути, который я выбрал для обхода 3D-матрицы, ячейки пронумерованы в порядке их обхода:

2 примера трехмерных матриц

Функции преобразования:

public int to1D( int x, int y, int z ) {
    return (z * xMax * yMax) + (y * xMax) + x;
}

public int[] to3D( int idx ) {
    final int z = idx / (xMax * yMax);
    idx -= (z * xMax * yMax);
    final int y = idx / xMax;
    final int x = idx % xMax;
    return new int[]{ x, y, z };
}
person Samuel Kerrien    schedule 18.12.2015

Я думаю, что сказанное выше требует небольшой поправки. Допустим, у вас ВЫСОТА 10 и ШИРИНА 90, одномерный массив будет 900. Согласно приведенной выше логике, если вы находитесь на последнем элементе массива 9 + 89 * 89, очевидно, что это больше 900. Правильный алгоритм:

Flat[x + HEIGHT* (y + WIDTH* z)] = Original[x, y, z], assuming Original[HEIGHT,WIDTH,DEPTH] 

По иронии судьбы, если вы выберете HEIGHT> WIDTH, вы не испытаете переполнения, а просто получите безумный результат;)

person Martin    schedule 17.05.2013
comment
Я не могу проголосовать или прокомментировать настоящий правильный ответ, но у Мартина он правильный, текущий выбранный ответ неверен. По сути: данные [x] [y] [z] = data [x + y maxX + z maxX * maxY] - person jking; 24.08.2013
comment
да, текущий ответ неверен, должна быть высота, а не глубина. Мне потребовалось слишком много времени, чтобы понять это, поскольку я впервые использовал неправильный ответ SO, чтобы что-то закодировать ›.‹ - person chilleo; 15.04.2016

x + y*WIDTH + Z*WIDTH*DEPTH. Визуализируйте его как прямоугольное тело: сначала вы двигаетесь по x, затем каждый y представляет собой "линию" длиной width шага, а каждый z является "плоскостью" WIDTH*DEPTH шагов по площади.

person Tom Zych    schedule 09.09.2011

Ты почти там. Вам нужно умножить Z на WIDTH и DEPTH:

Tiles[x + y*WIDTH + Z*WIDTH*DEPTH] = elements[x][y][z]; // or elements[x,y,z]
person dlev    schedule 09.09.2011
comment
Не могли бы вы помочь мне подтвердить, какой шаблон будет для 4D, 5D и т. Д. - person Jamie Nicholl-Shelley; 04.01.2021

TL;DR

Правильный ответ можно написать по-разному, но мне больше всего нравится, когда его можно написать так, чтобы его было очень легко понять и визуализировать. Вот точный ответ:

(width * height * z) + (width * y) + x

TS;DR

Визуализируйте это:

someNumberToRepresentZ + someNumberToRepresentY + someNumberToRepresentX

someNumberToRepresentZ указывает, на какой матрице мы находимся (depth). Чтобы узнать, в какой матрице мы находимся, мы должны знать, насколько велика каждая матрица. Матрица имеет размер 2d как width * height, простая. Возникает вопрос: «сколько матриц находится до того, как я использую матрицу?» Ответ z:

someNumberToRepresentZ = width * height * z

someNumberToRepresentY указывает, в какой строке мы находимся (height). Чтобы узнать, в какой строке мы находимся, мы должны знать, насколько велика каждая строка: каждая строка имеет размер 1d, размер width. Возникает вопрос: «сколько строк перед строкой, в которой я сейчас?». Ответ y:

someNumberToRepresentY = width * y

someNumberToRepresentX указывает, в каком столбце мы находимся (width). Чтобы узнать, в каком столбце мы находимся, мы просто используем x:

someNumberToRepresentX = x

Тогда наша визуализация

someNumberToRepresentZ + someNumberToRepresentY + someNumberToRepresentX

Становится

(width * height * z) + (width * y) + x
person Robert Plummer    schedule 09.07.2018

Прямые и обратные преобразования Самуэля Керриена, приведенные выше, почти верны. Более краткие (основанные на R) карты преобразования включены ниже с примером («a %% b» - это оператор по модулю, представляющий остаток от деления a на b):

dx=5; dy=6; dz=7  # dimensions
x1=1; y1=2; z1=3  # 3D point example
I = dx*dy*z1+dx*y1+x1; I  # corresponding 2D index
# [1] 101
x= I %% dx; x  # inverse transform recovering the x index
# [1] 1
y = ((I - x)/dx) %% dy; y  # inverse transform recovering the y index
# [1] 2
z= (I-x -dx*y)/(dx*dy); z  # inverse transform recovering the z index
# [1] 3

Обратите внимание на операторы деления (/) и модуля (%%).

person Joe Doe    schedule 18.02.2020

Чтобы лучше понять описание 3D-массива в 1D-массиве (я думаю, что под глубиной в лучшем ответе подразумевается размер Y)

IndexArray = x + y * InSizeX + z * InSizeX * InSizeY;

IndexArray = x + InSizeX * (y + z * InSizeY);
person Evalds Urtans    schedule 07.02.2014

Правильный алгоритм:

Flat[ x * height * depth + y * depth + z ] = elements[x][y][z] 
where [WIDTH][HEIGHT][DEPTH]
person Beta-Logics    schedule 13.03.2018
comment
Протестировано почти все другие ответы, только этот переводит вложенные циклы for (x ‹width, y‹ height, z ‹depth) в индексы массивов от 0 до width * height * depth (упорядочено) - person Kuba Chrabański; 10.01.2020

m [x] [y] [z] = данные [xYZ + yZ + z]

x-picture:
0-YZ
.
.
x-YZ

y-picture 

0-Z
.
.
.
y-Z


summing up, it should be : targetX*YZ + targetY*Z + targetZ  
person Faiq arif    schedule 11.03.2020