Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a massive into a 3 dimensional bitmap

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

like image 884
zаѓатhᵾѕтѓа Avatar asked Jan 26 '26 08:01

zаѓатhᵾѕтѓа


1 Answers

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'},
};
like image 169
hyit Avatar answered Jan 28 '26 23:01

hyit



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!