1-Click Mesh Repair: Solid, Manifold, Watertight

Turn arbitrary triangle soups into a clean, solid, manifold mesh with indexed topology (and optional half-edges).

This guide is a Swiss-army repair pass: take any triangle soup and produce a solid, watertight, manifold result with clean indexed topology.
We assume you already have ctx (solidean::Context*), arithmetic (ExactArithmetic&), and std::vector<example::triangle> triangles.

Referenced API:
MeshType::NonSupersolid,
Operation::heal,
Operation::selfUnion,
Operation::exportMesh,
ExportFormat::IndexedTriangles,
ExportOption::Manifold,
ExportOption::RemoveSpuriousVertices.

Repair → Solid → Manifold (indexed)

#include <vector>
#include <solidean.hh>
#include "ExampleFramework.hh" // triangle, pos3, tri_idx

// Inputs assumed to exist:
//   solidean::Context* ctx;
//   std::unique_ptr<solidean::ExactArithmetic> arithmetic;
//   std::vector<example::triangle> triangles;

auto blob = ctx->execute(*arithmetic, [&](solidean::Operation& op)
{
    // 1) Import as *NonSupersolid* to declare: “this soup may have holes / open borders / non-manifold bits”
    auto in = op.importFromTrianglesF32(
        solidean::as_triangle3_span(triangles),
        solidean::MeshType::NonSupersolid);

    // 2) Heal → make it supersolid (fills holes / resolves inconsistencies best-effort)
    auto healed = op.heal(in);

    // 3) Self-union → remove internal self-overlaps and nested structure → strictly *solid*
    auto solid = op.selfUnion(healed);

    // 4) Export: indexed triangles + guarantees
    //    - Manifold: topologically manifold decomposition (duplicates a few indices where necessary)
    //    - RemoveSpuriousVertices: cleans edge-midpoint artifacts on coplanar, same-ID regions
    auto options = solidean::ExportOption::VertexPositionF32
                 | solidean::ExportOption::Manifold
                 | solidean::ExportOption::RemoveSpuriousVertices;

    return op.exportMesh(solid, solidean::ExportFormat::IndexedTriangles, options);
});

// Access indexed topology
auto pos  = blob->getPositionsF32<example::pos3>();
auto tris = blob->getTrianglesIndexed<example::tri_idx>();

auto positions = std::vector<example::pos3>(pos.begin(),  pos.end());
auto indices = std::vector<example::tri_idx>(tris.begin(), tris.end());

// `positions` + `indices` now form a clean, watertight, manifold surface.

Optional: export with explicit half-edges

If you need rich adjacency (e.g., robust editing / analysis), export a half-edge structure instead:

auto options = solidean::ExportOption::VertexPositionF32
             | solidean::ExportOption::Manifold
             | solidean::ExportOption::RemoveSpuriousVertices
             | solidean::ExportOption::HalfedgeToVertex
             | solidean::ExportOption::HalfedgeToNextHalfedge
             | solidean::ExportOption::HalfedgeToOppositeHalfedge;

auto heBlob = ctx->execute(*arithmetic, [&](solidean::Operation& op)
{
    auto in     = op.importFromTrianglesF32(solidean::as_triangle3_span(triangles),
                                            solidean::MeshType::NonSupersolid);
    auto solid  = op.selfUnion(op.heal(in));
    return op.exportMesh(solid, solidean::ExportFormat::HalfedgeExplicit, options);
});

// Then read PositionsF32, HalfedgeToVertex, HalfedgeToNextHalfedge, HalfedgeToOppositeHalfedge, etc.

Notes

  • When to use this recipe: whenever input is unknown quality or known to be “bad” (holes, open borders, self-intersections, non-manifold).
  • Why heal then selfUnion:
    • Operation::heal converts arbitrary soup into a supersolid mesh (closed in the “winding-number” sense).
    • Operation::selfUnion collapses internal overlaps/duplicates to produce a strictly solid boundary.
  • Manifold guarantee: ExportOption::Manifold ensures a manifold decomposition of the output without inventing geometry (it may duplicate vertex indices to separate sheets).
    Use ExportOption::PreferLargerManifolds if you want the largest possible manifolds when ambiguous.
  • Spurious vertices: RemoveSpuriousVertices removes mid-edge artifacts that can appear on coplanar, same-provenance regions.
  • Positions: example uses VertexPositionF32. For exact or plane-based positions, see ExportOption::VertexPositionExact and VertexPositionPlanes.
  • Performance: this is a do-it-all pass; for trusted data, you can skip heal or selfUnion as appropriate.