The goal of this tutorial is to preserve and inspect provenance through a Boolean:
We will import three meshes with tracking IDs (A=0, B=1, C=2), compute \((A \cup B) \setminus C\) in a single operation, export triangles with IDs, then split the result by source ID and compute area per source.
You’ll use: Context, ExactArithmetic, Operation::importFromTrianglesF32WithID, Operation::union, Operation::difference, Operation::exportToTrianglesF32WithID, TypedBlob, and DataSlot::PrimitiveIDs.
ExampleFramework.hh for simple POD types and helpers.
1) Project setup (CMake)
Add the C++17 binding and link it:
add_subdirectory(path/to/solidean/lang/cpp17)
target_link_libraries(YourProject PRIVATE Solidean::Cpp17)
Ensure the Solidean dynamic library is discoverable at runtime.
2) Program: (A ∪ B) \ C with tracking IDs
We create two overlapping cubes A and B, and an overlapping icosphere C.
We import each with a distinct surface ID (0, 1, 2), run a single operation, export with IDs, and post-process by ID.
#include <iostream>
#include <vector>
#include <tuple>
#include <solidean.hh>
#include "ExampleFramework.hh" // pos3, triangle, createCube, createIcoSphere, computeAreaAndVolume
int main() {
// 1) Context + exact arithmetic
auto ctx = solidean::Context::create();
auto arithmetic = ctx->createExactArithmetic(10.0f);
// 2) Define three input meshes (triangle soups)
// A and B: overlapping cubes; C: icosphere overlapping both
auto trisA = example::createCube({-0.25f, 0.0f, 0.0f}, 0.5f);
auto trisB = example::createCube({+0.25f, 0.0f, 0.0f}, 0.5f);
auto trisC = example::createIcoSphere(/*subdiv*/3, /*center*/{0.0f, 0.0f, 0.0f}, /*radius*/0.35f);
// 3) Record a single operation: (A ∪ B) \ C
auto blob = ctx->execute(*arithmetic, [&](solidean::Operation& op) {
// Import with tracking IDs (A=0, B=1, C=2)
auto A = op.importFromTrianglesF32WithID(solidean::as_triangle3_span(trisA), /*surfaceID*/0);
auto B = op.importFromTrianglesF32WithID(solidean::as_triangle3_span(trisB), /*surfaceID*/1);
auto C = op.importFromTrianglesF32WithID(solidean::as_triangle3_span(trisC), /*surfaceID*/2);
auto U = op.union_(A, B);
auto R = op.difference(U, C);
// Export with IDs so each output triangle carries provenance
// This works through arbitrarily many Booleans
return op.exportToTrianglesF32WithID(R);
});
// 4) Split output triangles by source surface ID
auto triSpan = blob->getTrianglesF32<example::triangle>();
auto ids = blob->getPrimitiveIDs();
std::vector<example::triangle> fromA, fromB, fromC;
for (size_t i = 0; i < triSpan.size(); ++i) {
const auto id = ids[i];
// ID layout: [flags | 30-bit surface_id | 32-bit prim_id]
// The C++ API provides a convenience wrapper to access these
const uint32_t surf = id.surface_id();
switch (surf) {
case 0: fromA.push_back(triSpan[i]); break;
case 1: fromB.push_back(triSpan[i]); break;
case 2: fromC.push_back(triSpan[i]); break;
// everything is tracked, so the default case is never hit
}
}
// 5) Compute area per source
auto [areaA, volA ] = example::computeAreaAndVolume(fromA);
auto [areaB, volB ] = example::computeAreaAndVolume(fromB);
auto [areaC, volC ] = example::computeAreaAndVolume(fromC);
auto [areaAll, volAll] = example::computeAreaAndVolume(std::vector<example::triangle>(triSpan.begin(), triSpan.end()));
std::cout << "Output triangles total: " << triSpan.size() << "\n";
std::cout << "Area by provenance:\n";
std::cout << " from A (ID=0): " << areaA << "\n";
std::cout << " from B (ID=1): " << areaB << "\n";
std::cout << " from C (ID=2): " << areaC << "\n";
std::cout << " total: " << areaAll << "\n";
return EXIT_SUCCESS;
}
3) What you learned
-
Attach provenance at import:
UsingimportFromTrianglesF32WithIDsets a surface ID that is preserved through Booleans. -
Boolean with provenance:
A single operation computes ((A \cup B) \setminus C) viaOperation::unionandOperation::difference. -
Export IDs for analysis:
exportToTrianglesF32WithIDpopulatesDataSlot::PrimitiveIDs, enabling post-hoc grouping by source. -
Split by origin and measure:
After export, you can split triangles by surface ID and compute per-source area (or any metric you need).
4) Variations to try
- Export indexed triangles with IDs using the corresponding indexed export variant to preserve connectivity.
- Use separate surfaces in one mesh and set
SurfaceBuilder::TrackIDto customize ID ranges. - Visualize provenance by coloring triangles by ID in your renderer.
Next steps
- Browse the API Reference (search functions, classes, and types).
- Scan All Symbols (A–Z) for quick symbol lookup.
- See FAQ for developer-focused questions.
- Check Troubleshooting for common issues and fixes.
- Track changes in the Changelog.