Tutorials
October 31, 2026
pythonintersectionstutorial

Fast and Exact Mesh Intersection Curves in Python

Compute the exact curves where two or more triangle meshes intersect in Python. Polylines out, with transformations and N-mesh input. 3.5 ms on 2×500K polygons. One pip install, NumPy in and out.

Žiga Sajovic, Polydera

Also available in C++ and JavaScript.

An intersection curve is the line where two surfaces cross. You need it to inspect a fit, to trim a part, to mark a seam, or to check a boolean before you run it. trueform computes these curves exactly, between two meshes or between many, and returns them as polylines. One pip install. NumPy arrays in, NumPy arrays out. This tutorial covers two meshes, moved meshes, N meshes, and how to use the curves.

trueform GitHub Documentation

Install

pip install trueform

See the installation guide for all options.

Loading the meshes

import trueform as tf
import numpy as np

mesh0 = tf.Mesh(*tf.read_stl("part.stl"))
mesh1 = tf.Mesh(*tf.read_obj("tool.obj"))

To follow along without files, build a sphere and a box:

sphere = tf.Mesh(*tf.make_sphere_mesh(1.0, stacks=40, segments=40))
box = tf.Mesh(*tf.make_box_mesh(1.5, 1.5, 1.5))

The box is smaller than the sphere, but its corners poke out. Each of its six faces cuts the sphere in a circle.

Curves between two meshes

tf.intersection_curves takes two meshes and returns (paths, points):

paths, curve_points = tf.intersection_curves(sphere, box)

len(paths)          # 6
curve_points.shape  # (562, 3)

Six closed curves, one per box face. If the meshes do not touch, paths is empty and curve_points has shape (0, 3).

The computation is exact. Shared edges, shared vertices and coplanar faces are classified, not guessed. Open meshes, non-manifold edges and self-intersecting inputs are accepted.

Curves between moved meshes

Most real cases have one part in a different pose. Set a 4×4 transformation on the mesh. The points are not copied or rewritten.

moved = tf.Mesh(*tf.make_sphere_mesh(1.0, stacks=40, segments=40))

T = np.eye(4, dtype=np.float32)
T[:3, 3] = [1.0, 0.0, 0.0]
moved.transformation = T

paths, curve_points = tf.intersection_curves(sphere, moved)
# one closed circle in the plane x = 0.5

To intersect a mesh with a moved copy of itself, use a shared view. It shares the points, faces and spatial tree. Only the transformation differs:

view = sphere.shared_view()
view.transformation = T

paths, curve_points = tf.intersection_curves(sphere, view)

The tree is built once. Moving the view does not rebuild it.

Curves between N meshes

Pass a list to get all pairwise curves in one call:

m0 = tf.Mesh(*tf.make_sphere_mesh(1.0, stacks=40, segments=40))
m1 = tf.Mesh(*tf.make_sphere_mesh(1.0, stacks=40, segments=40))
m2 = tf.Mesh(*tf.make_sphere_mesh(1.0, stacks=40, segments=40))

T1 = np.eye(4, dtype=np.float32); T1[:3, 3] = [1.0, 0.0, 0.0]
T2 = np.eye(4, dtype=np.float32); T2[:3, 3] = [0.5, 0.8, 0.0]
m1.transformation = T1
m2.transformation = T2

paths, curve_points = tf.intersection_curves([m0, m1, m2])

len(paths)  # 6

Three spheres make three pairwise circles. The circles cross each other on the surfaces. trueform splits the curves at every crossing point, so here you get six open arcs instead of three circles.

Working with results

paths is an OffsetBlockedArray. Each block is one curve, as indices into curve_points. A closed curve repeats its first index at the end.

for path_ids in paths:
    curve = curve_points[path_ids]          # (k, 3) polyline
    closed = path_ids[0] == path_ids[-1]
    length = np.linalg.norm(np.diff(curve, axis=0), axis=1).sum()

To query the curves as geometry, turn them into an EdgeMesh:

edges = np.concatenate(
    [np.stack([p[:-1], p[1:]], axis=1) for p in paths]
).astype(np.int32)

curves = tf.EdgeMesh(edges, curve_points)
tf.distance(curves, m0)  # 0.0, the curves lie on the surface

If you also want the boolean, ask for the curves on the same call:

(faces, points), labels, face_labels, (paths, curve_points) = \
    tf.boolean_union(sphere, moved, return_curves=True)

To split faces along the curves instead, see tf.mesh_arrangements. For the curves where one mesh meets itself, see detecting mesh self-intersections.

Performance

3.5 msIntersection curves2×500K polygons
14 msBoolean union2×500K polygons
28.5 msSpatial tree build1,025,766 polygons, vs 302.7 ms MeshLib

Apple M4 Max 16 threads

The curve and boolean timings are on two 500K-polygon meshes. The tree build is measured from Python against MeshLib on a 1,025,766-polygon mesh: 28.5 ms against 302.7 ms, 10.6× faster. Build it ahead of time with mesh.build_tree(). Changing a transformation does not rebuild it.

Full benchmarks · Documentation · How the algorithm works

Cite as
@article{polydera:mesh-intersection-curves-in-python,
  title={Fast and Exact Mesh Intersection Curves in Python},
  author={Sajovic, {\v{Z}}iga, Polydera},
  year={2026},
  url={https://polydera.com/tutorials/mesh-intersection-curves-in-python},
  organization={Polydera}
}