# Convert a 3D NumPy array of voxels to an STL file

Given a 3D boolean array representing voxels, how can it be converted to a 3D-printer-ready file?

The end-goal I would like to achieve is to print the 3D shape that the numpy array represents (True coding for fill this voxel, False for leave it empty).

For example, the array

[
[
[T, T, T],
[T, F, T],
[T, T, T]
],
[
[T, F, T],
[F, F, F],
[T, F, T]
],
[
[T, T, T],
[T, F, T],
[T, T, T]
]
]


would encode a level-1 Menger sponge.

• Seems like OpenSCAD would be suitable for this. Jun 9, 2019 at 15:03

## 3 Answers

I agree with the use of OpenSCAD, but since it is difficult to program in OpenSCAD, I would use SolidPython, which is a front end for OpenSCAD with the full programming capability of Python.

In the alternative, you could use any programming language to decode your arrays and generate the OpenSCAD code for the little network of cubes (or voxels).

The final possibility is to generate an STL file directly. I've helped someone do this, but we found the rules to be a little non-intuitive. We used mesh tools to check out results, both by looking for error messages, and by displaying the result to see if it looked as we intended it to look.

• It's also possible to generate STL files in Python using numpy-stl. Jul 3, 2019 at 20:38
• An OpenSCAD approach will be fine if you're going for a low-res minecraft style model with a smallish number of cubes. If the voxels are supposed to approximate a smooth surface at the printer's best resolution, then OpenSCAD will take days to render it out. There has got to be a better way to do this! Nov 10, 2019 at 2:41

Try voxelfuse.

    from voxelfuse.voxel_model import VoxelModel
from voxelfuse.mesh import Mesh
from voxelfuse.primitives import generateMaterials

if __name__=='__main__':
sponge = [
[
[1, 1, 1],
[1, 0, 1],
[1, 1, 1]
],
[
[1, 0, 1],
[0, 0, 0],
[1, 0, 1]
],
[
[1, 1, 1],
[1, 0, 1],
[1, 1, 1]
]
]

model = VoxelModel(sponge, generateMaterials(4))  #4 is aluminium.
mesh = Mesh.fromVoxelModel(model)
mesh.export('mesh.stl')

• It's great that you posted an answer, with a code example, thereby expanding upon your comment. However, I can't help wondering whether a little explanation of the code would help significantly, i.e. a brief detail about each line (especially the last three lines). Just to save people having to go off and google VoxelModel in order to understand what is happening in the code. Jun 5, 2021 at 2:15
• @greenonline, sometimes I have little time and my answers are rushed- you are correct that this was minimal. But Python is (generally) so easy to install packages, ang Google is our friend for more details, in this case I thought maybe it would help the questioner. I would also have offered more had I had more experience with voxelfuse - but I generally use other modules for my 3D work.. Jun 5, 2021 at 10:15

You can try mayavi.mlab:

## Usage

from mayavi import mlab
import numpy as np

def draw3d_mayavi(array, path):
mlab.contour3d(array.astype(np.int32)) # a window would pop up
mlab.savefig(path)
mlab.clf() # clear the scene to generate a new one


mayavi's recontruction is meant for generating 3D heatmap models of the array, so you have to put in a numeric one with 1s and 0s.

## Note

There are some drawbacks:

1. A window will pop out, you have to clear it in your code if you want to make multiple models.

2. The model reconstructed is .obj and can be very large. If you look closer at the model, you'll see that on the boder the mesh gets 3 layers. I guess the program assumes there to be some gradient.

3. The contour3d function can set line_width, but I don't see any sense of using it for binary data.

Yet, mayavi is very quick, at least compared with voxelfuse. Maybe some post-processing is needed to solve the size problem.

## Doc

This function also enables setting color and opacity, etc. See Plotting functions - contour3d:

### contour3d

mayavi.mlab.contour3d(*args, **kwargs)

Plots iso-surfaces for a 3D volume of data supplied as arguments.

Function signatures:

contour3d(scalars, ...) contour3d(x, y, z, scalars, ...) scalars is a 3D numpy arrays giving the data on a grid.

If 4 arrays, (x, y, z, scalars) are passed, the 3 first arrays give the position, and the last the scalar value. The x, y and z arrays are then supposed to have been generated by numpy.mgrid, in other words, they are 3D arrays, with positions lying on a 3D orthogonal and regularly spaced grid with nearest neighbor in space matching nearest neighbor in the array. The function builds a scalar field assuming the points are regularly spaced.

Keyword arguments:

• color the color of the vtk object. Overides the colormap, if any, when specified. This is specified as a triplet of float ranging from 0 to 1, eg (1, 1, 1) for white.

• colormap type of colormap to use.

• contours Integer/list specifying number/list of contours. Specifying a list of values will only give the requested contours asked for.

• extent [xmin, xmax, ymin, ymax, zmin, zmax] Default is the x, y, z arrays extent. Use this to change the extent of the object created.

• figure Figure to populate.

• line_width The width of the lines, if any used. Must be a float. Default: 2.0

• name the name of the vtk object created.

• opacity The overall opacity of the vtk object. Must be a float. Default: 1.0

• reset_zoom Reset the zoom to accomodate the data newly added to the scene. Defaults to True.

• transparent make the opacity of the actor depend on the scalar.

• vmax vmax is used to scale the colormap. If None, the max of the data will be used

• vmin vmin is used to scale the colormap. If None, the min of the data will be used

Example (run in ipython --gui=qt, or in the mayavi2 interactive shell, see Running mlab scripts for more info):


def test_contour3d():
x, y, z = np.ogrid[-5:5:64j, -5:5:64j, -5:5:64j]

scalars = x * x * 0.5 + y * y + z * z * 2.0

obj = contour3d(scalars, contours=4, transparent=True)
return obj 
`
• Welcome to 3D Printing SE and thank you for your contribution. Could you summarize the "Doc" you linked to since links can "die" over time? Also, when you get a chance, please take the tour to understand how the site works and how it is different than others. Jun 4, 2021 at 13:06