pub fn fill_holes(
tx: &mut Transaction<'_>,
options: &FillOptions,
) -> Result<FillReport>Expand description
Fills boundary loops with triangle patches, chosen by size or by id.
- Selection: the loops of
tx.mesh()(asboundary_loopsfinds them) that meet every bound that is set:max_edges,max_perimeterandmax_area(againstBoundaryLoop::area). Withloops, only those loops are candidates, still subject to any bound that is set; the ids are matched by content, so an id whose loop changed or vanished, also through an earlier operation in the same transaction, fails the call. Loops that are open chains, or not clean unlessallow_unclean, are skipped. An open mesh’s intended border (a hemisphere’s rim) is kept by a bound below its size. A loop that crosses an orientation seam (a flipped triangle on its rim) is not clean, so runorient()first on meshes that may contain flipped triangles. - Each selected loop is triangulated on its own: up to
dp_limitedges by Liepa’s minimum-weight search (the worst dihedral angle to the neighbouring triangles first, then area), above it by projection onto the loop’s best-fit plane and ear clipping, which fails withFailReason::ProjectionFoldswhen the projection folds. Withmax_dihedral, a search whose best triangulation folds past it fails withFailReason::NoValidTriangulation. No new edge duplicates one the mesh has, so the patch never makes an edge non-manifold, and the patch triangles are wound to agree with their neighbours. A loop whose patch would reuse a diagonal planned for an earlier loop of the same call fails too. Two holes sharing a vertex are two loops, each filled. - The patch is refined to the density of the mesh around the loop
(Liepa’s rule: centroid splits while a triangle is coarse for the mean
edge length at its corners, with Delaunay edge flips between rounds of
splits), up to
max_refine_verticesnew vertices when set. Each new vertex has its triangle’s corners as creation parents, 1/3 each. - With
fair, the new vertices are moved to minimise bending, holding the loop and the ring of mesh vertices around it fixed: the discrete bi-Laplacian, cotangent weights falling back to uniform ones (FilledLoop::weights), factored once as a sparse LDLᵀ and solved for the three coordinates. A solve that does not reach its tolerance reportsconverged: false; one that is not finite fails the loop withFailReason::Solver. - Every selected loop is planned before the first write; the new
vertices of the successful patches are then added in creation order,
with their parents, and the triangles in one
add_trianglescall. A failed loop is reported and writes nothing.
§Errors
OpsError::InvalidInputwhen no bound and noloopsare given, or a bound (ormax_dihedral) is NaN or negative.OpsError::StaleLoopswhen an id inloopsmatches no loop of the mesh.OpsError::Storewhen memory runs out.
Nothing is written in any of these cases.