Basic Usage

PyMesh is rapid prototyping library focused on processing and generating 3D meshes. The Mesh class is the core data structure and is used by all modules.

Mesh Data Structure

In PyMesh, a Mesh consists of 3 parts: geometry, connectivity and attributes.

  • Geometry consists of vertices, faces and generialized voxels (i.e. a volume element such as tetrahedron or hexahedron). The dimention of the embedding space, face type, voxel type can all be inferred from the geometry data. It is possible for a mesh to consist of 0 vertices or 0 faces or 0 voxels.
  • The connectivity contains adjacency information, including vertex-vertex, vertex-face, vertex-voxel, face-face, face-voxel and voxel-voxel adjacencies.
  • Attributes are arbirary value field assigned to a mesh. One could assign a scalar or vector for each vertex/face/voxel. There are a number predefined attributes.

Loading Mesh

From file:

>>> mesh = pymesh.load_mesh("model.obj")

PyMesh supports parsing the following formats: .obj, .ply, .off, .stl, .mesh, .node, .poly and .msh.

From raw data:

>>> # for surface mesh:
>>> mesh = pymesh.form_mesh(vertices, faces)

>>> # for volume mesh:
>>> mesh = pymesh.form_mesh(vertices, faces, voxels)

where vertices, faces and voxels are of type numpy.ndarray. One vertex/face/voxel per row.

Accessing Mesh Data

Geometry data can be directly accessed:

>>> print(mesh.num_vertices, mesh.num_faces, mesh.num_voxels)
(8, 12, 6)

>>> print(mesh.dim, mesh.vertex_per_face, mesh.vertex_per_voxel)
(3, 3, 4)

>>> mesh.vertices
array([[-1., -1.,  1.],
       ...
       [ 1.,  1.,  1.]])

>>> mesh.faces
array([[0, 1, 2],
       ...
       [4, 5, 6]])

>>> mesh.voxels
array([[0, 1, 2, 3],
       ...
       [4, 5, 6, 7]])

Connectivity data is disabled by default because it is often not needed. To enable it:

>>> mesh.enable_connectivity();

The connectivity information can be queried using the following methods:

>>> mesh.get_vertex_adjacent_vertices(vi);
>>> mesh.get_vertex_adjacent_faces(vi);
>>> mesh.get_vertex_adjacent_voxels(vi);

>>> mesh.get_face_adjacent_faces(fi);
>>> mesh.get_face_adjacent_voxels(fi);

>>> mesh.get_voxel_adjacent_faces(Vi);
>>> mesh.get_voxel_adjacent_voxels(Vi);

Using Attributes

Attributes allow one to attach a scalar or vector fields to the mesh. For example, vertex normal could be stored as a mesh attribute where a normal vector is associated with each vertex. In addition to vertices, attribute could be associated with face and voxels. To create an attribute:

>>> mesh.add_attribute("attribute_name");

This creates an empty attribute (of length 0) called attribute_name. To assign value to the attribute:

>>> val = np.ones(mesh.num_vertices);
>>> mesh.set_attribute("attribute_name", val);

Notice that the val variable is a native python numpy.ndarray. The length of the attribute is used to determine whether it is a scalar field or vector field. The length is also used to determine whether the attribute is assigned to vertices, faces or voxels.

To access a defined attribute:

>>> attr_val = mesh.get_attribute("attribute_name");
>>> attr_val
array([ 1.0,  1.0,  1.0, ...,  1.0, 1.0,  1.0])

The following vertex attributes are predifined:

  • vertex_normal: A vector field representing surface normals. Zero vectors are assigned to vertices in the interior.
  • vertex_volume: A scalar field representing the lumped volume of each vertex (e.g. 1/4 of the total volume of all neighboring tets for tetrahedron mesh.).
  • vertex_area: A scalar field representing the lumped surface area of each vertex (e.g. 1/3 of the total face area of its 1-ring neighborhood).
  • vertex_laplacian: A vector field representing the discretized Laplacian vector.
  • vertex_mean_curvature: A scalar field representing the mean curvature field of the mesh.
  • vertex_gaussian_curvature: A scalar field representing the Gaussian curvature field of the mesh.
  • vertex_index: A scalar field representing the index of each vertex.
  • vertex_valance: A scalar field representing the valance of each vertex.
  • vertex_dihedral_angle: A scalar field representing the max dihedral angle of all edges adjacent to this vertex.

The following face attributes are predefined:

  • face_area: A scalar field representing face areas.
  • face_centroid: A vector field representing the face centroids (i.e. average of all corners).
  • face_circumcenter: A vector field representing the face circumcenters (defined for triangle faces only).
  • face_index: A scalar field representing the index of each face.
  • face_normal: A vector field representing the normal vector of each face.
  • face_voronoi_area: A vector field representing the voronoi area of each corner of the face.

The following voxel attributes are predefined:

  • voxel_index: A scalar field representing the index of each voxel.
  • voxel_volume: A scalar field representing the volume of each voxel.
  • voxel_centroid: A scalar field representing the centroid of each voxel (i.e. average of all corners of a voxel).

Predefined attribute does not need to be set:

>>> mesh.add_attribute("vertex_area")
>>> mesh.get_attribute("vertex_area")
array([ 0.56089278,  0.5608997 ,  0.57080866, ...,  5.62381961,
        2.12105028,  0.37581711])

Notice that attribute values are always stored as a 1D array. For attributes that represent vector/tensor fiels, the attribute values are the flattened version of the vector field:

>>> mesh.add_attribute("vertex_normal")
>>> mesh.get_attribute("vertex_normal")
array([ 0.35735435, -0.49611438, -0.79130802, ..., -0.79797784,
        0.55299134, -0.23964964])

If an attribute is known to be a per-vertex attribute, one can:

>>> mesh.get_vertex_attribute("vertex_normal")
array([[ 0.35735435, -0.49611438, -0.79130802],
       [ 0.41926554, -0.90767626, -0.01844495],
       [-0.64142577,  0.76638469, -0.03503568],
       ...,
       [-0.64897662, -0.64536558, -0.40290522],
       [-0.92207726, -0.10573231, -0.37228242],
       [-0.79797784,  0.55299134, -0.23964964]])

where attribute values are returned as a 2D matrix. Each row represents the value per vertex.

Similarly, per-face and per-voxel attribute can be retrieved using get_face_attribute() and get_voxel_attribute() methods.

To retrieve the names of all defined attributes for a given mesh:

>>> mesh.get_attribute_names()
("attribute_name", "vertex_area", "vertex_normal")

Saving Mesh

The following formats are supported for saving meshes: .obj, .off, .ply, .mesh, .node, .poly, .stl and .msh. However, saving in .stl format is strongly discouraged because STL files use more disk space and stores less information. To save a mesh:

>>> pymesh.save_mesh("filename.obj", mesh);

For certain formats (e.g. .ply, .msh, .stl), it is possible to save either as an ASCII file or a binary file. By default, PyMesh will always use the binary format. To save in ASCII, just set the ascii argument:

>>> pymesh.save_mesh("filename.obj", mesh, ascii=True)

In addition, vertex position can be saved using double or float. By default, PyMesh saves in double, to save using float:

>>> pymesh.save_mesh("filename.obj", mesh, use_float=True)

Mesh attributes can also be saved in .msh and .ply formats. To save with attributes:

>>> pymesh.save_mesh("filename.msh", mesh, attribute_name_1, attribute_name_2, ...)

To save with all defined attributes:

>>> pymesh.save_mesh("filename.msh", mesh, *mesh.get_attribute_names())

It is also possible to save from raw vertices, faces and voxels:

>>> # For surface mesh
>>> pymesh.save_mesh_raw("filename.ply", vertices, faces)

>>> # For volume mesh
>>> pymesh.save_mesh_raw("filename.ply", vertices, faces, voxels)

>>> # In ascii and using float
>>> pymesh.save_mesh_raw("filename.ply", vertices, faces, voxels,\
        ascii=True, use_float=True)