Modules | TS

Intersect

Exact mesh intersections and self-intersections.

The Intersect module computes geometric intersections between meshes. All intersection computations are geometrically and topologically exact, using exact arithmetic.

import * as tf from "@polydera/trueform";

Overview

The Intersect module computes intersection geometry without modifying the input meshes:

  • Mesh-mesh intersections: Curves where two or more meshes intersect
  • Self-intersections: Curves where a mesh intersects itself

Curves are returned as Curves objects with paths (an OffsetBlockedBuffer of index sequences) and points (an NDArrayFloat32 of coordinates).

To embed intersection curves into mesh topology (splitting faces along curves), use the Arrangement module. For scalar field crossings and their curves, see the Iso module.

Supported Input

Intersection computation supports a wide range of input geometry:

  • Open and closed meshes — boundaries are handled correctly
  • Non-manifold edges — edges shared by 3 or more faces
  • Coplanar faces — overlapping faces from the same or different meshes
  • Self-intersecting geometry — meshes that intersect themselves are detected and curves are extracted
  • Crossing intersection curves — where curves from different mesh pairs meet on a face, crossings can be resolved. Configured via the IntersectOpts interface — see Intersection Configuration.
For detecting where a single mesh intersects itself, use tf.selfIntersectionCurves.

Intersection Configuration

The IntersectOpts interface controls intersection computation. It maps to the C++ tf::intersect_config (mode + tolerance) extended with the contour-crossing flags.

interface IntersectOpts {
  mode?: "sos" | "primitives";
  tolerance?: number;
  resolveCrossings?: boolean;
  resolveSelfCrossings?: boolean;
  within?: boolean;
}
FieldDescription
mode"sos" (Simulation of Simplicity — fast, no degenerate cases) or "primitives" (handles shared edges/vertices/coplanar faces).
toleranceWorld-coordinate distance an input vertex may move to reach the lattice (default 0, exact).
resolveCrossingsCrossings between contours of different classes — (A,B) against (A,C). Such a pair needs a third mesh to exist, so the pipeline derives this from the number of operands; the flag is declarative.
resolveSelfCrossingsCrossings within one contour class (A,B): a contour with itself, or with another contour of the same pair — e.g. two disjoint components of one mesh cutting the same face.
withinAlso intersect each mesh with itself. Required when a mesh can self-overlap, e.g. meshes concatenated into one input; implies self-crossing resolution. Applies to arrangements and booleans only — intersectionCurves ignores it; for a mesh against itself use selfIntersectionCurves. Default false.

Tolerance

A non-zero tolerance is a statement about the INPUT, not about a predicate: it is the pitch the input's planes are quantized to. Every face's plane — its direction and its offset — is rounded onto a grid of that pitch, so two faces whose planes differ by less than it get one plane; every vertex is then moved, by at most the tolerance, onto a lattice point of the quantized planes its own faces stand on — the meet of three at a corner, a line of two on a crease, its own tangent plane where the surface is smooth — and the result is the EXACT arrangement of that moved mesh: every predicate below the placement runs exact. Two features closer than the tolerance therefore meet only when their quantized planes agree and the placement puts them on the same lattice point: a weld is identity, not proximity, and one form's rim is never dragged onto another's wall for being near it.

const { mesh, tagLabels, faceLabels } = tf.meshArrangements(
  [mesh0, mesh1], { tolerance: 1e-6 });

A tolerance of 0 is the identity: nothing moves, no placement table is built, and the result is the exact arrangement of the input as given.

Each function sets appropriate defaults — see individual function documentation below and in Arrangement.

Intersection Curves

Between Two Meshes

const curves = tf.intersectionCurves(mesh0, mesh1);
const curves = tf.intersectionCurves(mesh0, mesh1, { mode: "primitives" });

Default: mode: "primitives", resolveCrossings: false, resolveSelfCrossings: false. With two meshes every contour is of the one class (A,B); crossings among them — e.g. from disjoint components of one mesh — are resolved by resolveSelfCrossings or within.

N-Mesh Intersection Curves

Compute all pairwise intersection curves from an array of meshes:

const curves = tf.intersectionCurves([mesh0, mesh1, mesh2]);
const curves = tf.intersectionCurves([mesh0, mesh1, mesh2], {
  mode: "primitives", resolveCrossings: true,
});

Default: mode: "primitives", resolveCrossings: true (for 3+ meshes), resolveSelfCrossings: false.

Self-Intersection Curves

Find where a mesh intersects itself:

const curves = tf.selfIntersectionCurves(mesh);
const curves = tf.selfIntersectionCurves(mesh, { mode: "primitives" });

Default: mode: "primitives", resolveCrossings: true, resolveSelfCrossings: true.

To embed self-intersection curves into mesh topology, use tf.polygonArrangements from the Arrangement module.

With Transformations

mesh1.transformation = tf.makeTranslation([5, 0, 0]);
const curves = tf.intersectionCurves(mesh0, mesh1);

Using Curves

for (let i = 0; i < curves.length; i++) {
  const pathIds = curves.paths.at(i);
  // Access points: curves.points
}

Async

All intersect functions are available as async variants via tf.async for off-main-thread execution:

const curves = await tf.async.intersectionCurves(mesh0, mesh1);
const self   = await tf.async.selfIntersectionCurves(mesh);
For implementation details, exact arithmetic, and low-level intersection access, see the C++ Intersect documentation.