2. Repairing the surface
Chapter 1 found that the dragon is neither watertight nor manifold. Before it goes into a grid, sanitize repairs what can be repaired, and the validity predicates say what is left.
use fossil, only : surface_stl_object
use penf, only : I4P, R8P
implicit none
type(surface_stl_object) :: surface, original
integer(I4P) :: facets_before
real(R8P) :: lower, upper
call surface%load_from_file(file_name='dragon.stl', guess_format=.true.)
facets_before = surface%get_facets_number()call surface%sanitize(fill_holes=.true.)sanitizeruns the repair passes in order:remove_degenerate_facets(zero-area and sliver facets),connect_nearby_vertices(only when facets have disconnected edges),analyze,remove_duplicate_facets(the same triangle twice, in any winding), thensanitize_normals(consistent winding, outward by the sign of the volume). The surface is analyzed again after every pass that changes it.fill_holes=.true.addsfill_holesafter the orientation pass: every closed loop of boundary edges with at mostmax_hole_edgesedges (default 200) and a bounding-box diagonal at mostmax_hole_sizetimes the surface's (default 0.1) is closed by a minimum-weight patch. Larger loops stay open, so a genuine opening (an inlet, a symmetry cut) survives.refine_holes=.true.andfair_holes=.true.refine the patches and fair them to a smooth surface. The default isfill_holes=.false..
A hole is a loop of boundary edges, the facet edges without a neighbour (fcon_edge == 0); fill_holes triangulates it, refine matches the density of the loop and fair smooths the patch into the surface around it.
- Each defect class left or removed is reported once on stderr, as a
WARNING: sanitize:line.
print '(A,I0,A,I0)', 'facets ', facets_before, ' -> ', surface%get_facets_number()
print '(A,I0)', 'degenerate ', surface%get_degenerate_facets_removed()
print '(A,I0)', 'duplicate ', surface%get_duplicate_facets_removed()
print '(A,I0)', 'non-manif. ', surface%get_non_manifold_edges_number()
print '(A,F12.4)', 'volume ', surface%get_volume()
print '(A,L1,A,L1,A,L1)', 'watertight ', surface%is_watertight(), ' manifold ', surface%is_manifold(), &
' volume ', surface%is_volume()
call surface%save_into_file(file_name='dragon-clean.stl')
print '(A)', 'saved dragon-clean.stl'get_degenerate_facets_removedandget_duplicate_facets_removedcount what the last passes dropped.is_volume()is true when the surface is manifold, its signed volume positive and its centroid finite: the condition for the pseudo-normal sign of chapter 4 to be exact.save_into_filewithoutis_asciiwrites ASCII.
How far did the repair move the dragon?
call original%load_from_file(file_name='dragon.stl', guess_format=.true.)
call surface%deviation(original, lower=lower, upper=upper)
print '(A,ES10.3,A,ES10.3)', 'repaired -> original ', lower, ' <= h <= ', upper
call original%deviation(surface, lower=lower, upper=upper)
print '(A,ES10.3,A,ES10.3)', 'original -> repaired ', lower, ' <= h <= ', upperdeviation(other, lower=, upper=)brackets the one-sided Hausdorff distance h(self, other): how far the farthest point of self is from other. The bracket is certified, to 1e-3 of the bounding-box diagonal by default.- The two directions differ. The repaired dragon's farthest points are in the middle of the hole patches. The original's farthest points are on the facets
sanitizedropped as degenerate or duplicate. See surface deviation.
Running it
$ tut02_repair
WARNING: sanitize: 54 hole(s) filled
WARNING: sanitize: 136 boundary edge(s) not forming a closed loop
WARNING: sanitize: 62 duplicate facet(s) removed
WARNING: sanitize: 188 facet(s) with 1 disconnected edge remain
WARNING: sanitize: 14 facet(s) with 2 disconnected edges remain
WARNING: sanitize: 54 non-manifold edge(s) (3+ incident facets) detected
facets 6588 -> 6586
degenerate 0
duplicate 62
non-manif. 54
volume 465.9329
watertight F manifold F volume F
saved dragon-clean.stl
repaired -> original 9.439E-02 <= h <= 1.190E-01
original -> repaired 3.009E-03 <= h <= 2.968E-02The repair removed 62 duplicate facets and filled 54 holes, but the dragon is still not watertight: 54 edges carry three or more facets, and 136 boundary edges do not close into a loop, so fill_holes cannot patch them. sanitize fixes local defects; it does not rebuild a surface. The rest of the tutorial uses this cleaned but open dragon on purpose: it shows where the choice of sign algorithm matters (chapters 4 and 5) and which cut cells cannot be trusted (chapter 10). For a watertight surrogate of a broken surface see Alpha wrapping.
What you learned
sanitize (with fill_holes=.true. for small holes) repairs local defects and reports them on stderr; is_watertight, is_manifold and is_volume tell whether the result is a closed solid, and deviation how far the repair moved it. Reference: Hole filling, Surface deviation.
Next: 3. Placing the body.