Import, export, process, analyze and view triangular meshes.
Advanced mesh processing capabilities including repair operations, boolean operations, remeshing, subdivision, and mesh quality improvement. These functions enable comprehensive mesh manipulation and enhancement workflows.
Perform CSG (Constructive Solid Geometry) operations between meshes.
def union(self, other, engine=None, check_volume=True, **kwargs) -> 'Trimesh':
"""
Boolean union with another mesh.
Parameters:
- other: Trimesh object or sequence of Trimesh objects to union with
- engine: str, boolean engine ('manifold' or 'blender')
- check_volume: bool, check that all meshes are watertight volumes
- **kwargs: boolean operation options
Returns:
New Trimesh object containing the union
"""
def intersection(self, other, engine=None, check_volume=True, **kwargs) -> 'Trimesh':
"""
Boolean intersection with another mesh.
Parameters:
- other: Trimesh object or sequence of Trimesh objects to intersect with
- engine: str, boolean engine ('manifold' or 'blender')
- check_volume: bool, check that all meshes are watertight volumes
- **kwargs: boolean operation options
Returns:
New Trimesh object containing the intersection
"""
def difference(self, other, engine=None, check_volume=True, **kwargs) -> 'Trimesh':
"""
Boolean difference (subtraction) with another mesh.
Parameters:
- other: Trimesh object or sequence of Trimesh objects to subtract
- engine: str, boolean engine ('manifold' or 'blender')
- check_volume: bool, check that all meshes are watertight volumes
- **kwargs: boolean operation options
Returns:
New Trimesh object with other subtracted from self
"""Fix common mesh problems and improve mesh quality.
def fix_normals(self) -> None:
"""Fix face winding order to ensure consistent normals"""
def fill_holes(self) -> bool:
"""
Fill holes in the mesh surface.
Returns:
bool, True if holes were filled
"""
def remove_duplicate_faces(self) -> None:
"""Remove faces that are duplicated"""
def remove_degenerate_faces(self, height=1e-12) -> None:
"""
Remove degenerate faces (zero area or invalid).
Parameters:
- height: float, minimum face area threshold
"""
def remove_unreferenced_vertices(self) -> None:
"""Remove vertices not referenced by any face"""
def merge_vertices(self, merge_tex=False, merge_norm=False, digits_vertex=None) -> None:
"""
Merge vertices closer than tolerance.
Parameters:
- merge_tex: bool, merge texture coordinates
- merge_norm: bool, merge vertex normals
- digits_vertex: int, decimal places for vertex precision
"""
def process(self, validate=True, merge_tex=False, merge_norm=False) -> None:
"""
Apply standard mesh processing pipeline.
Parameters:
- validate: bool, run mesh validation
- merge_tex: bool, merge texture coordinates
- merge_norm: bool, merge vertex normals
"""Modify mesh resolution and topology.
def subdivide(self, face_index=None) -> 'Trimesh':
"""
Subdivide mesh faces to increase resolution.
Parameters:
- face_index: array of face indices to subdivide (None for all)
Returns:
New subdivided mesh
"""
def subdivide_loop(self, iterations=1) -> 'Trimesh':
"""
Apply Loop subdivision for smooth surfaces.
Parameters:
- iterations: int, number of subdivision iterations
Returns:
Subdivided mesh with smooth surface
"""
def smoothed(self, **kwargs) -> 'Trimesh':
"""
Return smoothed version using Laplacian smoothing.
Parameters:
- **kwargs: smoothing parameters
Returns:
Smoothed mesh
"""
def simplify_quadratic_decimation(self, face_count=None, **kwargs) -> 'Trimesh':
"""
Simplify mesh using quadratic error decimation.
Parameters:
- face_count: int, target number of faces
- **kwargs: decimation options
Returns:
Simplified mesh
"""Work with convex hulls and convex decomposition.
@property
def convex_hull(self) -> 'Trimesh':
"""Convex hull of the mesh vertices"""
def convex_decomposition(self, **kwargs) -> list:
"""
Decompose mesh into convex components.
Parameters:
- **kwargs: decomposition parameters
Returns:
List of convex Trimesh objects
"""
def is_convex(self) -> bool:
"""Check if mesh is convex"""Cut meshes with planes and extract cross-sections.
def slice_plane(self, plane_normal, plane_origin, **kwargs):
"""
Slice mesh with a plane.
Parameters:
- plane_normal: (3,) plane normal vector
- plane_origin: (3,) point on plane
- **kwargs: slicing options
Returns:
List of mesh pieces or cross-section paths
"""
def section(self, plane_normal, plane_origin) -> 'Path3D':
"""
Get 2D cross-section of mesh at plane.
Parameters:
- plane_normal: (3,) plane normal vector
- plane_origin: (3,) point on plane
Returns:
Path3D object representing cross-section
"""
def section_multiplane(self, plane_normal, plane_origins) -> list:
"""
Get multiple parallel cross-sections.
Parameters:
- plane_normal: (3,) plane normal vector
- plane_origins: (n, 3) points on planes
Returns:
List of Path3D cross-section objects
"""Apply various smoothing algorithms to improve mesh quality.
def smooth_laplacian(self, lamb=0.5, iterations=1, implicit_time_integration=False, volume_constraint=True) -> 'Trimesh':
"""
Laplacian mesh smoothing.
Parameters:
- lamb: float, smoothing strength (0-1)
- iterations: int, number of smoothing iterations
- implicit_time_integration: bool, use implicit integration
- volume_constraint: bool, preserve volume during smoothing
Returns:
Smoothed mesh
"""
def smooth_taubin(self, lamb=0.5, mu=-0.53, iterations=10) -> 'Trimesh':
"""
Taubin smoothing (reduces shrinkage compared to Laplacian).
Parameters:
- lamb: float, positive smoothing factor
- mu: float, negative smoothing factor
- iterations: int, number of iterations
Returns:
Smoothed mesh
"""Align meshes to each other or to coordinate systems.
def register(self, other, samples=500, scale=False, icp_first=10, icp_final=50) -> tuple:
"""
Register mesh to another using ICP algorithm.
Parameters:
- other: Trimesh to register to
- samples: int, number of sample points
- scale: bool, allow scaling transformation
- icp_first: int, iterations for initial ICP
- icp_final: int, iterations for final ICP
Returns:
tuple: (transform_matrix, cost, iterations)
"""
def rezero(self) -> 'Trimesh':
"""Center mesh at origin"""
def apply_obb(self) -> 'Trimesh':
"""Apply oriented bounding box transformation"""
def principal_inertia_transform(self) -> np.ndarray:
"""Get transformation matrix aligning to principal inertia axes"""Create new mesh geometry and modify existing meshes.
def extrude_polygon(polygon, height, **kwargs) -> 'Trimesh':
"""
Extrude 2D polygon to create 3D mesh.
Parameters:
- polygon: Path2D or (n, 2) polygon vertices
- height: float, extrusion height
- **kwargs: extrusion options
Returns:
Extruded Trimesh object
"""
def extrude_path(self, path, **kwargs) -> 'Trimesh':
"""
Extrude mesh along path.
Parameters:
- path: Path3D or (n, 3) path points
- **kwargs: extrusion options
Returns:
Extruded mesh
"""
def thicken(self, thickness, **kwargs) -> 'Trimesh':
"""
Thicken surface mesh to create solid.
Parameters:
- thickness: float, thickness amount
- **kwargs: thickening options
Returns:
Thickened solid mesh
"""import trimesh
# Create primitive shapes
box = trimesh.primitives.Box(extents=[2, 2, 2])
sphere = trimesh.primitives.Sphere(radius=1.5)
cylinder = trimesh.primitives.Cylinder(radius=0.5, height=3)
# Boolean operations
union_result = box.union(sphere)
intersection_result = box.intersection(sphere)
difference_result = box.difference(cylinder)
# Chain operations
complex_shape = box.union(sphere).difference(cylinder)
# Check results
print(f"Original box volume: {box.volume}")
print(f"Union volume: {union_result.volume}")
print(f"Intersection volume: {intersection_result.volume}")# Load a potentially problematic mesh
mesh = trimesh.load('broken_model.stl')
print(f"Initial: {len(mesh.vertices)} vertices, {len(mesh.faces)} faces")
print(f"Is watertight: {mesh.is_watertight}")
# Apply repair operations
mesh.remove_duplicate_faces()
mesh.remove_degenerate_faces()
mesh.remove_unreferenced_vertices()
mesh.fill_holes()
mesh.fix_normals()
# Merge nearby vertices
mesh.merge_vertices()
# Full processing pipeline
mesh.process(validate=True)
print(f"After repair: {len(mesh.vertices)} vertices, {len(mesh.faces)} faces")
print(f"Is watertight: {mesh.is_watertight}")# Load base mesh
mesh = trimesh.load('low_poly_model.obj')
# Subdivision for higher resolution
high_res = mesh.subdivide_loop(iterations=2)
print(f"Original: {len(mesh.faces)} faces")
print(f"After subdivision: {len(high_res.faces)} faces")
# Smoothing operations
laplacian_smooth = mesh.smooth_laplacian(lamb=0.6, iterations=5)
taubin_smooth = mesh.smooth_taubin(iterations=10)
# Compare results
mesh.show() # Original
laplacian_smooth.show() # Laplacian smoothed
taubin_smooth.show() # Taubin smoothedimport numpy as np
# Slice mesh with horizontal planes
plane_normal = np.array([0, 0, 1]) # Z-axis
plane_origins = np.array([[0, 0, z] for z in np.linspace(-1, 1, 10)])
# Get cross-sections
sections = mesh.section_multiplane(plane_normal, plane_origins)
# Export each section
for i, section in enumerate(sections):
if section is not None:
section.export(f'section_{i}.svg')
# Slice mesh in half
plane_origin = np.array([0, 0, 0])
pieces = mesh.slice_plane(plane_normal, plane_origin)
if len(pieces) == 2:
pieces[0].export('bottom_half.stl')
pieces[1].export('top_half.stl')# Register two similar meshes
mesh1 = trimesh.load('scan1.ply')
mesh2 = trimesh.load('scan2.ply')
# Perform registration
transform, cost, iterations = mesh1.register(mesh2, samples=1000)
print(f"Registration cost: {cost}")
print(f"Iterations: {iterations}")
# Apply transformation
mesh1.apply_transform(transform)
# Check alignment quality
aligned_mesh = mesh1.copy()
distance = np.mean(aligned_mesh.nearest.vertex(mesh2.vertices)[1])
print(f"Mean distance after alignment: {distance}")Install with Tessl CLI
npx tessl i tessl/pypi-trimesh