Modules | TS

Fill

Hole filling — patches that continue the surface around a mesh's boundary rims.

The Fill module closes holes. It takes a mesh and the rims its boundary walks, states one patch per rim, and replays those patches into the filled mesh. The patch continues the surface around the hole where the surface allows it, and falls back to a flat lid or an exact minimal triangulation where it does not.

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

Fill Holes

const rims = tf.boundaryRims(mesh);
const fill = tf.fillHoles(mesh, rims);
const filled = tf.filledMesh(mesh, fill);

// off the main thread
const fillAsync = await tf.async.fillHoles(mesh, rims, { splitRim: false });
const filledAsync = await tf.async.filledMesh(mesh, fillAsync);
ParameterTypeDescription
meshMeshThe mesh the rims are read against
rimsBoundaryRimsResultThe rims tf.boundaryRims returns
opts.minQualitynumber, default 0.3The floor a table patch is refined toward; 0 asks for none
opts.fairingboolean, default trueWhether a refined table patch is faired against its host
opts.splitRimboolean, default trueWhether a tier may split the host's original rim edges

Each rim is one group, handed down three tiers: exhaustive enumeration for a rim of at most five vertices; then the chart, which reads the hole as a height field over the plane its rim spans and continues the surface around it — or, where it refuses and the rim lies exactly in one plane, the flat lid in that plane; and otherwise the table, the patch with the best smallest angle and least area over a tetrahedralization of the rim, refined and faired.

The chart decides on the mesh's own exact lattice, so a rim of only a few lattice units per edge gets the lid or a refusal rather than an invented surface.

With splitRim, a tier may split a rim edge; the split and the carrying face's new triangles are published in the result and replayed by filledMesh, so the filled mesh stays watertight. Without it no original edge is touched, and a rim the table could only fill by a split refuses as refusedRimSplit.

The Result

HoleFill is laid out per group: entry g of every per-group lane belongs to rim g; jagged lanes are rows beside [G + 1] offsets.

FieldTypeMeaning
triangleOffsets, trianglesNDArrayInt32 [G+1], [T, 3]The patch triangles per group
mintedOffsets, mintedPointsNDArrayInt32, [M, 3] floatThe points each patch minted
status, charted, refined, fairedNDArrayInt8 [G]Values of tf.HoleFillStatus, tf.HoleChartStatus, tf.HoleRefineStatus, tf.HoleFairStatus
seamMaxAnglefloat [G]Largest angle across the seam in radians, -1 where not computed
offendingNDArrayInt32 [G, 2]The (rim, edge) a refusal names, (-1, -1) where none
splits, splitParametersNDArrayInt32 [S, 4], [S]Split edges as (face, v0, v1, point), parameter over 64 from v0
planFaces, planOffsets, planTrianglesNDArrayInt32The carrying faces the splits reached, and what each becomes
groupOffsetsNDArrayInt32 [G+1]The run of rims each group fills
const status = fill.status.data;
for (let g = 0; g < status.length; g++) {
  if (status[g] === tf.HoleFillStatus.filled) continue;
  const [rim, edge] = [fill.offending.data[2 * g], fill.offending.data[2 * g + 1]];
  console.log(`rim ${rim} refused at edge ${edge}`);
}
tf.deleteHoleFill(fill);   // releases every lane at once

tf.HoleFillStatus is filled, refusedOpen, refusedInvalidRim, refusedHostFace, refusedProtectionDepth, refusedTable, refusedResource or refusedRimSplit. tf.HoleChartStatus is admitted (the chart continues the host), rimPlane (the flat lid), notAttempted, or the reason the chart refused: noArea, steep, folded, collarArity, uncovered, unsampled, unsolved, forbiddenChord, resource, outOfLattice.

Filled Mesh

tf.filledMesh(mesh, fill) returns a new Mesh: the input with what the fill changed. A face no split reached is copied, a carrying face a split reached is replaced by its plan, and the patches follow. The points are the input's, then the minted ones.

Every lane of a HoleFill owns its WASM storage, so it outlives the mesh and rims it was computed from. Release it with tf.deleteHoleFill(fill) when done.