Converting a massive into a 3 dimensional bitmap

Question:

Problem

I need this massive to serve as an input (for C based arduino).

This is our massive from the example above in the required format:

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

As you can see the above massive uses hex, for example 0xFF in binary is 0b11111111

Which makes sense: imagine bits coming out of your monitor through z axis forming a full line of 8 squares.

If you break up the byte into bits and imagine those bits forming layers (with parallel bits) then you can see that this massive represents the 3D cube (shown above in itroduction). *Alternatively you could visualize it as whole bytes through z axis – you would end up with the 3D cube from introduction either way.

I need a function that would convert the massive such that:

Input:

       [[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]]

Output:

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

Attempt

Below is my attempt:

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)

Output:

['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
['', '', '', '', '', '', '', '']
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

I do understand my mistake here, but I am stuck and cannot think of a neat way to get the desired output.

Edit: * Please consider the layers of the cube to go from bottom to top.
Thus, massive[0] would be the first layer and hence the bottom-most one, whereas massive[7] would be the last layer and hence the top one (when visualised as a cube, see 3D Massive representation in Introduction).

Answers:

Here’s a solution using bitwise operations and then converting integers to hex representation:

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

will output

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: I suggest you use 4 spaces per indentation level instead of 2 (see PEP8 indentation guidline).

Answered By: Alex Bochkarev

Pure python solution

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)

output

['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']
Answered By: Mehmet Güngören

Definitely not the most efficient, but hopefully quite readable and simple solution.

Start with a simple function that converts the indices into the desired layer bitmaps:

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)]

For example, for the first row in massive, you’d get:

[[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]]

This matches your illustration of the layers, and can be used to also create them visually with matplotlib

plt.imshow(bitmap(massive[0]), cmap='gray_r')
plt.show()

First bitmap layer

Or even as a 3D plot using voxels:

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()

enter image description here

Then a small function to add those vertical layers as needed:

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]

Then calling hexaize(massive) returns:

[['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']]

Finally, if you want the exact output as described above (in C-like notation?), then you can chain several replace calls like so:

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

And then calling

print(massive_to_arduino(massive))

produces

{
    {'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'},
};
Answered By: hyit
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.