Appearance
Mesh decimation (QEM)
What it does
Reduces the number of triangles in a surface while preserving its overall shape. Iteratively collapses the edge that introduces the smallest geometric error, as measured by the quadric error metric (Garland & Heckbert 1997).
Complementary to isotropic remeshing: decimation reduces facet count and is shape-preserving (silhouette priority); isotropic remeshing equalizes edge length without changing the count. Reach for decimation when "this 2 M-facet STL needs to fit in 100 k facets for cut-cell tagging." Reach for remesh when "the tessellation is irregular and tripping AABB query balance."
Pipeline
- Per-vertex 4×4 quadrics. For each vertex, accumulate
where for plane equation with normal and offset . The quadric measures squared distance to all incident facet planes. - Per-edge optimal collapse target. For each edge
, the minimizer of is the optimal collapse position. 3×3 Cramer's-rule solve; midpoint fallback if singular. - Min-heap, keyed on collapse cost. Decrease/increase-key supported (~80 LOC inline) so neighbour edges can have their costs re-evaluated after each collapse without rebuilding the heap.
- Iterative collapse with three safety checks per candidate:
- Normal-flip rejection: if any incident triangle's normal would flip, the collapse is skipped.
- Duplicate-vertex rejection: if the collapse would merge two vertices that already exist on the same facet (creating a degenerate triangle), skipped.
- Non-manifold-edge rejection: if the collapse would create an edge with > 2 incident facets, skipped. When all remaining edges fail one of these checks, the algorithm returns
DEC_STATUS_NO_PROGRESS— the smallest count it could safely reach.
API
fortran
call surface%decimate(target_facets, status)
class(surface_stl_object), intent(inout) :: self
integer(I4P), intent(in) :: target_facets
integer(I4P), intent(out), optional :: status ! DEC_STATUS_*target_facetsis the upper bound on the output facet count. The algorithm collapses edges until it reaches this count or cannot safely collapse any more.status:DEC_STATUS_OK— reachedtarget_facetsexactly.DEC_STATUS_NO_PROGRESS— couldn't reach the target; surface is at the smallest count the safety checks allowed.DEC_STATUS_BAD_INPUT—target_facets <= 0, or the input surface has no facets.
After return, self's AABB tree, vertex pool, connectivity, and pseudo-normals are all rebuilt — no further user action needed before calling distance, winding_number, boolean, or any other query.
Example
fortran
program ex_decimate
use fossil
use penf, only : I4P, R8P
implicit none
type(surface_stl_object) :: bunny
integer(I4P) :: n0, status
call bunny%load_from_file(file_name='src/tests/bunny.stl', guess_format=.true.)
n0 = bunny%get_facets_number()
print '(A,I0)', 'input bunny: n_facets = ', n0
! Decimate to 25% of original.
call bunny%decimate(target_facets=n0 / 4_I4P, status=status)
print '(A,I0,A,I0)', 'after 25%: n_facets = ', bunny%get_facets_number(), &
' status=', status
endprogram ex_decimateVisual reference
Stanford bunny — input (69 451 facets):

After decimation to 25 % of input (17 362 facets):

The silhouette is preserved; per-facet area roughly quadruples (each remaining facet covers roughly 4× the area of an original). The ear-and-chin detail is preserved because those regions have high quadric error per edge, so the heap keeps them out of the collapse queue until the budget forces it.
Known limitations
These are the documented gaps from §1.3's implementation issue, deliberately surfaced here so the user knows what they're getting. Production-grade quality requires fixing all four:
- No tie-breaking heuristic. On perfectly flat inputs (a flat cube face, an axis-aligned rectangle) every edge has cost ≈ 0 and the algorithm picks arbitrarily. Result: degenerates on cube-shaped inputs, where any collapse produces equally-valid (or equally-bad) output.
- No volume-preservation term. Collapses pull vertices inward, so volume drifts down monotonically with iteration. Sphere-via-MC decimated to 50 % / 25 % / 10 % loses ~9 % / ~13 % / ~24 % of volume respectively. Lindstrom-Turk 1998 adds a volume- preservation Lagrange multiplier; deferred.
- No boundary preservation. Edges on a mesh boundary (open shells) are treated like interior edges; the boundary moves during collapse. Irrelevant for closed solids (the typical CFD case); breaks open shell decimation.
- No link condition. Some edge collapses produce non-manifold vertices even when the per-edge safety checks pass. In practice, the three existing checks catch ~99 % of pathological cases on CFD-relevant inputs.
See also
- isotropic remeshing — the complementary primitive for tessellation regularity.
surface%get_facets_number— read the count before/after decimation.- Constants —
DEC_STATUS_*.
References
- Garland & Heckbert, Surface Simplification Using Quadric Error Metrics, SIGGRAPH 1997. The reference algorithm.
- Lindstrom & Turk, Fast and memory efficient polygonal simplification, IEEE Visualization 1998. Adds the volume- preservation term that addresses the documented sphere drift.
- Hoppe, New quadric metric for simplifying meshes with appearance attributes, IEEE Visualization 1999. Generalization to colored meshes; not relevant to FOSSIL's per-vertex-coordinate-only API.