Contributing:

Quickstart

importnumpyfromstlimportmesh# Using an existing stl file:your_mesh=mesh.Mesh.from_file('some_file.stl')# Or creating a new mesh (make sure not to overwrite the `mesh` import by# naming it `mesh`):VERTICE_COUNT=100data=numpy.zeros(VERTICE_COUNT,dtype=mesh.Mesh.dtype)your_mesh=mesh.Mesh(data,remove_empty_areas=False)# The mesh normals (calculated automatically)your_mesh.normals# The mesh vectorsyour_mesh.v0,your_mesh.v1,your_mesh.v2# Accessing individual points (concatenation of v0, v1 and v2 in triplets)assert(your_mesh.points[0][0:3]==your_mesh.v0[0]).all()assert(your_mesh.points[0][3:6]==your_mesh.v1[0]).all()assert(your_mesh.points[0][6:9]==your_mesh.v2[0]).all()assert(your_mesh.points[1][0:3]==your_mesh.v0[1]).all()your_mesh.save('new_stl_file.stl')

fromstlimportmeshfrommpl_toolkitsimportmplot3dfrommatplotlibimportpyplot# Create a new plotfigure=pyplot.figure()axes=mplot3d.Axes3D(figure)# Load the STL files and add the vectors to the plotyour_mesh=mesh.Mesh.from_file('tests/stl_binary/HalfDonut.stl')axes.add_collection3d(mplot3d.art3d.Poly3DCollection(your_mesh.vectors))# Auto scale to the mesh sizescale=your_mesh.points.flatten(-1)axes.auto_scale_xyz(scale,scale,scale)# Show the plot to the screenpyplot.show()

Modifying Mesh objects

fromstlimportmeshimportmathimportnumpy# Create 3 faces of a cubedata=numpy.zeros(6,dtype=mesh.Mesh.dtype)# Top of the cubedata['vectors'][0]=numpy.array([[0,1,1],[1,0,1],[0,0,1]])data['vectors'][1]=numpy.array([[1,0,1],[0,1,1],[1,1,1]])# Right facedata['vectors'][2]=numpy.array([[1,0,0],[1,0,1],[1,1,0]])data['vectors'][3]=numpy.array([[1,1,1],[1,0,1],[1,1,0]])# Left facedata['vectors'][4]=numpy.array([[0,0,0],[1,0,0],[1,0,1]])data['vectors'][5]=numpy.array([[0,0,0],[0,0,1],[1,0,1]])# Since the cube faces are from 0 to 1 we can move it to the middle by# substracting .5data['vectors']-=.5# Generate 4 different meshes so we can rotate them latermeshes=[mesh.Mesh(data.copy())for_inrange(4)]# Rotate 90 degrees over the Y axismeshes[0].rotate([0.0,0.5,0.0],math.radians(90))# Translate 2 points over the X axismeshes[1].x+=2# Rotate 90 degrees over the X axismeshes[2].rotate([0.5,0.0,0.0],math.radians(90))# Translate 2 points over the X and Y pointsmeshes[2].x+=2meshes[2].y+=2# Rotate 90 degrees over the X and Y axismeshes[3].rotate([0.5,0.0,0.0],math.radians(90))meshes[3].rotate([0.0,0.5,0.0],math.radians(90))# Translate 2 points over the Y axismeshes[3].y+=2# Optionally render the rotated cube facesfrommatplotlibimportpyplotfrommpl_toolkitsimportmplot3d# Create a new plotfigure=pyplot.figure()axes=mplot3d.Axes3D(figure)# Render the cube facesforminmeshes:axes.add_collection3d(mplot3d.art3d.Poly3DCollection(m.vectors))# Auto scale to the mesh sizescale=numpy.concatenate([m.pointsforminmeshes]).flatten(-1)axes.auto_scale_xyz(scale,scale,scale)# Show the plot to the screenpyplot.show()

Extending Mesh objects

fromstlimportmeshimportmathimportnumpy# Create 3 faces of a cubedata=numpy.zeros(6,dtype=mesh.Mesh.dtype)# Top of the cubedata['vectors'][0]=numpy.array([[0,1,1],[1,0,1],[0,0,1]])data['vectors'][1]=numpy.array([[1,0,1],[0,1,1],[1,1,1]])# Right facedata['vectors'][2]=numpy.array([[1,0,0],[1,0,1],[1,1,0]])data['vectors'][3]=numpy.array([[1,1,1],[1,0,1],[1,1,0]])# Left facedata['vectors'][4]=numpy.array([[0,0,0],[1,0,0],[1,0,1]])data['vectors'][5]=numpy.array([[0,0,0],[0,0,1],[1,0,1]])# Since the cube faces are from 0 to 1 we can move it to the middle by# substracting .5data['vectors']-=.5cube_back=mesh.Mesh(data.copy())cube_front=mesh.Mesh(data.copy())# Rotate 90 degrees over the X axis followed by the Y axis followed by the# X axiscube_back.rotate([0.5,0.0,0.0],math.radians(90))cube_back.rotate([0.0,0.5,0.0],math.radians(90))cube_back.rotate([0.5,0.0,0.0],math.radians(90))cube=mesh.Mesh(numpy.concatenate([cube_back.data.copy(),cube_front.data.copy(),]))# Optionally render the rotated cube facesfrommatplotlibimportpyplotfrommpl_toolkitsimportmplot3d# Create a new plotfigure=pyplot.figure()axes=mplot3d.Axes3D(figure)# Render the cubeaxes.add_collection3d(mplot3d.art3d.Poly3DCollection(cube.vectors))# Auto scale to the mesh sizescale=cube_back.points.flatten(-1)axes.auto_scale_xyz(scale,scale,scale)# Show the plot to the screenpyplot.show()

Creating Mesh objects from a list of vertices and faces

importnumpyasnpfromstlimportmesh# Define the 8 vertices of the cubevertices=np.array([\
[-1,-1,-1],[+1,-1,-1],[+1,+1,-1],[-1,+1,-1],[-1,-1,+1],[+1,-1,+1],[+1,+1,+1],[-1,+1,+1]])# Define the 12 triangles composing the cubefaces=np.array([\
[0,3,1],[1,3,2],[0,4,7],[0,7,3],[4,5,6],[4,6,7],[5,1,2],[5,2,6],[2,3,6],[3,7,6],[0,1,5],[0,5,4]])# Create the meshcube=mesh.Mesh(np.zeros(faces.shape[0],dtype=mesh.Mesh.dtype))fori,finenumerate(faces):forjinrange(3):cube.vectors[i][j]=vertices[f[j],:]# Write the mesh to file "cube.stl"cube.save('cube.stl')

Combining multiple STL files

importmathimportstlfromstlimportmeshimportnumpy# find the max dimensions, so we can know the bounding box, getting the height,# width, length (because these are the step size)...deffind_mins_maxs(obj):minx=maxx=miny=maxy=minz=maxz=Noneforpinobj.points:# p contains (x, y, z)ifminxisNone:minx=p[stl.Dimension.X]maxx=p[stl.Dimension.X]miny=p[stl.Dimension.Y]maxy=p[stl.Dimension.Y]minz=p[stl.Dimension.Z]maxz=p[stl.Dimension.Z]else:maxx=max(p[stl.Dimension.X],maxx)minx=min(p[stl.Dimension.X],minx)maxy=max(p[stl.Dimension.Y],maxy)miny=min(p[stl.Dimension.Y],miny)maxz=max(p[stl.Dimension.Z],maxz)minz=min(p[stl.Dimension.Z],minz)returnminx,maxx,miny,maxy,minz,maxzdeftranslate(_solid,step,padding,multiplier,axis):if'x'==axis:items=0,3,6elif'y'==axis:items=1,4,7elif'z'==axis:items=2,5,8else:raiseRuntimeError('Unknown axis %r, expected x, y or z'%axis)# _solid.points.shape == [:, ((x, y, z), (x, y, z), (x, y, z))]_solid.points[:,items]+=(step*multiplier)+(padding*multiplier)defcopy_obj(obj,dims,num_rows,num_cols,num_layers):w,l,h=dimscopies=[]forlayerinrange(num_layers):forrowinrange(num_rows):forcolinrange(num_cols):# skip the position where original being copied isifrow==0andcol==0andlayer==0:continue_copy=mesh.Mesh(obj.data.copy())# pad the space between objects by 10% of the dimension being# translatedifcol!=0:translate(_copy,w,w/10.,col,'x')ifrow!=0:translate(_copy,l,l/10.,row,'y')iflayer!=0:translate(_copy,h,h/10.,layer,'z')copies.append(_copy)returncopies# Using an existing stl file:main_body=mesh.Mesh.from_file('ball_and_socket_simplified_-_main_body.stl')# rotate along Ymain_body.rotate([0.0,0.5,0.0],math.radians(90))minx,maxx,miny,maxy,minz,maxz=find_mins_maxs(main_body)w1=maxx-minxl1=maxy-minyh1=maxz-minzcopies=copy_obj(main_body,(w1,l1,h1),2,2,1)# I wanted to add another related STL to the final STLtwist_lock=mesh.Mesh.from_file('ball_and_socket_simplified_-_twist_lock.stl')minx,maxx,miny,maxy,minz,maxz=find_mins_maxs(twist_lock)w2=maxx-minxl2=maxy-minyh2=maxz-minztranslate(twist_lock,w1,w1/10.,3,'x')copies2=copy_obj(twist_lock,(w2,l2,h2),2,2,1)combined=mesh.Mesh(numpy.concatenate([main_body.data,twist_lock.data]+[copy.dataforcopyincopies]+[copy.dataforcopyincopies2]))combined.save('combined.stl',mode=stl.Mode.ASCII)# save as ASCII