stl.Mesh¶
- class stl.Mesh(*args, **kwargs)[source]¶
Bases:
BaseStlPrimary user-facing class for STL mesh operations.
Inherits all functionality from
BaseStlandBaseMesh. Use class methods likefrom_file()andfrom_multi_file()to load STL files, or pass a structured NumPy array to the constructor.Example
>>> import numpy as np >>> from stl import mesh >>> data = np.zeros(1, dtype=mesh.Mesh.dtype) >>> m = mesh.Mesh(data, remove_empty_areas=False) >>> len(m) 1
- Parameters:
data (ndarray[tuple[int], dtype[void]])
calculate_normals (bool)
remove_empty_areas (bool)
remove_duplicate_polygons (_Dedupe)
name (bytes | str)
speedups (Final[bool])
kwargs (object)
- dtype = numpy.dtype([('normals', '<f4', (3,)), ('vectors', '<f4', (3, 3)), ('attr', '<u2', (1,))])¶
numpy.float32(), (3, ) - vectors:numpy.float32(), (3, 3) - attr:numpy.uint16(), (1, )- Type:
normals
The structured NumPy dtype used for mesh data storage. Each record contains: normals (3x float32), vectors (3x3 float32), and attr (1x uint16).
- property areas: ndarray[tuple[int, int], dtype[float32]]¶
Per-triangle surface areas, shape (N, 1).
Lazily computed and cached. Call
update_areas()after modifying vertices to refresh.Example
>>> import numpy as np >>> from stl.base import BaseMesh >>> data = np.zeros(2, dtype=BaseMesh.dtype) >>> data['vectors'][0] = [[0, 0, 0], [1, 0, 0], [0, 1, 0]] >>> m = BaseMesh(data, remove_empty_areas=False) >>> float(m.areas[0][0]) 0.5
Warning
This value is cached on first access. If you modify vertices, call
update_areas()to get correct results.
- property attr: ndarray[tuple[int, int], dtype[uint16]]¶
Per-triangle attribute field (uint16), shape (N, 1).
- property centroids: ndarray[tuple[int, int], dtype[float32]]¶
Per-triangle centroids, shape (N, 3).
Lazily computed and cached. Call
update_centroids()after modifying vertices.Example
>>> import numpy as np >>> from stl.base import BaseMesh >>> data = np.zeros(1, dtype=BaseMesh.dtype) >>> data['vectors'][0] = [[0, 0, 0], [3, 0, 0], [0, 3, 0]] >>> m = BaseMesh(data, remove_empty_areas=False) >>> m.centroids[0].tolist() [1.0, 1.0, 0.0]
- check(exact=False)¶
Check whether the mesh is valid (closed).
- Parameters:
exact (bool) – If True, perform an exact edge-matching check. If False, use a faster normal-sum heuristic.
- Returns:
True if the mesh is closed, False otherwise.
- Return type:
bool
- classmethod from_3mf_file(filename, calculate_normals=True, **kwargs)¶
Load meshes from a 3MF file (read-only).
Parses the 3MF ZIP archive and yields one Mesh per
<mesh>element found.- Parameters:
filename (str) – Path to the .3mf file.
calculate_normals (bool) – Whether to recalculate normals. Defaults to True.
**kwargs (object) – Additional arguments.
- Yields:
Mesh instances, one per 3MF mesh element.
- Return type:
Generator[Self, None, None]
Example
>>> from stl import mesh >>> meshes = list(mesh.Mesh.from_3mf_file('tests/3mf/Moon.3mf')) >>> len(meshes) > 0 True
Note
3MF support is experimental and read-only. Not all 3MF features are supported.
- classmethod from_file(filename, calculate_normals=True, fh=None, mode=Mode.AUTOMATIC, speedups=True, **kwargs)¶
Load a mesh from an STL file.
Reads binary or ASCII STL files. Format is auto-detected unless
modeis explicitly set.- Parameters:
filename (str) – Path to the STL file.
calculate_normals (bool) – Whether to recalculate normals after loading. Defaults to True.
fh (IO[bytes] | None) – Optional pre-opened binary file handle. If provided,
filenameis used only for the mesh name.mode (Mode) – Force ASCII or BINARY loading, or AUTOMATIC detection (default).
speedups (bool) – Use Cython speedups for ASCII parsing when available. Defaults to True.
**kwargs (Any) – Additional arguments passed to the Mesh constructor.
- Returns:
A new Mesh instance containing the loaded data.
- Raises:
ValueError – If the file is empty.
- Return type:
Self
Example
>>> from stl import mesh >>> m = mesh.Mesh.from_file('tests/stl_binary/HalfDonut.stl') >>> len(m.data) > 0 True
Note
When
speedupsis True and the speedups package is installed, ASCII parsing uses a fast C implementation. Speedups are automatically disabled for non-seekable streams (e.g., stdin).
- classmethod from_files(filenames, calculate_normals=True, mode=Mode.AUTOMATIC, speedups=True, **kwargs)¶
Load and merge multiple STL files into one mesh.
- Parameters:
filenames (list[str]) – List of STL file paths.
calculate_normals (bool) – Whether to recalculate normals. Defaults to True.
mode (Mode) – Format mode for each file.
speedups (bool) – Use Cython speedups when available.
**kwargs (Any) – Additional arguments passed to the Mesh constructor.
- Returns:
A single Mesh with data from all files.
- Return type:
Self
Example
>>> from stl import mesh >>> m = mesh.Mesh.from_files(['tests/stl_binary/HalfDonut.stl']) >>> len(m.data) > 0 True
- classmethod from_multi_file(filename, calculate_normals=True, fh=None, mode=Mode.AUTOMATIC, speedups=True, **kwargs)¶
Load multiple solids from a single STL file.
Yields one Mesh per
solidblock found.- Parameters:
filename (str) – Path to the STL file.
calculate_normals (bool) – Whether to recalculate normals. Defaults to True.
fh (IO[bytes] | None) – Optional pre-opened binary file handle.
mode (Mode) – Format mode. Defaults to
Mode.AUTOMATIC.speedups (bool) – Use Cython speedups when available.
**kwargs (Any) – Additional arguments passed to the Mesh constructor.
- Yields:
Mesh instances, one per solid block.
- Return type:
Generator[Self, None, None]
Example
>>> from stl import mesh >>> # Single-solid file yields one mesh >>> solids = list( ... mesh.Mesh.from_multi_file('tests/stl_ascii/HalfDonut.stl') ... ) >>> len(solids) >= 1 True
Note
Multi-solid loading only works with ASCII STL files. Binary STL files always contain a single solid.
- classmethod from_ply_file(filename, calculate_normals=True, fh=None, **kwargs)¶
Load a mesh from a PLY file.
Supports ASCII and binary PLY formats (little-endian and big-endian).
- Parameters:
filename (str) – Path to the .ply file.
calculate_normals (bool) – Whether to recalculate normals. Defaults to True.
fh (IO[bytes] | None) – Optional pre-opened binary file handle.
**kwargs (Any) – Additional arguments passed to the Mesh constructor.
- Returns:
A Mesh instance.
- Return type:
Self
Example
>>> from stl import mesh >>> m = mesh.Mesh.from_ply_file('tests/ply_ascii/Cube.ply') >>> len(m.data) == 12 True
- get(k[, d]) D[k] if k in D, else d. d defaults to None.¶
- get_header(name)¶
Build the 80-byte binary STL header string.
- Parameters:
name (_Name) – Solid name to embed in the header.
- Returns:
Header string truncated to 80 bytes.
- Return type:
str
- get_mass_properties()¶
Compute volume, center of gravity, and inertia.
Uses the polyhedral mass properties algorithm from Eberly (Geometric Tools).
- Returns:
volume – Mesh volume as float32.
center_of_gravity – COG as (3,) array.
inertia – Inertia tensor as (3, 3) array expressed at the COG.
- Return type:
A tuple of (volume, center_of_gravity, inertia)
Example
>>> from stl import mesh >>> m = mesh.Mesh.from_file('tests/stl_binary/HalfDonut.stl') >>> vol, cog, inertia = m.get_mass_properties() >>> float(vol) > 0 True
Warning
These values are only meaningful for closed (watertight) meshes. This method checks via
check(exact=True)and logs a warning for open meshes, but still computes and returns the (then unreliable) values. Useis_closed()to verify beforehand.
- get_mass_properties_with_density(density)¶
Compute mass properties with a given density.
Like
get_mass_properties()but scales volume to mass using the provided density.- Parameters:
density (float) – Material density in consistent units (e.g., kg/m^3 when mesh units are meters).
- Returns:
volume – Mesh volume.
mass – Volume * density.
cog – Center of gravity as (3,) array.
inertia – Inertia tensor as (3, 3) array.
- Return type:
A tuple of (volume, mass, cog, inertia)
Warning
These values are only meaningful for closed (watertight) meshes. This method checks via
check(exact=True)and logs a warning for open meshes, but still computes and returns the (then unreliable) values.
- get_unit_normals()¶
Return a copy of normals normalized to unit length.
Unlike the
unitsproperty, this method always recomputes from the currentnormalsarray.- Returns:
Array of shape (N, 3) with unit-length normals. Zero-length normals remain as zeros.
- Return type:
ndarray[tuple[int, int], dtype[float32]]
- is_closed(exact=False)¶
Check whether the mesh is watertight.
A closed mesh has every edge shared by exactly two triangles with consistent winding.
- Parameters:
exact (bool) – If True, checks directed edges for matching pairs. If False, uses a faster normal-sum heuristic.
- Returns:
True if the mesh is closed, False otherwise.
- Return type:
bool
Warning
The non-exact check (
exact=False) can give false positives and false negatives for certain mesh geometries. Useexact=Truefor reliable results. See #198.
- is_convex()¶
Return True if the mesh is convex.
Tests whether every vertex lies on or behind every face plane.
- Returns:
True if convex, False otherwise.
- Return type:
bool
- items() a set-like object providing a view on D's items¶
- keys() a set-like object providing a view on D's keys¶
- classmethod load(fh, mode=Mode.AUTOMATIC, speedups=True)¶
Load mesh data from an open STL file handle.
Auto-detects binary vs ASCII format unless
modeis explicitly set.- Parameters:
fh (IO[Any]) – Open binary file handle.
mode (Mode | int) – Force a specific format or use
Mode.AUTOMATIC(default). Plain ints are accepted and converted toMode.speedups (bool) – Use Cython speedups for ASCII parsing. Defaults to True.
- Returns:
A (name, data) tuple, or None if the file is empty.
- Raises:
ValueError – If
modeis not a validModevalue.- Return type:
tuple[bytes, _data_1d] | Any
- property max_: ndarray[tuple[int], dtype[float32]]¶
Bounding box maximum corner, shape (3,).
Lazily computed and cached. Call
update_max()after modifying vertices to refresh.Warning
This value is cached on first access. If you modify vertices, call
update_max()to refresh.
- property min_: ndarray[tuple[int], dtype[float32]]¶
Bounding box minimum corner, shape (3,).
Lazily computed and cached. Call
update_min()after modifying vertices to refresh.Warning
This value is cached on first access. If you modify vertices, call
update_min()to refresh.
- property normals: ndarray[tuple[int, int], dtype[float32]]¶
Per-triangle normal vectors, shape (N, 3).
Lazily computed on first access. Call
update_normals()after modifying vertices to refresh.Example
>>> import numpy as np >>> from stl.base import BaseMesh >>> data = np.zeros(1, dtype=BaseMesh.dtype) >>> data['vectors'][0] = [[0, 0, 0], [1, 0, 0], [0, 1, 0]] >>> m = BaseMesh(data, remove_empty_areas=False) >>> m.normals[0].tolist() [0.0, 0.0, 1.0]
- property points: ndarray[tuple[int, int], dtype[float32]]¶
All vertices flattened as (N, 9) array.
- static remove_duplicate_polygons(data, value=RemoveDuplicates.SINGLE)¶
Remove duplicate triangles from mesh data.
- Parameters:
data (ndarray[tuple[int], dtype[void]]) – Structured mesh array.
value (_Dedupe) – Deduplication strategy. Use
RemoveDuplicates.SINGLEto keep one copy,RemoveDuplicates.ALLto remove all copies, orRemoveDuplicates.NONEto keep everything.
- Returns:
Filtered mesh data array.
- Return type:
ndarray[tuple[int], dtype[void]]
Note
ALLhas a safety fallback: when removing every duplicated polygon would drop half the mesh or more, a single copy of each duplicate is kept instead (theSINGLEbehavior).
- static remove_empty_areas(data)¶
Remove triangles with zero surface area.
Filters out degenerate triangles where all three vertices are collinear or coincident.
- Parameters:
data (ndarray[tuple[int], dtype[void]]) – Structured mesh array.
- Returns:
Filtered mesh data array with zero-area triangles removed.
- Return type:
ndarray[tuple[int], dtype[void]]
- rotate(axis, theta=0, point=None)¶
Rotate the mesh around an axis.
- Parameters:
axis (_ToAxis) – Axis to rotate around as [x, y, z].
theta (float) – Rotation angle in radians. Use
math.radians()to convert from degrees.point (_ToPoint | None) – Optional point to rotate around. If None, rotates around the origin.
- Return type:
None
Example
>>> import math >>> import numpy as np >>> from stl.base import BaseMesh >>> data = np.zeros(1, dtype=BaseMesh.dtype) >>> data['vectors'][0] = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] >>> m = BaseMesh(data, remove_empty_areas=False) >>> m.rotate([0, 0, 1], math.radians(90))
Warning
In older versions, the
pointparameter was accidentally inverted. If you relied on the old behavior, pass-pointinstead.
- rotate_using_matrix(rotation_matrix, point=None)¶
Rotate using a pre-computed rotation matrix.
- Parameters:
rotation_matrix (_f32_2d | _f64_2d) – A (3, 3) rotation matrix.
point (_ToPoint | None) – Optional point to rotate around. If None, rotates around the origin.
- Return type:
None
Warning
This method produces clockwise rotations for positive angles, which is arguably incorrect but retained for backwards compatibility. See #166.
- static rotation_matrix(axis, theta)¶
Generate a 3x3 rotation matrix.
Uses the Euler-Rodrigues formula for fast rotation matrix construction.
- Parameters:
axis (_ToAxis) – Axis to rotate around as [x, y, z].
theta (float) – Rotation angle in radians. Use
math.radians()to convert from degrees.
- Returns:
A (3, 3) rotation matrix. Returns the identity matrix if the axis is zero.
- Return type:
ndarray[tuple[int, int], dtype[float64]]
- save(filename, fh=None, mode=Mode.AUTOMATIC, update_normals=True)¶
Save the mesh to an STL file.
If mode is
Mode.AUTOMATIC, writes binary unless the output is a TTY.- Parameters:
filename (_Name) – Output file path. Required even when
fhis provided (used for STL header).fh (IO[bytes] | None) – Optional pre-opened binary file handle.
mode (Mode | int) – Output format. Defaults to
Mode.AUTOMATIC.update_normals (bool) – Whether to recalculate normals before saving. Defaults to True.
- Raises:
TypeError – If
fhis a text-mode handle.ValueError – If
modeis not a validModevalue.
- Return type:
None
Example
>>> import numpy as np >>> from stl import mesh >>> data = np.zeros(1, dtype=mesh.Mesh.dtype) >>> data['vectors'][0] = [[0, 0, 0], [1, 0, 0], [0, 1, 0]] >>> m = mesh.Mesh(data, remove_empty_areas=False) >>> m.save('/tmp/_numpy_stl_test.stl')
Warning
Even for ASCII output, the file handle must be opened in binary mode (
'wb'). A text-mode handle raisesTypeError.
- save_ply(filename, fh=None, mode='binary_little_endian', update_normals=True)¶
Save the mesh to a PLY file.
- Parameters:
filename (str) – Output file path.
fh (IO[bytes] | None) – Optional pre-opened binary file handle.
mode (str) – PLY format. One of
'ascii','binary_little_endian'(default),'binary_big_endian'.update_normals (bool) – Whether to recalculate normals before saving. Defaults to True.
- Return type:
None
Example
>>> from stl import mesh >>> m = mesh.Mesh.from_file('tests/stl_binary/HalfDonut.stl') >>> m.save_ply('/tmp/_numpy_stl_test.ply')
- transform(matrix)¶
Apply a 4x4 transformation matrix.
The upper-left 3x3 submatrix is the rotation. The rightmost column (0:3, 3) is the translation.
- Parameters:
matrix (ndarray[tuple[int, int], dtype[float32]] | ndarray[tuple[int, int], dtype[float64]]) – A (4, 4) transformation matrix. The rotation part must have unit determinant.
- Raises:
AssertionError – If matrix shape is not (4, 4) or rotation determinant is not 1.0.
- Return type:
None
- translate(translation)¶
Translate (move) the mesh.
- Parameters:
translation (_ToTranslation) – Translation vector [x, y, z].
- Raises:
AssertionError – If translation is not length 3.
- Return type:
None
Example
>>> import numpy as np >>> from stl.base import BaseMesh >>> data = np.zeros(1, dtype=BaseMesh.dtype) >>> data['vectors'][0] = [[0, 0, 0], [1, 0, 0], [0, 1, 0]] >>> m = BaseMesh(data, remove_empty_areas=False) >>> m.translate([10, 20, 30]) >>> float(m.v0[0][0]) 10.0
- property units: ndarray[tuple[int, int], dtype[float32]]¶
Per-triangle unit normal vectors, shape (N, 3).
Lazily computed and cached. Call
update_units()after modifying vertices.Example
>>> import numpy as np >>> from stl.base import BaseMesh >>> data = np.zeros(1, dtype=BaseMesh.dtype) >>> data['vectors'][0] = [[0, 0, 0], [1, 0, 0], [0, 1, 0]] >>> m = BaseMesh(data, remove_empty_areas=False) >>> m.units[0].tolist() [0.0, 0.0, 1.0]
- update_areas(normals=None)¶
Refresh the cached per-triangle areas.
- Parameters:
normals (ndarray[tuple[int, int], dtype[float32]] | None) – Pre-computed cross products. If None, recomputes from current vertices.
- Return type:
None
- update_centroids()¶
Refresh the cached per-triangle centroids.
- Return type:
None
- update_max()¶
Refresh the cached bounding box maximum.
- Return type:
None
- update_min()¶
Refresh the cached bounding box minimum.
- Return type:
None
- update_normals(update_areas=True, update_centroids=True)¶
Recalculate normals from current vertex positions.
Also refreshes areas and centroids by default.
- Parameters:
update_areas (bool) – Whether to also refresh cached areas. Defaults to True.
update_centroids (bool) – Whether to also refresh cached centroids. Defaults to True.
- Return type:
None
- update_units()¶
Refresh the cached unit normal vectors.
- Return type:
None
- property v0: ndarray[tuple[int, int], dtype[float32]]¶
First vertex of each triangle, shape (N, 3).
- property v1: ndarray[tuple[int, int], dtype[float32]]¶
Second vertex of each triangle, shape (N, 3).
- property v2: ndarray[tuple[int, int], dtype[float32]]¶
Third vertex of each triangle, shape (N, 3).
- values() an object providing a view on D's values¶
- property vectors: ndarray[tuple[int, int, int], dtype[float32]]¶
Triangle vertices as (N, 3, 3) array.
- property x: ndarray[tuple[int, int], dtype[float32]]¶
X coordinates by vertex, shape (N, 3).
- property y: ndarray[tuple[int, int], dtype[float32]]¶
Y coordinates by vertex, shape (N, 3).
- property z: ndarray[tuple[int, int], dtype[float32]]¶
Z coordinates by vertex, shape (N, 3).