Modules | TS

Clean

Remove duplicates, degenerates, and unreferenced elements.

The Clean module removes duplicate, degenerate, and unreferenced elements from geometric data. It operates on meshes, point batches, and triangle soups, maintains referential integrity, and provides optional index maps for reindexing associated data.

What Gets Removed

GeometryRemoves
PointsDuplicate vertices
PolygonsDuplicate vertices, duplicate faces, degenerate faces, unreferenced vertices

Definitions:

  • Duplicate vertices: Points at the same location (exact or within tolerance)
  • Duplicate faces: Faces with the same vertex set (any cyclic rotation, either winding)
  • Degenerate faces: Faces with fewer than 3 unique vertices (zero area)
  • Unreferenced vertices: Vertices not used by any face

When duplicates are removed, the element with the smallest index is kept.

Meshes

Clean a mesh by removing duplicate/degenerate vertices and faces:

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

const mesh = tf.readStl("model.stl");

// Exact duplicate removal
const clean = tf.cleaned(mesh);

// Tolerance-based: merge vertices within distance
const clean2 = tf.cleaned(mesh, 1e-6);

With Index Maps

Get index maps for reindexing associated data:

const { mesh: clean, faceMap, pointMap } = tf.cleaned(mesh, {
  returnIndexMap: true,
  tolerance: 1e-6,
});

// Reindex per-face attributes using keptIds
// faceMap.keptIds: original face indices that survived
// pointMap.keptIds: original vertex indices that survived

Points

Remove duplicate points from point batches:

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

const pts = tf.point([[0, 0, 0], [1, 0, 0], [0, 0, 0]]);

// Exact duplicate removal
const clean = tf.cleaned(pts);

// Tolerance-based
const clean2 = tf.cleaned(pts, 1e-6);

// With index map
const { points: clean3, pointMap } = tf.cleaned(pts, {
  returnIndexMap: true,
  tolerance: 1e-6,
});

Triangle Soups

Triangle soups (batch primitives with unindexed geometry) can be cleaned, converting them to an indexed Mesh with shared vertices:

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

// Triangle batch: each triangle has 3 vertices
const soup = tf.triangle(
  [[0, 0, 0], [1, 0, 0], [2, 0, 0]],  // v0s
  [[1, 0, 0], [2, 0, 0], [3, 0, 0]],  // v1s
  [[0.5, 1, 0], [1.5, 1, 0], [2.5, 1, 0]],  // v2s
);

// Clean converts to indexed Mesh with shared vertices
const mesh = tf.cleaned(soup);

// With tolerance
const mesh2 = tf.cleaned(soup, 1e-6);

Understanding Index Maps

When returnIndexMap: true is set, cleaning operations return IndexMap objects:

  • fNDArrayInt32 forward mapping of length N (original count). f[oldId] gives the new index for kept elements. Removed elements map to a sentinel value equal to the original count.
  • keptIdsNDArrayInt32 of original indices that were kept, length M (new count).
const { mesh: clean, faceMap, pointMap } = tf.cleaned(mesh, {
  returnIndexMap: true,
});

// faceMap.f[oldFaceId] → new face index (or sentinel if removed)
// faceMap.keptIds → original face indices that survived
// pointMap.f[oldVertexId] → new vertex index (or sentinel if removed)
// pointMap.keptIds → original vertex indices that survived
See the Reindex module for more ways to use index maps.
All cleaning operations maintain referential integrity: after removing elements, all face indices are correctly updated to reference the new vertex positions.

Async

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

const clean = await tf.async.cleaned(mesh, 1e-6);
const { mesh: m, faceMap, pointMap } = await tf.async.cleaned(mesh, {
  returnIndexMap: true,
});
For implementation details, see the C++ Clean documentation.