Blind agitation series~
preface
NumPy is the basic package of Python scientific computing. It is a python library that provides multidimensional array objects, various derived objects (such as mask arrays and matrices), and various routines for fast operation of arrays, including mathematics, logic, shape operation, sorting, selection, I/O, discrete Fourier transform, basic linear algebra, basic statistical operations, random simulation, etc.
github
Official documents
Recently, there was a demand for palletizing planning in the project. The three-dimensional array in Numpy is particularly easy to use, so I made a stir.
Then I saw the magic cube I bought two years ago on the table. I haven't played for a long time. As soon as my mind is hot, I want to draw a magic cube with Numpy!
Fuck!
Here, we choose to use Matplotlib as the visualization tool
Matplotlib GitHub
Matplotlib official documentation
Construction voxel
In order to make a magic cube, we mainly use voxels, a function in Matplotlib
voxels([x, y, z, ]/, filled, facecolors=None, edgecolors=None, **kwargs) Draw a set of fill voxels All voxels are drawn as 1 on the axis x1x1 Cube, filled[0, 0, 0]of lower corner At the origin. Occluded faces are no longer drawn.
Take 3x3x3 cube as an example:
import matplotlib.pyplot as plt import numpy as np # Prepare a set of voxel coordinates n_voxels = np.ones((3, 3, 3), dtype=bool) # draw ax = plt.figure().add_subplot(projection='3d') ax.voxels(n_voxels) plt.show()
It can be seen that although there are 3x3x3 voxels, there is no gap between voxels, which doesn't look very beautiful.
Make gap effect
In order to make a gap between voxels, you can up sample 3x3x3 voxels, that is, build a 5x5x5 voxel, so that the voxel in the middle of the two voxels is not displayed in each dimension, which can produce the effect of gap.
size = np.array(n_voxels.shape) * 2 filled_2 = np.zeros(size - 1, dtype=n_voxels.dtype) filled_2[::2, ::2, ::2] = n_voxels ax = plt.figure().add_subplot(projection='3d') ax.voxels(filled_2) plt.show()
In this way, there is a gap, but the gap is too large. At this time, you can use the optional parameters [x, y, z] of the voxels function to control the vertex position of each voxel, and then control the size of the gap
# lessen a gap # Building voxels vertex control mesh # x. Matrices with y and Z being 6x6x6 are voxels' grids # //2 is to convert the index range from [0 1 2 3 4 5] to [0 0 0 1 1 2 2], so that the x,y,z range returns to 0 ~ 3 x, y, z = np.indices(np.array(filled_2.shape) + 1).astype(float) // 2 x[1::2, :, :] += 0.95 y[:, 1::2, :] += 0.95 z[:, :, 1::2] += 0.95
In this way, the gap looks almost the same. Next, add color to the six faces of the cube.
Give each face a different color
Since only one color can be given to each voxel as a whole, and different colors cannot be assigned to different faces of a voxel, in order to realize different colors for no more than six faces, the matrix of 3x3x3 can only be changed to 5x5x5, and the thickness of the outermost layer of voxels can be set to be a little smaller, similar to the face, and then color.
import matplotlib.pyplot as plt import numpy as np # Prepare some coordinates n_voxels = np.ones((5, 5, 5), dtype=bool) # Generate gap size = np.array(n_voxels.shape) * 2 filled_2 = np.zeros(size - 1, dtype=n_voxels.dtype) filled_2[::2, ::2, ::2] = n_voxels # lessen a gap # Building voxels vertex control mesh # x. Y and Z are 6x6x8 matrices, voxels meshes, 3x3x4 small squares, and 6x6x8 vertices in total. # Here / / 2 is the essence. Convert the index range from [0 1 2 3 4 5] to [0 0 1 1 2 2], so that the vertex range of each block can be set separately x, y, z = np.indices(np.array(filled_2.shape) + 1).astype(float) //2 # 3x6x6x8, where x, y and Z are 6x6x8 x[1::2, :, :] += 0.95 y[:, 1::2, :] += 0.95 z[:, :, 1::2] += 0.95 # Modify the thickness of the outermost voxel to use as six faces x[0, :, :] += 0.94 y[:, 0, :] += 0.94 z[:, :, 0] += 0.94 x[-1, :, :] -= 0.94 y[:, -1, :] -= 0.94 z[:, :, -1] -= 0.94 # Remove leftovers filled_2[0, 0, :] = 0 filled_2[0, -1, :] = 0 filled_2[-1, 0, :] = 0 filled_2[-1, -1, :] = 0 filled_2[:, 0, 0] = 0 filled_2[:, 0, -1] = 0 filled_2[:, -1, 0] = 0 filled_2[:, -1, -1] = 0 filled_2[0, :, 0] = 0 filled_2[0, :, -1] = 0 filled_2[-1, :, 0] = 0 filled_2[-1, :, -1] = 0
Then it is to give six faces different colors.
Six directions: up, down, left, right, front and back
Six colors: yellow, white, orange, red, blue and green
The initial form of the cube is: Upper Yellow, lower white, left orange, right red, front blue and back green.
Reference: color Daquan https://www.5tu.cn/colors/yansebiao.html
# Give different colors to the six sides of the cube colors = np.array(['#ffd400', "#fffffb", "#f47920", "#d71345", "#145b7d", "#45b97c"]) facecolors = np.full(filled_2.shape, '#77787b') # Set a gray tone facecolors[:, :, -1] = colors[0] facecolors[:, :, 0] = colors[1] facecolors[:, 0, :] = colors[2] facecolors[:, -1, :] = colors[3] facecolors[0, :, :] = colors[4] facecolors[-1, :, :] = colors[5]
Complete code
The complete code is as follows:
# -*- coding: utf-8 -*- # @Time : DATE:2021/8/29 # @Author : yan # @Email : 1792659158@qq.com # @File : blogDemo.py import matplotlib.pyplot as plt import numpy as np def generate_rubik_cube(nx, ny, nz): """ Generate a magic cube of the specified size according to the input :param nx: :param ny: :param nz: :return: """ # Prepare some coordinates n_voxels = np.ones((nx + 2, ny + 2, nz + 2), dtype=bool) # Generate gap size = np.array(n_voxels.shape) * 2 filled_2 = np.zeros(size - 1, dtype=n_voxels.dtype) filled_2[::2, ::2, ::2] = n_voxels # lessen a gap # Building voxels vertex control mesh # x. Y and Z are 6x6x8 matrices, voxels meshes, 3x3x4 small squares, and 6x6x8 vertices in total. # Here / / 2 is the essence. Convert the index range from [0 1 2 3 4 5] to [0 0 1 1 2 2], so that the vertex range of each block can be set separately x, y, z = np.indices(np.array(filled_2.shape) + 1).astype(float) // 2 # 3x6x6x8, where x, y and Z are 6x6x8 x[1::2, :, :] += 0.95 y[:, 1::2, :] += 0.95 z[:, :, 1::2] += 0.95 # Modify the outermost face x[0, :, :] += 0.94 y[:, 0, :] += 0.94 z[:, :, 0] += 0.94 x[-1, :, :] -= 0.94 y[:, -1, :] -= 0.94 z[:, :, -1] -= 0.94 # Remove leftovers filled_2[0, 0, :] = 0 filled_2[0, -1, :] = 0 filled_2[-1, 0, :] = 0 filled_2[-1, -1, :] = 0 filled_2[:, 0, 0] = 0 filled_2[:, 0, -1] = 0 filled_2[:, -1, 0] = 0 filled_2[:, -1, -1] = 0 filled_2[0, :, 0] = 0 filled_2[0, :, -1] = 0 filled_2[-1, :, 0] = 0 filled_2[-1, :, -1] = 0 # Give different colors to the six sides of the cube colors = np.array(['#ffd400', "#fffffb", "#f47920", "#d71345", "#145b7d", "#45b97c"]) facecolors = np.full(filled_2.shape, '#77787b') # Set a gray tone # facecolors = np.zeros(filled_2.shape, dtype='U7') facecolors[:, :, -1] = colors[0] # Upper Yellow facecolors[:, :, 0] = colors[1] # Lower white facecolors[:, 0, :] = colors[2] # Left orange facecolors[:, -1, :] = colors[3] # Right red facecolors[0, :, :] = colors[4] # Front blue facecolors[-1, :, :] = colors[5] # Post green ax = plt.figure().add_subplot(projection='3d') ax.voxels(x, y, z, filled_2, facecolors=facecolors) plt.show() if __name__ == '__main__': generate_rubik_cube(3, 3, 3)
Magic squares of different sizes can be generated according to the input:
4x4x4:
6x6x6
Even 4x4x6, but this is not the magic cube we usually play~