Скажем, у нас есть массив таких, что: massive = [[] for each in range(8)]
Каждый список в массиве описывает растровое изображение 8x8 таким образом, что:
И каждый элемент в списке описывает, является ли данный «квадрат» черным.
Например, list = [0, 63]
будет означать, что только первый и последний «квадраты» черные.
Массив, определенный в начале, должен описывать куб 8x8x8, где каждый список является «слоем», и, таким образом, 8 слоев образуют трехмерный куб. Поэтому индекс каждого элемента в массиве (который указывает на конкретный список в массиве) сообщает нам слой, на котором находится конкретное растровое изображение.
Теперь рассмотрим следующий массив:
massive = [[63, 62, 61, 60, 59, 58, 57, 56, 48, 40, 32, 24, 16, 8, 0, 1, 2, 3, 4, 5, 6, 7, 15, 23, 31, 39, 47, 55],
[63, 56, 0, 7],
[63, 56, 0, 7],
[35, 36, 27, 56, 28, 0, 7, 63],
[63, 56, 0, 7, 36, 35, 27, 28],
[63, 7, 56, 0],
[7, 0, 56, 63],
[7, 6, 5, 4, 3, 2, 1, 0, 8, 16, 32, 24, 40, 48, 56, 57, 58, 59, 60, 61, 62, 63, 55, 39, 47, 31, 23, 15]]
Вот визуализация всех уникальных растровых изображений в этом массиве:
Обратите внимание на массивные[0] и массивные[7] — первый и последний слои.
Обратите внимание на массивный[3] и массивный[4]
Примечание: массивный[1], массивный[2], массивный[5], массивный[6]
Упомяните, что элементы в этих списках могут быть не по порядку, тем не менее это незначительно, поскольку каждый элемент может только указать, активна ли эта конкретная позиция, и в списке не может быть дубликатов. Таким образом, отсутствие других номеров (от 0 до 63) в списке просто означало бы, что позиция «неактивна».
Вот как массив будет визуализирован в 3D
Мне нужно, чтобы этот массив служил входом (для Arduino на базе C).
Это наш массив из примера выше в нужном формате:
const byte bitmap[8][8] = {
{0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF},
{0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81},
{0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81},
{0x81, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x81},
{0x81, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x81},
{0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81},
{0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81},
{0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF},
};
Как вы можете видеть, в приведенном выше массиве используется шестнадцатеричный код, например, 0xFF в двоичном формате равен 0b11111111.
Что имеет смысл: представьте, что биты выходят из вашего монитора по оси z, образуя полную линию из 8 квадратов.
Если разбить байт на биты и представить, что эти биты образуют слои (с параллельными битами), то можно увидеть, что этот массив представляет собой трехмерный куб (показан выше во введении). * В качестве альтернативы вы можете визуализировать его как целые байты по оси Z - в любом случае вы получите 3D-куб из введения.
Мне нужна функция, которая будет преобразовывать массив так, чтобы:
Вход:
[[63, 62, 61, 60, 59, 58, 57, 56, 48, 40, 32, 24, 16, 8, 0, 1, 2, 3, 4, 5, 6, 7, 15, 23, 31, 39, 47, 55],
[63, 56, 0, 7],
[63, 56, 0, 7],
[35, 36, 27, 56, 28, 0, 7, 63],
[63, 56, 0, 7, 36, 35, 27, 28],
[63, 7, 56, 0],
[7, 0, 56, 63],
[7, 6, 5, 4, 3, 2, 1, 0, 8, 16, 32, 24, 40, 48, 56, 57, 58, 59, 60, 61, 62, 63, 55, 39, 47, 31, 23, 15]]
Выход:
{
{0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF},
{0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81},
{0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81},
{0x81, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x81},
{0x81, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x81},
{0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81},
{0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81},
{0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF},
};
Ниже моя попытка:
massive = [[63, 62, 61, 60, 59, 58, 57, 56, 48, 40, 32, 24, 16, 8, 0, 1, 2, 3, 4, 5, 6, 7, 15, 23, 31, 39, 47, 55],
[63, 56, 0, 7],
[63, 56, 0, 7],
[35, 36, 27, 56, 28, 0, 7, 63],
[63, 56, 0, 7, 36, 35, 27, 28],
[63, 7, 56, 0],
[7, 0, 56, 63],
[7, 6, 5, 4, 3, 2, 1, 0, 8, 16, 32, 24, 40, 48, 56, 57, 58, 59, 60, 61, 62, 63, 55, 39, 47, 31, 23, 15]]
rows, cols = (8, 8)
arr = [['' for i in range(cols)] for j in range(rows)]
arr[0][0] = ''
for row in arr:
print(row)
def convert():
for i in range(0, 64):
for n in range(0,64):
for each in massive:
if i == massive[massive.index(each)][n]:
arr[massive.index(each)][n] = '1'
else:
arr[massive.index(each)][n] = '0'
convert()
for row in arr:
print(row)
Выход:
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
Traceback (most recent call last):
File "main.py", line 28, in <module>
convert()
File "main.py", line 23, in convert
if i == massive[massive.index(each)][n]:
IndexError: list index out of range
Я понимаю свою ошибку здесь, но я застрял и не могу придумать аккуратный способ получить желаемый результат.
Редактировать: * Учтите, что слои куба идут снизу вверх. Таким образом, массив Massive[0] будет первым слоем и, следовательно, самым нижним, тогда как массив[7] будет последним слоем и, следовательно, самым верхним (при визуализации в виде куба см. 3D-представление Massive во Введении).
Вот решение, использующее побитовые операции и затем преобразующее целые числа в шестнадцатеричное представление:
massive = [[63, 62, 61, 60, 59, 58, 57, 56, 48, 40, 32, 24, 16, 8, 0, 1, 2, 3, 4, 5, 6, 7, 15, 23, 31, 39, 47, 55],
[63, 56, 0, 7],
[63, 56, 0, 7],
[35, 36, 27, 56, 28, 0, 7, 63],
[63, 56, 0, 7, 36, 35, 27, 28],
[63, 7, 56, 0],
[7, 0, 56, 63],
[7, 6, 5, 4, 3, 2, 1, 0, 8, 16, 32, 24, 40, 48, 56, 57, 58, 59, 60, 61, 62, 63, 55, 39, 47, 31, 23, 15]]
rows, cols = (8, 8)
arr = [[0 for i in range(cols)] for j in range(rows)]
# Since lower-bit layers go first in massive, we have to traverse massive in reverse order
for layer in reversed(massive):
# Shift each bitmask by one bit
for x in range(rows):
for y in range(cols):
arr[x][y] <<= 1
# Write current layer to the last bit of arr[x][y]
for val in layer:
x, y = val % cols, val // cols
arr[x][y] |= 1
for row in arr:
print(', '.join('0x{0:0{1}X}'.format(x, 2) for x in row))
будет выводить
0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF
0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81
0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81
0x81, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x81
0x81, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x81
0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81
0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81
0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF
PS: я предлагаю вам использовать 4 пробела на уровень отступа вместо 2 (см. Руководство по отступам PEP8).
Обновил мой ответ, чтобы отразить это. На самом деле, использование reversed
здесь было бы лучше, чем [::-1]
, поскольку оно не создает новый список и (возможно) более читабельно.
Чистое решение на питоне
massive = [[63, 62, 61, 60, 59, 58, 57, 56, 48, 40, 32, 24, 16, 8, 0, 1, 2, 3, 4, 5, 6, 7, 15, 23, 31, 39, 47, 55],
[63, 56, 0, 7],
[63, 56, 0, 7],
[35, 36, 27, 56, 28, 0, 7, 63],
[63, 56, 0, 7, 36, 35, 27, 28],
[63, 7, 56, 0],
[7, 0, 56, 63],
[7, 6, 5, 4, 3, 2, 1, 0, 8, 16, 32, 24, 40, 48, 56, 57, 58, 59, 60, 61, 62, 63, 55, 39, 47, 31, 23, 15]]
cube = [[[0] * 8 for _ in range(8)] for _ in range(8)]
for i, row in enumerate(massive):
for cell in row:
cell_y = cell % 8
cell_x = cell // 8
cube[i][cell_x][cell_y] = 1
result = [[''] * 8 for _ in range(8)]
for x in range(8):
for y in range(8):
binary = ''
for z in range(8):
binary += str(cube[x][y][z])
result[x][y] = hex(int(binary, 2))
for row in result:
print(row)
выход
['0xff', '0x81', '0x81', '0x81', '0x81', '0x81', '0x81', '0xff']
['0x81', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x81']
['0x81', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x81']
['0x81', '0x0', '0x0', '0x18', '0x18', '0x0', '0x0', '0x81']
['0x81', '0x0', '0x0', '0x18', '0x18', '0x0', '0x0', '0x81']
['0x81', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x81']
['0x81', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x81']
['0xff', '0x81', '0x81', '0x81', '0x81', '0x81', '0x81', '0xff']
Определенно не самое эффективное, но, надеюсь, вполне читаемое и простое решение.
Начните с простой функции, которая преобразует индексы в требуемые растровые изображения слоя:
def bitmap(indices, side=8):
"""Transform a list of indices to an 8x8 bitmap with those indices turned on"""
indices = set(indices)
return [[int(side*i+j in indices) for j in range(side)] for i in range(side)]
Например, для первой строки в massive
вы получите:
[[1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1]]
Это соответствует вашей иллюстрации слоев и может быть использовано для их визуального создания с помощью matplotlib
--
plt.imshow(bitmap(massive[0]), cmap='gray_r')
plt.show()
Или даже как 3D-график с использованием вокселей:
cube = np.array([bitmap(layer) for layer in massive])
fig, ax = plt.subplots(subplot_kw = {"projection": "3d"})
# Use transpose of `cube` to get the direction right
# (bottom->up rather than left->right)
ax.voxels(cube.T, edgecolor='k')
ax.set(xticklabels=[], yticklabels=[], zticklabels=[])
plt.show()
Затем небольшая функция для добавления этих вертикальных слоев по мере необходимости:
def hexaize(massive, side=8):
"""Adds the values for each column across vertical layers"""
final_map = [[0] * side for _ in range(side)]
# Reverse-iterate over massive since it's given bottom-up and not top-down
for i, layer in enumerate(reversed(massive)):
for j, row in enumerate(bitmap(layer)):
for k, val in enumerate(row):
final_map[i][j] += val*2**k
# Finally convert the added values to hexadecimal
# Use the f-string formatting to ensure upper case and 2-digits
return [[f"0x{val:02X}" for val in row] for row in final_map]
Затем вызов hexaize(massive)
возвращает:
[['0xFF', '0x81', '0x81', '0x81', '0x81', '0x81', '0x81', '0xFF'],
['0x81', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x81'],
['0x81', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x81'],
['0x81', '0x00', '0x00', '0x18', '0x18', '0x00', '0x00', '0x81'],
['0x81', '0x00', '0x00', '0x18', '0x18', '0x00', '0x00', '0x81'],
['0x81', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x81'],
['0x81', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x81'],
['0xFF', '0x81', '0x81', '0x81', '0x81', '0x81', '0x81', '0xFF']]
Наконец, если вам нужен точный вывод, как описано выше (в C-подобной нотации?), то вы можете связать несколько вызовов replace
следующим образом:
def massive_to_arduino(massive, side=8):
"""Converts a massive to Arduino style input"""
# Get the hexa format of massive
in_hex = hexaize(massive, side=side)
# Replace square brackets with curly ones
in_hex = str(in_hex).replace("[", "{").replace("]", "}")
# Break rows to join them with new lines and indentation
in_hex = "},\n ".join(in_hex.split("},"))
# Add new line, indentation, and semicolon to start and end
return in_hex.replace("{{", "{\n {").replace("}}", "},\n};")
А потом звоню
print(massive_to_arduino(massive))
производит
{
{'0xFF', '0x81', '0x81', '0x81', '0x81', '0x81', '0x81', '0xFF'},
{'0x81', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x81'},
{'0x81', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x81'},
{'0x81', '0x00', '0x00', '0x18', '0x18', '0x00', '0x00', '0x81'},
{'0x81', '0x00', '0x00', '0x18', '0x18', '0x00', '0x00', '0x81'},
{'0x81', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x81'},
{'0x81', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x81'},
{'0xFF', '0x81', '0x81', '0x81', '0x81', '0x81', '0x81', '0xFF'},
};
Спасибо за визуализацию. Можно ли изменить его так, чтобы при построении в режиме реального времени (например, при изменении массива) слои отображались снизу вверх, а не слева направо (см. Редактирование в этом сообщении).
@zaѓathᵾѕтѓа Ах да, извините, я пропустил это. Это просто простая операция транспонирования (т.е. вы рисуете cube.T
). Я отредактировал свой ответ для этого. Обновлено: я также добавил reversed
, чтобы соответствовать этому требованию для вывода значений.
Да, нижние слои идут первыми, спасибо.