The Cut module provides scalar field cutting operations (isobands) and boolean operations on meshes.
Cut operations embed curves into mesh topology, splitting faces so the curves become edges:
All operations return face labels for classification.
Extract regions (bands) between consecutive threshold values from scalar fields:
import trueform as tf
import numpy as np
# Load mesh and create scalar field
faces, points = tf.read_stl("terrain.stl")
mesh = tf.Mesh(faces, points)
plane = tf.Plane(normal=[0, 0, 1], offset=0.0)
scalar_field = tf.distance_field(mesh.points, plane)
# Extract isobands
(band_faces, band_points), labels = tf.isobands(
mesh, scalar_field, [-1.0, 0.0, 1.0]
)
The labels array contains one integer per face, indicating which isoband the face belongs to. Faces are labeled 0, 1, 2, ... corresponding to regions (-∞, cut_values[0]), [cut_values[0], cut_values[1]), etc.
Select specific bands:
(faces, points), labels = tf.isobands(
mesh, scalar_field, cut_values, selected_bands=[1, 3]
)
Get boundary curves:
(faces, points), labels, (paths, curve_points) = tf.isobands(
mesh, scalar_field, cut_values, return_curves=True
)
for path_ids in paths:
curve_pts = curve_points[path_ids]
Isobands works with both triangle meshes and dynamic meshes (n-gons via OffsetBlockedArray):
| Input Mesh Type | Returns faces as |
|---|---|
| Triangle mesh | np.ndarray (N, 3) |
| Dynamic mesh | OffsetBlockedArray |
# Dynamic mesh input
quads = tf.as_offset_blocked(np.array([[0,1,2,3], ...], dtype=np.int32))
mesh = tf.Mesh(quads, points)
# Result faces are also OffsetBlockedArray
(band_faces, band_points), labels = tf.isobands(mesh, scalar_field, cut_values)
# band_faces is OffsetBlockedArray
Boolean operations combine two meshes using set operations.
Boolean operations accept PWN (piecewise winding number) meshes—plus the non-manifold flaps and geometric inconsistencies that real-world pipelines accumulate. These defects are modeled as independent noise: locally across polygon regions, globally across manifold edge-connected components.
Intersections collapse to canonical form via reduction diagrams in the ε-topology. The goal is commutative correctness: operations that commute with mesh idealization.
import trueform as tf
# Load meshes
mesh0 = tf.Mesh(*tf.read_stl("mesh0.stl"))
mesh1 = tf.Mesh(*tf.read_stl("mesh1.stl"))
# Union: A ∪ B
(faces, points), labels = tf.boolean_union(mesh0, mesh1)
# Intersection: A ∩ B
(faces, points), labels = tf.boolean_intersection(mesh0, mesh1)
# Difference: A - B
(faces, points), labels = tf.boolean_difference(mesh0, mesh1)
The labels array contains one integer per face, indicating which input mesh (0 or 1) the face originated from.
Get intersection curves:
(faces, points), labels, (paths, curve_points) = tf.boolean_union(
mesh0, mesh1, return_curves=True
)
for path_ids in paths:
curve_pts = curve_points[path_ids]
With transformations:
import numpy as np
# Attach transformations to meshes
# Translation for mesh1
translation = np.eye(4, dtype=np.float32)
translation[:3, 3] = [5, 0, 0]
mesh1.transformation = translation
# Boolean operations use transformed poses
(faces, points), labels = tf.boolean_union(mesh0, mesh1)
# Update transformation - no rebuild needed
for angle in range(0, 360, 10):
rotation = create_rotation_matrix(angle)
mesh1.transformation = rotation
(faces, points), labels = tf.boolean_union(mesh0, mesh1)
mesh.build_tree(), mesh.build_face_membership(), and mesh.build_manifold_edge_link().Boolean operations work with both triangle meshes and dynamic meshes (n-gons via OffsetBlockedArray):
| Input Mesh Types | Returns faces as |
|---|---|
| Both triangle meshes | np.ndarray (N, 3) |
| Either mesh is dynamic | OffsetBlockedArray |
# Triangle mesh + dynamic mesh
mesh0 = tf.Mesh(triangles, points0) # triangle mesh
mesh1 = tf.Mesh(tf.as_offset_blocked(quads), points1) # dynamic mesh
# Result is OffsetBlockedArray because mesh1 is dynamic
(faces, points), labels = tf.boolean_union(mesh0, mesh1)
# faces is OffsetBlockedArray
Embed self-intersection curves into mesh topology, splitting faces so intersections become edges:
import trueform as tf
# Load mesh with self-intersections
faces, points = tf.read_stl("self_intersecting.stl")
mesh = tf.Mesh(faces, points)
# Embed self-intersection curves
result_faces, result_points = tf.embedded_self_intersection_curves(mesh)
print(f"Result has {len(result_faces)} faces")
# Get curves as well
(faces, points), (paths, curve_pts) = tf.embedded_self_intersection_curves(
mesh, return_curves=True
)
print(f"Found {len(paths)} self-intersection curve(s)")
This is useful for:
Works with both triangle meshes and dynamic meshes:
| Input Mesh Type | Returns faces as |
|---|---|
| Triangle mesh | np.ndarray (N, 3) |
| Dynamic mesh | OffsetBlockedArray |