Remesh
The Remesh module provides tools for modifying triangle mesh resolution: reducing face count through decimation (to a target count) or simplification (to an error budget), or redistributing vertices through isotropic remeshing.
tf.triangulated from the Geometry module to convert polygon meshes first.parallel=False for sequential execution (e.g. when processing many meshes in parallel externally).Meshtransformation. When a Mesh has a transformation, lengths are measured in the transformed coordinate space. This allows remeshing a scaled or rotated mesh without modifying vertex data.Decimation
Reduce face count using quadric error metrics. The algorithm collapses edges in priority order, placing the new vertex at the position that minimizes geometric error.
Basic Usage
import trueform as tf
faces, points = tf.read_stl("model.stl")
mesh = tf.Mesh(faces, points)
# Decimate to 10% of original faces
dec_faces, dec_points = tf.decimated(mesh, 0.1)
From Tuple
# Also accepts (faces, points) tuples directly
dec_faces, dec_points = tf.decimated((faces, points), 0.1)
With Configuration
dec_faces, dec_points = tf.decimated(
mesh, 0.1,
preserve_boundary=True,
min_quality=0.3,
parallel=False,
)
| Parameter | Type | Default | Description |
|---|---|---|---|
data | Mesh or tuple | Triangle mesh or (faces, points) tuple | |
target_proportion | float | Target face count as fraction of original (0.0–1.0) | |
min_quality | float | -1.0 | Worst triangle quality allowed after a collapse, in 0,1 (1 = equilateral). Negative disables, 0 = never worsen, >0 = quality floor |
preserve_boundary | bool | True | If True, boundary edges are never collapsed |
stabilizer | float | 1e-3 | Tikhonov stabilizer for quadric solve |
parallel | bool | True | Use parallel partitioned collapse |
feature_angle | float | -1.0 | Feature edge detection angle in degrees. Edges sharper than this are preserved. Negative disables |
feature_weight | float | 100.0 | Penalty weight for feature edge preservation. Higher = stronger |
preserve_regions | ndarray | None | Per-face region labels (one int per input face). When given, edges between differing labels are preserved as features and a third value — the output mesh's per-face labels (int32) — is returned. Keyword-only |
Feature Edge Preservation
Preserve sharp creases and corners during decimation:
dec_faces, dec_points = tf.decimated(mesh, 0.1, feature_angle=30)
| Returns | Type | Description |
|---|---|---|
faces | ndarray | Face indices, shape (N, 3) |
points | ndarray | Vertex positions, shape (M, 3) |
labels | ndarray | Per-face region labels of the output mesh, shape (N,), int32. Returned only when preserve_regions is given |
Simplification
Like decimation, simplification reduces a triangle mesh by quadric-error edge collapse (Garland–Heckbert). The difference is the stopping criterion: decimation stops at a target face count, while simplification stops at a geometric error budget. Flat regions collapse to almost nothing while curved detail and feature edges survive — the right tool when you care about fidelity rather than a specific size, such as cleaning up the over-sampled output of a boolean or arrangement.
Basic Usage
import trueform as tf
mesh = tf.Mesh(*tf.read_stl("model.stl"))
# Simplify within the default budget (0.2% of the bounding-box diagonal)
sim_faces, sim_points = tf.simplified(mesh)
From Tuple
# Also accepts (faces, points) tuples directly
sim_faces, sim_points = tf.simplified((faces, points))
With Configuration
sim_faces, sim_points = tf.simplified(
mesh,
error_rel=0.005, # allow more deviation -> fewer faces
optimize_iterations=3,
feature_angle=30,
)
| Parameter | Type | Default | Description |
|---|---|---|---|
data | Mesh or tuple | Triangle mesh or (faces, points) tuple | |
error_rel | float | 0.002 | Error allowed per collapse pass, as a fraction of the bounding-box diagonal: an edge is collapsed when its quadric error is <= error_rel * diagonal. With iterations > 1 it is re-applied each pass (against the current surface), so it caps per-pass error, not total deviation from the original |
optimize_iterations | int | 3 | Rounds of quality cleanup (min-angle edge flip + tangential relaxation) run after each collapse. 0 = pure error-budget collapse |
iterations | int | 1 | Outer collapse rounds. 1 = single collapse + cleanup; >1 re-collapses after each cleanup (iterated remesh), removing more at the cost of more deviation from the original |
relaxation_iters | int | 3 | Tangential relaxation passes per cleanup round |
lambda_ | float | 0.5 | Damping factor for tangential relaxation in (0, 1] |
min_quality | float | 0.3 | Worst triangle quality allowed after a collapse, in 0,1 (1 = equilateral). Negative disables, 0 = never worsen, >0 = quality floor |
preserve_boundary | bool | True | If True, boundary edges are never collapsed |
stabilizer | float | 1e-3 | Tikhonov stabilizer for quadric solve |
parallel | bool | True | Use parallel partitioned collapse |
feature_angle | float | -1.0 | Feature edge detection angle in degrees. Edges sharper than this are preserved. Negative disables |
feature_weight | float | 100.0 | Penalty weight for feature edge preservation. Higher = stronger |
preserve_regions | ndarray | None | Per-face region labels (one int per input face). When given, edges between differing labels are preserved as features and a third value — the output mesh's per-face labels (int32) — is returned. Keyword-only |
Quality Cleanup
A pure error-budget collapse leaves whatever triangulation the collapses produce, which can include thin slivers in flattened regions. Set optimize_iterations to run that many rounds of min-angle edge flip plus tangential relaxation after the collapse — feature- and boundary-aware — to even out the result:
sim_faces, sim_points = tf.simplified(mesh, optimize_iterations=5)
Iterated Remesh
A single collapse stops at the edges the quality guard blocked. Set
iterations > 1 to re-collapse after each cleanup round: the flip pass repairs
the slivers a collapse leaves behind, which unblocks collapses the previous
round refused, so more is removed. This turns simplification into an iterated
error-budget remesh — the error sibling of isotropic remeshing. It trades
fidelity for coarseness, since each round measures error against the
already-collapsed surface rather than the original.
sim_faces, sim_points = tf.simplified(mesh, iterations=3)
Region Preservation
To keep the boundaries between labelled regions (e.g. geological domains,
material groups) intact, pass preserve_regions with a per-face label array
(one int per input face). Edges between differently-labelled faces are treated
as features and never crossed, and the call returns the output mesh's per-face
labels (int32) alongside the mesh. This works the same way for decimated,
simplified, and isotropic_remeshed.
faces, points, labels = tf.simplified(mesh, preserve_regions=region_labels)
| Returns | Type | Description |
|---|---|---|
faces | ndarray | Face indices, shape (N, 3) |
points | ndarray | Vertex positions, shape (M, 3) |
labels | ndarray | Per-face region labels of the output mesh, shape (N,), int32. Returned only when preserve_regions is given |
Isotropic Remeshing
Redistribute vertices to achieve uniform edge lengths. Each iteration splits long edges, collapses short edges, flips edges to improve valence, and relaxes vertex positions tangentially.
Basic Usage
import trueform as tf
mesh = tf.Mesh(*tf.read_stl("model.stl"))
# Remesh to target edge length
mel = tf.mean_edge_length(mesh)
rem_faces, rem_points = tf.isotropic_remeshed(mesh, 2.0 * mel)
From Tuple
rem_faces, rem_points = tf.isotropic_remeshed((faces, points), 0.02)
With Configuration
rem_faces, rem_points = tf.isotropic_remeshed(
mesh, 0.02,
iterations=5,
relaxation_iters=5,
preserve_boundary=True,
use_quadric=True,
)
| Parameter | Type | Default | Description |
|---|---|---|---|
data | Mesh or tuple | Triangle mesh or (faces, points) tuple | |
target_length | float | Target edge length. Longer edges are split, shorter are collapsed | |
iterations | int | 3 | Number of outer iterations (split + collapse + flip + relax) |
relaxation_iters | int | 3 | Tangential relaxation iterations per outer iteration |
min_quality | float | 0.3 | Worst triangle quality allowed after a collapse, in 0,1 (1 = equilateral). Negative disables, 0 = never worsen, >0 = quality floor |
lambda_ | float | 0.5 | Damping factor for tangential relaxation in (0, 1] |
preserve_boundary | bool | True | If True, boundary edges are never split or collapsed |
use_quadric | bool | False | Use quadric error metric for collapse vertex placement |
parallel | bool | True | Use parallel execution |
feature_angle | float | -1.0 | Feature edge detection angle in degrees. Edges sharper than this are preserved. Negative disables |
feature_weight | float | 100.0 | Penalty weight for feature edge preservation. Higher = stronger |
preserve_regions | ndarray | None | Per-face region labels (one int per input face). When given, edges between differing labels are preserved as features and a third value — the output mesh's per-face labels (int32) — is returned. Keyword-only |
| Returns | Type | Description |
|---|---|---|
faces | ndarray | Face indices, shape (N, 3) |
points | ndarray | Vertex positions, shape (M, 3) |
labels | ndarray | Per-face region labels of the output mesh, shape (N,), int32. Returned only when preserve_regions is given |
Typical Pipeline
A common workflow is to decimate first, then isotropic remesh to improve triangle quality:
import trueform as tf
mesh = tf.Mesh(*tf.read_stl("model.stl"))
# Decimate to 5%
dec_faces, dec_points = tf.decimated(mesh, 0.05)
# Isotropic remesh to mean edge length of decimated result
mel = tf.mean_edge_length((dec_faces, dec_points))
dec_mesh = tf.Mesh(dec_faces, dec_points)
rem_faces, rem_points = tf.isotropic_remeshed(dec_mesh, mel, use_quadric=True)
tf.mean_edge_length to compute a natural target length from the current mesh. This is a good default for isotropic remeshing after decimation.