简体   繁体   中英

Converting a massive into a 3 dimensional bitmap

Introduction

Say we have a massive such that: massive = [[] for each in range(8)]

Each list in massive describes a 8x8 bitmap such that:

And each element in the list describes whether or not a given "square" is black. For example, list = [0, 63] would indicate that only first and last "squares" are black.

The massive defined in the beginning is supposed to describe a 8x8x8 cube, where each list is a "layer" and thus 8 layers form a 3-dimensional cube. Therefore the index of each element in the massive (which indicates to a particular list in the massive) tells us the layer that the particular bitmap is on.

Now, consider the following 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]]

Here is a visualisation of all unique bitmaps in this massive:

Bitmap #1

Notice massive[0] and massive[7] - first and last layers

Bitmap #2

Notice massive[3] and massive[4]

Bitmap #3

Notice: massive[1], massive[2], massive[5], massive[6]

Mention that elements in those lists might not be in order, nevertheless its negligible since each element can only state whether that particular position is active and there can be no duplicates in the list. Thus, absence of other numbers (from 0-63) in the list would simply mean that the position is "inactive".

3D Massive representation

This is how the massive would visualize in 3D


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.

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

for layer in 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

From your question it's unclear what's the direction of the layers though. If lower bits go first in massive then you have to slightly modify one line of the code above and iterate massive in reverse order:

...
for layer in massive[::-1]:
...

PS: I suggest you use 4 spaces per indentation level instead of 2 (see PEP8 indentation guidline ).

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

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

第一个位图图层

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)]
    for i, layer in enumerate(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'},
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM