Modules | TS

Topology

Connectivity analysis, boundary detection, and component labeling.

The Topology module provides tools for analyzing connectivity and structure in meshes — adjacency structures, boundary detection, manifold analysis, connected components, and neighborhood queries.

Every function also has an async variant via tf.async for off-main-thread execution.

Adjacency Structures

Meshes lazily compute and cache adjacency structures on first access. These structures power the topology queries below.

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

const mesh = tf.mesh(faces, points);

// Face membership — vertex → faces containing it
const fm = mesh.faceMembership;   // OffsetBlockedBuffer

// Manifold edge link — face edge → adjacent face
const mel = mesh.manifoldEdgeLink; // NDArrayInt32 [F, 3]

// Face link — face → adjacent faces (sharing an edge)
const fl = mesh.faceLink;          // OffsetBlockedBuffer

// Vertex link — vertex → connected vertices
const vl = mesh.vertexLink;        // OffsetBlockedBuffer
PropertyTypeDescription
faceMembershipOffsetBlockedBufferVertex → faces containing it
manifoldEdgeLinkNDArrayInt32 [F, N]Face edge → adjacent face index
faceLinkOffsetBlockedBufferFace → adjacent faces
vertexLinkOffsetBlockedBufferVertex → connected vertices
ValueMeaning
>= 0Adjacent face index
-1Boundary edge (no adjacent face)
-2Non-manifold edge (3+ faces)
-3Non-manifold representative

Mesh Analysis

Closed / Open

Check if a mesh is closed (watertight) or open (has boundary edges).

tf.isClosed(mesh);  // true if every edge shared by exactly 2 faces
tf.isOpen(mesh);     // true if at least one boundary edge

A closed mesh is watertight — suitable for volume calculations and boolean operations.

Use tf.boundaryEdges(mesh) to get the actual boundary edges when isOpen returns true.

Manifold / Non-Manifold

Check if a mesh is manifold (no edge shared by more than 2 faces).

tf.isManifold(mesh);     // true if every edge shared by at most 2 faces
tf.isNonManifold(mesh);  // true if any edge shared by 3+ faces

Non-manifold edges typically occur at T-junctions where three or more faces meet at a single edge.

Use tf.nonManifoldEdges(mesh) to get the actual non-manifold edges when isNonManifold returns true.

Euler Characteristic

const chi = tf.eulerCharacteristic(mesh);  // V - E + F

Boundary Detection

Boundary Edges

Extract boundary edges — edges belonging to only one face.

const edges = tf.boundaryEdges(mesh);  // NDArrayInt32 [N, 2]

Boundary Paths

Organize boundary edges into connected loops of vertex indices.

const paths = tf.boundaryPaths(mesh);  // OffsetBlockedBuffer

Non-Manifold Edges

Find edges shared by more than two faces.

const edges = tf.nonManifoldEdges(mesh);  // NDArrayInt32 [N, 2]

Neighborhoods

k-Ring

Compute k-ring neighborhoods for all vertices using breadth-first traversal. The 1-ring is the immediate neighbors, the 2-ring includes neighbors of neighbors, etc.

// 2-ring neighborhoods for all vertices
const rings = tf.kRings(mesh, 2);  // OffsetBlockedBuffer

// Include the seed vertex in each neighborhood
const ringsInc = tf.kRings(mesh, 2, true);

Radius-Based

Find all vertices reachable via mesh edges within a Euclidean distance.

// All vertices within radius 0.5
const neighs = tf.neighborhoods(mesh, 0.5);  // OffsetBlockedBuffer

// Include seed vertex
const neighsInc = tf.neighborhoods(mesh, 0.5, true);

Connected Components

From Mesh

connectedComponents computes connected components of a mesh using a specified connectivity type.

const { labels, nComponents } = tf.connectedComponents(mesh, "edge");
console.log(`${nComponents} components`);
// labels: NDArrayInt32 [F] — component ID per face
TypeConnectivityDescription
"edge"faceLinkFaces connected by any shared edge
"manifoldEdge"manifoldEdgeLinkFaces connected by manifold edges only
"vertex"vertexLinkVertices connected by shared edges

From Connectivity

labelConnectedComponents works on raw connectivity data — either an OffsetBlockedBuffer (variable-width) or an NDArrayInt32 (fixed-width, -1 for missing neighbors).

// Variable-width connectivity (e.g., faceLink)
const { labels, nComponents } = tf.labelConnectedComponents(mesh.faceLink);

// Fixed-width connectivity (e.g., manifoldEdgeLink)
const result = tf.labelConnectedComponents(mesh.manifoldEdgeLink);

Face Orientation

Make all faces in each connected region have consistent winding order.

const oriented = tf.consistentlyOriented(mesh);

The function uses flood-fill through manifold edges to propagate orientation. Non-manifold edges act as barriers between regions. The final orientation preserves the majority area within each region.

For outward-pointing normals on closed meshes, use tf.positivelyOriented(mesh) from the Geometry module, which calls consistentlyOriented internally and then flips faces if needed.

Path Finding

Connect edges into continuous vertex paths between branch points and endpoints.

const paths = tf.connectEdgesToPaths(edges);  // OffsetBlockedBuffer

Edges are organized into maximal paths between endpoints and junctions (vertices with degree ≠ 2). Works on any edge collection — useful for organizing boundary segments, cut curves, and graph decomposition.

Async

All functions are available as tf.async.isClosed(), tf.async.boundaryEdges(), tf.async.connectedComponents(), etc. Signatures are identical — they return Promise<T> instead of T.

const closed = await tf.async.isClosed(mesh);
const edges = await tf.async.boundaryEdges(mesh);
const { labels, nComponents } = await tf.async.connectedComponents(mesh, "edge");