Modules | PY

Cut

Mesh booleans and scalar field cutting operations.

The Cut module provides scalar field cutting operations (isobands) and boolean operations on meshes.

Overview

Cut operations embed curves into mesh topology, splitting faces so the curves become edges:

  • Isobands: Cut along scalar field thresholds, label regions by band
  • Booleans: Cut along mesh intersection curves, combine by set operation
  • Embedded intersection curves: Embed intersection curves from mesh B into mesh A
  • Embedded self-intersections: Cut where mesh intersects itself

Boolean operations and isobands return face labels for classification.

Isobands

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(tf.Point(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]

Dynamic Mesh Support

Isobands works with both triangle meshes and dynamic meshes (n-gons via OffsetBlockedArray):

Input Mesh TypeReturns faces as
Triangle meshnp.ndarray (N, 3)
Dynamic meshOffsetBlockedArray
# 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

Boolean operations combine two meshes using set operations.

Input Mesh Requirements

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.

See our Research for the conceptual model and formal definitions.

Operations

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)
Required topology structures (tree, face_membership, manifold_edge_link) are built automatically on first use. For performance-critical applications, prebuild using mesh.build_tree(), mesh.build_face_membership(), and mesh.build_manifold_edge_link().

Dynamic Mesh Support

Boolean operations work with both triangle meshes and dynamic meshes (n-gons via OffsetBlockedArray):

Input Mesh TypesReturns faces as
Both triangle meshesnp.ndarray (N, 3)
Either mesh is dynamicOffsetBlockedArray
# 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

Embedded Intersection Curves

Embed intersection curves between mesh A and mesh B into mesh A, without performing boolean selection. All faces from mesh A are preserved (split where intersecting), with no faces from mesh B:

import trueform as tf

# Load two meshes
mesh0 = tf.Mesh(*tf.read_stl("mesh0.stl"))
mesh1 = tf.Mesh(*tf.read_stl("mesh1.stl"))

# Embed intersection curves into mesh0
faces, points = tf.embedded_intersection_curves(mesh0, mesh1)
print(f"Result has {len(faces)} faces")

# Get curves as well
(faces, points), (paths, curve_pts) = tf.embedded_intersection_curves(
    mesh0, mesh1, return_curves=True
)
print(f"Found {len(paths)} intersection curve(s)")

This is useful when you need to mark where meshes intersect without carving—for example, projecting cutting guides onto a surface or visualizing contact regions.

Unlike boolean operations, this function does not remove any faces from mesh0. The output mesh has the same volume and surface area as mesh0, with additional edges along the intersection curves.

Dynamic Mesh Support

Result type depends only on mesh0 since only mesh0's faces are returned:

mesh0 Typemesh1 TypeReturns faces as
Triangle meshAnynp.ndarray (N, 3)
Dynamic meshAnyOffsetBlockedArray
# Triangle mesh0 with dynamic mesh1 → result is triangle
mesh0 = tf.Mesh(triangles, points0)
mesh1 = tf.Mesh(tf.as_offset_blocked(quads), points1)
faces, points = tf.embedded_intersection_curves(mesh0, mesh1)
# faces is np.ndarray (N, 3)

# Dynamic mesh0 with triangle mesh1 → result is dynamic
mesh0 = tf.Mesh(tf.as_offset_blocked(quads), points0)
mesh1 = tf.Mesh(triangles, points1)
faces, points = tf.embedded_intersection_curves(mesh0, mesh1)
# faces is OffsetBlockedArray

Embedded Self-Intersection Curves

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:

  • Mesh repair pipelines
  • Preparing meshes for boolean operations
  • Visualization of self-intersection regions

Dynamic Mesh Support

Works with both triangle meshes and dynamic meshes:

Input Mesh TypeReturns faces as
Triangle meshnp.ndarray (N, 3)
Dynamic meshOffsetBlockedArray
For extracting isocontour curves without cutting, see Intersect. For implementation details, see C++ Cut.