Using PyMeshLab


This document summarizes some of the usage of the pymeshlab library. Unfortunately, the library has been through severe changes over time, so some methods are renamed and filter calling conventions are shifting too, which makes AI and humans mad :-)




Core Concepts

The MeshSet

The MeshSet is the primary container for handling multiple mesh layers and applying filters.

ms = pymeshlab.MeshSet()

Mesh Objects

Individual meshes within a MeshSet. They contain the geometry and attribute data.

mesh = ms.current_mesh()

Basic Operations

Loading and Saving

# Load a new mesh into the MeshSet
ms.load_new_mesh("input.stl")

# Save the currently selected mesh
ms.save_current_mesh("output.stl", colormode=False)

Layer Management

# Get total number of meshes in the MeshSet
count = ms.mesh_number()

# Select a specific mesh by index
ms.set_current_mesh(1)

# Delete the currently active mesh
ms.delete_current_mesh()

# Control visibility (often used for merging)
ms.set_current_mesh_visibility(True)

# Clear all meshes from the MeshSet
ms.clear()

Mesh Data Access (NumPy Integration)

PyMeshLab provides direct access to mesh data as NumPy arrays, which is highly efficient for custom logic.

Geometry and Attributes

mesh = ms.current_mesh()

# Get matrices as NumPy arrays
vertices = mesh.vertex_matrix()      # (N, 3) float
faces = mesh.face_matrix()          # (M, 3) int
normals = mesh.face_normal_matrix() # (M, 3) float
colors = mesh.face_color_matrix()   # (M, 4) float32 [0.0 - 1.0]

# Metadata
v_count = mesh.vertex_number()
f_count = mesh.face_number()

Bounding Box

bbox = mesh.bounding_box()
min_corner = bbox.min() # [x, y, z]
max_corner = bbox.max() # [x, y, z]

# Recalculate if geometry changed manually
mesh.update_bounding_box()

Filters and Geometric Operations

Boolean Operations

# Intersect the first two layers
# transfer_face_color: attempts to preserve colors from source meshes
ms.generate_boolean_intersection(transfer_face_color=True)

Splitting and Merging

# Split a mesh into separate layers based on connectivity
ms.generate_splitting_by_connected_components(delete_source_mesh=True)

# Flatten all visible layers into a single mesh
ms.generate_by_merging_visible_meshes(deletelayer=True)

Normals

# Recompute normals
ms.compute_normal_for_point_clouds()
ms.compute_normal_per_face()

Creating Meshes Manually

You can instantiate a Mesh object directly from NumPy arrays and add it to a MeshSet.

# vertices: (N, 3) array
# faces: (M, 3) array
# f_color_matrix: (M, 4) optional array
new_mesh = pymeshlab.Mesh(
    vertex_matrix=vertices,
    face_matrix=faces,
    f_color_matrix=face_colors
)

ms.add_mesh(new_mesh, "OptionalName")

Color Management

Filter-based Coloring

# Apply a constant color using strings for values
ms.compute_color_by_function_per_face(r='248', g='248', b='248', a='255')

# Create a random color matrix for the mesh
ms.compute_color_random_per_face()

Manual Color Assignment

When modifying colors manually via the color matrix, values are expected to be float32 in the range [0.0, 1.0].

face_colors = mesh.face_color_matrix()
white = [1.0, 1.0, 1.0, 1.0]
face_colors[0] = white # Set first face to white

2D Projection and Contour Extraction

Projecting 3D geometry into 2D and extracting its outline (silhouette/contour) is a common task for layout optimization or SVG generation.

2D Projection (Flattening)

The simplest way to project a mesh onto the XY plane is to scale its Z-axis to zero.

# Flatten the mesh to the XY plane
ms.compute_matrix_from_translation_rotation_scale(scalez=0.0)

Alternatively, you can manually modify the vertex matrix:

vertices = ms.current_mesh().vertex_matrix()
vertices[:, 2] = 0  # Set all Z coordinates to 0

Planar Sections (Slicing)

To get the actual intersection polyline at a specific height:

# Generates a polyline/surface at the specified Z-offset
ms.generate_polyline_from_planar_section(
    planeaxis=2,        # 0:X, 1:Y, 2:Z
    planeoffset=100.0,
    createsectionsurface=True
)

Contour Extraction (Silhouette)

To obtain a clean 2D contour from a projected mesh (merging all overlapping triangles), the project often uses shapely:

from shapely.geometry import Polygon
from shapely.ops import unary_union

# 1. Project to XY (as shown above)
# 2. Extract faces and 2D vertices
faces = ms.current_mesh().face_matrix()
vertices = ms.current_mesh().vertex_matrix()[:, :2] # Only X and Y

# 3. Create Shapely polygons for each face
polygons = [Polygon(vertices[f]) for f in faces]

# 4. Merge into a single contour
union_geom = unary_union(polygons)

# 5. Clean up (optional)
contour = union_geom.buffer(0.1).buffer(-0.1)


Selection and Conditional Coloring

PyMeshLab allows selecting elements based on geometric conditions, which can then be used to apply colors or filters selectively.

Selection by Condition

# Select faces based on a boolean expression (e.g., faces pointing down)
# The condition uses variables like 'nx', 'ny', 'nz' for normals, 'px', 'py', 'pz' for position.
ms.compute_selection_by_condition_per_face(condselect="nz < -0.99")

Applying Colors to Selection

Filters that support selections usually have an onselected parameter.

# Color only the selected faces
ms.compute_color_by_function_per_face(
    r='64', g='128', b='248',
    onselected=True
)

Working with Matrices and Re-sampling

When creating a new Mesh from an existing one (e.g., to fix state or clear selection), ensure you copy the matrices to avoid reference issues or segmentation faults.

# Safely recreating a mesh from current data
current = ms.current_mesh()
new_mesh = pymeshlab.Mesh(
    vertex_matrix=current.vertex_matrix().copy(),
    face_matrix=current.face_matrix().copy(),
    f_color_matrix=current.face_color_matrix().copy()
)
ms.add_mesh(new_mesh)

Best Practices & Gotchas

  1. Precision Tolerance: When checking if vertices lie on a plane (e.g., for coloring cut edges), use a tolerance (e.g., 1e-12) rather than exact equality.
  2. Layer Order: Boolean operations typically act on the top layers of the MeshSet. Ensure you add/select layers in the correct order before calling them.
  3. Internal Updates: If you modify vertices or faces directly via NumPy, always call mesh.update_bounding_box() if subsequent operations depend on spatial boundaries.
  4. Exception Handling: Complex geometric operations like Boolean intersections can fail on non-manifold or "messy" meshes; wrap them in try/except blocks.

Comments

Popular posts from this blog

VFD control with Arduino using RS485 link

Arduino mood light

Importing OpenSCAD designs into Onshape