Clean
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
| Geometry | Removes |
|---|---|
| Points | Duplicate vertices |
| Polygons | Duplicate 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:
f—NDArrayInt32forward 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.keptIds—NDArrayInt32of 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
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,
});
