Release Notes
v0.10.1
Repairs; no API breaks. orient_faces_consistently and
ensure_positive_orientation now return whether the surface is
orientable: every reversal is decided against the input winding and
applied after the walk, so one call settles every orientable
manifold-edge component, and a non-orientable component is left
untouched rather than half-repaired. An integral coordinate type votes
by face count, its lattice not holding a squared area.
euler_characteristic counts boundary edges correctly — a disk is 1,
an uncapped tube 0. fit_similarity_alignment measures the source
spread in the same space as its covariance, so a scaled frame no longer
skews the recovered scale. The VTK wrappers delegate orientation to the
core and surface its verdict.
v0.10.0 — one engine
The engine
Every boolean runs on the CSG engine. tf.booleanUnion,
tf.booleanIntersection, and tf.booleanDifference build the same N-form
arrangement that powers tf.csgGraph and evaluate the operation as a
boolean expression over it. One arrangement answers any number of boolean
expressions, domain reads, and curve extractions; there is one pipeline, one
triangulator, one classification tier.
The arrangement is two tiers. The identity tier classifies polygon intersections exactly: every crossing, landing, and coincidence names one created identity on an exact integer lattice — a point's identity is a canonical name, not a coordinate. Coplanar faces, within a mesh and across meshes, pool onto one geometric plane, so coincident walls cannot disagree. The triangulation tier triangulates each plane against its own constraint set, resolving what cannot stand as given — split at its crossings, welded at its coincidences — in recovery waves that repeat until nothing new is stated. Classification reads the finished arrangement, and every product (boolean meshes, domain cells, the outer shell, intersection curves) is a read.
Coincidence is resolved at the root. When quantization collapses geometry — duplicate vertices, sub-epsilon edges, folded slivers, vertices landing on the intersection curve — the arrangement resolves each such coincidence to a single created identity, and the surrounding faces re-emit conformed to it. Raw arrangements are watertight by construction at these sites.
Tolerance is a statement about the input
A tolerance is the pitch the input's planes are quantized to; it never widens a predicate. One converter is built over the union of the input forms; every face's plane — direction and offset — is rounded onto a grid of that pitch, so two faces whose planes differ by less than it share one plane. Every original vertex is then placed, at most the tolerance away, onto the quantized planes its own faces stand on — the meet of three where it is a corner, a line of two where it lies on a crease, its own tangent plane where the surface is smooth. The result is the exact arrangement of that moved mesh: every predicate below the placement runs exact, and a weld is an identity, never a proximity. A tolerance of zero is the identity — nothing moves, no placement table is built, and the result is byte-identical to the exact arrangement of the input as given.
A wall doubled at less than the pitch becomes one wall; one doubled at more stays two. A vertex a weld retires has no output point of its own — the index map reports it absent. Plane identity is quantized-name equality, so results from calls with different converter domains are not identity-comparable; a session that needs one perturbation across calls pins one converter.
Provenance selection
One arrangement, any read. A selection names which forms' faces reach the output, orthogonally to the boolean algebra:
const graph = await tf.csgGraph([a, b, c]); // a=0, b=1, c=2
const expr = tf.op(0).or(tf.op(1)).sub(tf.op(2));
const solid = graph.mesh(expr); // as always
const partA = graph.mesh(expr, { selection: [0] }); // a's walls of it
const inA = graph.mesh(tf.op(0), { inside: [2] }); // c inside a
const embed = graph.mesh({ selection: [0] }); // a cut by all, no boolean
The per-form parts partition the result face-for-face and re-weld into the
solid. inside reads the other side of the same question — the named
forms' surface lying INSIDE the expression's region, both sides of a piece
in it, kept in the form's stored winding, which is how a sheet's surface
within a solid is asked for. Domain reads take the same selection — each
cell's walls by contributor — with the boundary read only. Every existing
expression call is unchanged.
The module map (breaking)
The cut module was named for the verb its first consumer needed. Its
ground is split by mechanism — arrangement for the arrangements and their
reads, iso for the scalar-field tier, csg for the booleans, expressions,
selections, domains, and the outer shell. There is no compatibility shim:
the old names are gone, not deprecated.
The surviving entry points keep their names, their overloads and their
options: tf.booleanUnion, tf.booleanIntersection, tf.booleanDifference,
tf.meshArrangements, tf.polygonArrangements, tf.isobands and
tf.isocontours are called exactly as before, sync and tf.async alike.
Only the modules they are documented and implemented under moved — and the
booleans moved once more, to CSG,
whose two-operand case they are.
Removed entry points
| Removed | Use instead |
|---|---|
embeddedIntersectionCurves | meshArrangements(meshes, { returnCurves: true }) |
embeddedSelfIntersectionCurves | polygonArrangements(mesh, { returnCurves: true }) |
type CutResultWithCurves | MeshArrangementResultWithCurves / PolygonArrangementResultWithCurves |
type OuterShellOptions | — (see below) |
The embedded curve entries answered a question the arrangements answer whole: the recut mesh and the curves embedded in it are one product of one arrangement, read through its own entry.
outerShell(mesh) takes no options. The shell is a structural read of a
settled arrangement — the boundary between the unbounded universe and
everything the mesh encloses — so there is nothing left to configure.
Types
| Old | New |
|---|---|
LabeledCutResult | LabeledBooleanResult |
LabeledCutResultWithCurves | LabeledBooleanResultWithCurves |
The result of booleanUnion / booleanIntersection / booleanDifference is
named for the operation that produces it. Its fields — mesh, labels,
faceLabels, and curves under { returnCurves: true } — are unchanged.
IsobandsResult, MeshArrangementResult and PolygonArrangementResult keep
their names.
Both types are re-exported from the package root, so a tf.LabeledCutResult
annotation becomes tf.LabeledBooleanResult; nothing else in a call site
changes.
Documentation
| Page | Contents |
|---|---|
| Arrangement | mesh and polygon arrangements, formerly Cut |
| CSG | the booleans, formerly on the Cut page |
| Iso | isocontours and isobands, formerly split across Cut and Intersect |
For the C++ names behind these bindings, see the C++ release notes.
