BRep Cleanup: Segmentation, Defeaturing, and Continuity
Overview
An imported STEP file or a merged assembly is often a single BRep that actually contains several disjoint solids, or a solid littered with tiny featurelets (fillets, holes, logos) that complicate meshing. Rasmah provides three cleanup operations on the exact BRep:
- Segmentation — split a multi-solid BRep into its connected solids (
num_solids,separate_solids,extract_solid,connected_components). - Defeaturing — drop small solids/components below a size threshold (
defeature), or fill small holes in faces (fill_face_holes). - Continuity — measure the geometric smoothness across an edge (
edge_continuity).
This tutorial walks the cleanup the way it happens in practice: an imported 80 × 60 × 8 mm mounting plate arrives with a stray fragment, is split into solids, defeatured, and its edges classified. Dimensions are millimetres.
using RasmahA multi-solid import
The union of the plate and a stray 6 mm fragment — a left-over construction cube — produces a BRep with two solids that do not touch:
m = brep(union(box(80.0, 60.0, 8.0), translate(box(6.0, 6.0, 6.0), 60.0, 0.0, 0.0)))
println("solids: ", num_solids(m), ", components: ", length(connected_components(m)),
", watertight: ", is_watertight(m))solids: 2, components: 2, watertight: true
vtk(m)Segmentation
separate_solids splits the BRep into one watertight BRep per connected solid; extract_solid pulls out a single one by index:
parts = separate_solids(m)
println("separated into ", length(parts), " solids")
for (i, p) in enumerate(parts)
println(" solid ", i, ": volume ", round(volume(surface_mesh(p; n = 32)); digits = 1),
" mm³, watertight ", is_watertight(p))
end
cube = extract_solid(m, 2)
println("stray fragment volume = ", round(volume(surface_mesh(cube; n = 32)); digits = 1),
" mm³ (6³ = 216)")separated into 2 solids
solid 1: volume 38400.0 m^3 mm³, watertight true
solid 2: volume 216.0 m^3 mm³, watertight true
stray fragment volume = 216.0 m^3 mm³ (6³ = 216)
The plate is the 38 400 mm³ solid and the fragment the 216 mm³ one — each now a valid closed body.
Defeaturing
defeature removes solids of volume < tol³ — the “virtual topology” cleanup before meshing. A 10 mm threshold drops the 216 mm³ stray fragment while keeping the plate:
clean = defeature(m; tol = 10.0)
println("after defeature: ", num_solids(clean), " solid, volume ",
round(volume(surface_mesh(clean; n = 32)); digits = 1), " mm³")after defeature: 1 solid, volume 38400.0 m^3 mm³
Filling small holes
The same idea applies inside a face. A plate with a Ø0.5 locator hole — scan noise or a feature the mesh should ignore — has three hole loops (top face, bottom face, and the hole wall); fill_face_holes fills holes whose parameter-space area is ≤ tol²:
drilled = brep(difference(box(80.0, 60.0, 8.0), cylinder(0.25, 9.0)))
println("drilled plate: ", length(drilled.faces), " faces, ",
sum(length(f.loops) - 1 for f in drilled.faces), " hole loops, watertight ",
is_watertight(drilled))
filled = fill_face_holes(drilled; tol = 2.0)
println("after fill_face_holes(tol = 2): ", sum(length(f.loops) - 1 for f in filled.faces),
" hole loops, watertight ", is_watertight(filled))drilled plate: 7 faces, 3 hole loops, watertight true
after fill_face_holes(tol = 2): 1 hole loops, watertight true
With tol = 2 the Ø0.5 hole (≈0.2 mm²) is filled while an intentional Ø6 mounting hole (≈28 mm²) would be kept — small-feature cleanup that respects the drawing.
Edge continuity
edge_continuity(b, ei) classifies an edge as G0 (position-continuous, i.e. sharp), G1 (tangent-continuous), or G2 (curvature-continuous). The drilled plate’s edges are sharp, a sphere’s seam is fully smooth, and a cylinder’s cap rim is sharp:
plate = brep(difference(box(80.0, 60.0, 8.0), cylinder(3.0, 9.0)))
println("plate edges (sharp): ", [edge_continuity(plate, ei) for ei in 1:2], " …")
sph_b = brep(sphere(1.0))
println("sphere seams (smooth): ", [edge_continuity(sph_b, ei) for ei in 1:2])
cyl_b = brep(cylinder(1.0, 2.0))
println("cylinder cap rim: ", edge_continuity(cyl_b, 2), " (G0: perpendicular)")plate edges (sharp): [0, 0] …
sphere seams (smooth): [3, 3]
cylinder cap rim: 0 (G0: perpendicular)
This is the classification a fillet/chamfer or a mesher uses to decide whether an edge is a sharp feature to preserve.
Next steps
- BRep booleans — the boolean and editing operations these cleanups follow.
- STEP — the import format that most often needs this cleanup.
- Meshing algorithms — meshing the cleaned solid.
