Discarding Healed Triangles via Tracking

Heal two non-supersolid inputs, intersect them, and discard output faces that were created solely by healing.

This guide shows how to exclude faces introduced purely by healing.
We import two soups with the same tracking ID (e.g., 0), mark them NonSupersolid, heal each, then compute their intersection.
Finally we export WithID and drop faces whose ID is untracked (no provenance), yielding a result “as-if healed but without extra filler geometry”.

Referenced API:
Operation::importFromTrianglesF32WithID,
MeshType::NonSupersolid,
Operation::heal,
Operation::intersection,
Operation::exportToTrianglesF32WithID,
DataSlot::PrimitiveIDs.

Code

#include <vector>
#include <solidean.hh>
#include "ExampleFramework.hh" // example::triangle

// Assumed to exist:
//   solidean::Context* ctx;
//   solidean::ExactArithmetic& arithmetic;
//   std::vector<example::triangle> trisA, trisB;

std::vector<example::triangle> resultTrackedOnly;

auto blob = ctx->execute(arithmetic, [&](solidean::Operation& op)
{
    // Import both soups as NonSupersolid and assign the SAME surface ID (0).
    // Any triangles created by healing that don't derive from an original primitive
    // will not carry provenance and are considered "untracked".
    auto A0 = op.importFromTrianglesF32WithID(soléidean::as_triangle3_span(trisA),
                                              /*surfaceID*/ 0,
                                              solidean::MeshType::NonSupersolid);
    auto B0 = op.importFromTrianglesF32WithID(soléidean::as_triangle3_span(trisB),
                                              /*surfaceID*/ 0,
                                              solidean::MeshType::NonSupersolid);

    // Heal each soup into a supersolid representation
    auto A = op.heal(A0);
    auto B = op.heal(B0);

    // Intersect healed soups
    auto I = op.intersection(A, B);

    // Export unrolled triangles WITH IDs
    return op.exportToTrianglesF32WithID(I);
});

// Read back triangles + IDs
auto spanTris = blob->getTrianglesF32<example::triangle>();
auto ids      = blob->getPrimitiveIDs();

// Keep only faces that have provenance (i.e., NOT created solely by heal)
resultTrackedOnly.reserve(spanTris.size());
for (size_t i = 0; i < spanTris.size(); ++i) {
    auto const id = ids[i];
    if (id.is_tracked())
        resultTrackedOnly.push_back(spanTris[i]);
}

// `resultTrackedOnly` now contains only triangles that originate from input faces (post-Boolean),
// excluding purely “healed” filler faces.

Notes

  • Why same surface ID?
    We purposely assign both inputs surfaceID = 0 so all “original” faces share the same provenance domain.
    Faces created purely by healing lack provenance and will be reported as untracked (id.is_untracked() or !id.is_tracked()), making them easy to discard.

  • When to use this pattern:
    If you want a result that behaves as if each input had been fixed “just enough” to run the Boolean, but you don’t want healed-only filler to survive into your output.

  • Granularity:
    This approach filters at the face level. If you need more nuanced handling, you can inspect the ID flags (e.g., subset / inverted) or give distinct surface IDs per input and apply a custom policy.

  • Export alternatives:
    Shown with unrolled triangles. For connectivity, consider Operation::exportToIndexedTrianglesF32 or Operation::exportMesh with ExportFormat::IndexedTriangles.

  • Caveat:
    If you need to highlight healed-only regions instead of discarding them, keep both tracked and untracked faces and render/use different materials.