In [1]:
import numpy as np
import trimesh
In [2]:
# load a file by name or from a buffer
mesh = trimesh.load_mesh('../models/featuretype.STL')
In [3]:
# is the current mesh watertight?
mesh.is_watertight
Out[3]:
True
In [4]:
# what's the euler number for the mesh?
mesh.euler_number
Out[4]:
-16
In [5]:
# the convex hull is another Trimesh object that is available as a property
# lets compare the volume of our mesh with the volume of its convex hull
np.divide(mesh.volume, mesh.convex_hull.volume)
Out[5]:
0.77924077444669315
In [6]:
# since the mesh is watertight, it means there is a
# volumetric center of mass which we can set as the origin for our mesh
mesh.vertices -= mesh.center_mass
In [7]:
# what's the moment of inertia for the mesh?
mesh.moment_inertia
Out[7]:
array([[  6.93059627e+00,   1.43877613e-03,   1.49424850e-01],
       [  1.43877613e-03,   2.19191960e+01,   1.25194047e-04],
       [  1.49424850e-01,   1.25194047e-04,   2.62344872e+01]])
In [8]:
# if there are multiple bodies in the mesh we can split the mesh by
# connected components of face adjacency
# since this example mesh is a single watertight body we get a list of one mesh
mesh.split()
Out[8]:
array([<trimesh.base.Trimesh object at 0x7fde02cbb908>], dtype=object)
In [9]:
# preview mesh in a pyglet window from a terminal, or inline in a notebook
mesh.show()
Out[9]:
In [10]:
# facets are groups of coplanar adjacent faces
# set each facet to a random color
# colors are 8 bit RGBA by default (n,4) np.uint8
for facet in mesh.facets:
    mesh.visual.face_colors[facet] = trimesh.visual.random_color()
In [11]:
# transform method can be passed a (4,4) matrix and will cleanly apply the transform
mesh.apply_transform(trimesh.transformations.random_rotation_matrix())
Out[11]:
<trimesh.base.Trimesh at 0x7fde48027860>
In [12]:
# an axis aligned bounding box is available
mesh.bounding_box.primitive.extents
Out[12]:
TrackedArray([ 4.49845071,  2.83213732,  5.54513315])
In [13]:
# a minimum volume oriented bounding box is available
mesh.bounding_box_oriented.primitive.extents
Out[13]:
TrackedArray([ 5.   ,  2.5  ,  1.375])
In [14]:
mesh.bounding_box_oriented.primitive.transform
Out[14]:
TrackedArray([[-0.46414255, -0.79161922, -0.3973798 ,  0.05310496],
              [ 0.15607469, -0.51470337,  0.84304278, -0.11929664],
              [-0.87190159,  0.3292711 ,  0.36244746, -0.05861935],
              [ 0.        ,  0.        ,  0.        ,  1.        ]])
In [15]:
# the bounding box is a trimesh.primitives.Box object, which subclasses
# Trimesh and lazily evaluates to fill in vertices and faces when requested
mesh.bounding_box_oriented.show()
Out[15]:
In [16]:
# bounding spheres and bounding cylinders of meshes are also
# available, and will be the minimum volume version of each
# except in certain degenerate cases, where they will be no worse
# than a least squares fit version of the primitive.
print(mesh.bounding_box_oriented.volume, 
      mesh.bounding_cylinder.volume,
      mesh.bounding_sphere.volume)
17.18750000000001 28.4825512989 95.8943899752