Basic C API

Using the Solidean C API (from a C++ project): create a context, run a boolean, and export triangles.

This example demonstrates the minimal flow using the C API:
Create a context → set up arithmetic → define an operation → import meshes → run a boolean → export results.

Note:
This uses C++ as a language but the C Solidean API.

Result

A cube subtracted from a cube

CMake setup

To use the C API from CMake, add the Solidean C language target and link it to your project:

# Add the Solidean C API to your project
add_subdirectory(path/to/solidean/lang/c)

# Link against the Solidean C API
target_link_libraries(YourProject PRIVATE Solidean::C)

Code

#include <cstring>
#include <iostream>
#include <vector>

#include <solidean.h>

int main()
{
    // Note that this example uses the C-API.
    // There is a less verbose C++-API available.

    Solidean_ContextHandle ctx;
    Solidean_Result_t res = Solidean_Context_Create(&ctx);

    if (res != Solidean_Result_Ok)
    {
        std::cerr << "could not initialize solidean, reason: " << res << std::endl;
        return EXIT_FAILURE;
    }

    // The arithmetic defines the usable AABB and the bit depth (and performance) of the underlying math kernel
    Solidean_ExactArithmeticHandle arithmetic;
    Solidean_ExactArithmetic_Create(ctx, &arithmetic, 10.f, Solidean_ArithmeticKernel_Fixed256Pos26);

    // Vertices consist of 3 consecutive floats
    std::vector<Solidean_Pos3_F32> vertsA;
    std::vector<Solidean_Pos3_F32> vertsB;
    // Triangles consist of 3 consecutive ints
    std::vector<Solidean_IndexedTriangle> trisA;
    std::vector<Solidean_IndexedTriangle> trisB;

    // Define the indices for the cube
    trisA = trisB = {
        {0, 1, 2}, // front face
        {0, 2, 3}, //
        {4, 6, 5}, // back face
        {4, 7, 6}, //
        {1, 5, 6}, // right face
        {1, 6, 2}, //
        {0, 7, 4}, // left face
        {0, 3, 7}, //
        {3, 2, 6}, // top face
        {3, 6, 7}, //
        {0, 5, 1}, // bottom face
        {0, 4, 5},
    };

    // Define the vertices for the cube
    vertsA = vertsB = {
        {-0.5f, 0.5f, -0.5f},  // vertex 0
        {0.5f, 0.5f, -0.5f},   // vertex 1
        {0.5f, -0.5f, -0.5f},  // vertex 2
        {-0.5f, -0.5f, -0.5f}, // vertex 3
        {-0.5f, 0.5f, 0.5f},   // vertex 4
        {0.5f, 0.5f, 0.5f},    // vertex 5
        {0.5f, -0.5f, 0.5f},   // vertex 6
        {-0.5f, -0.5f, 0.5f}   // vertex 7
    };

    for (auto& v : vertsB)
    {
        v.x += 0.25f;
        v.y += 0.25f;
        v.z += 0.25f;
    }

    // Create an operation handle for a subtraction operation that consists of three operands:
    // Result mesh and input meshes A and B
    Solidean_OperationHandle op;
    // Every Solidean call returns a result enum value that can be used for error handling
    res = Solidean_Operation_Create(ctx, &op, arithmetic);

    if (res != Solidean_Result_Ok)
    {
        // ... error handling
    }

    // Directly creates mesh operands for both data sets
    // NOTE: this uses a default SurfaceBuilder and MeshBuilder, i.e. no self-intersections.
    // For more advanced usage, create an explicit MeshHandle (possibly from multiple surfaces)
    Solidean_MeshOperand opMeshA;
    Solidean_Operation_ImportFromIndexedTrianglesF32(op, &opMeshA, (Solidean_Pos3_F32*)vertsA.data(), vertsA.size(),
                                                     (Solidean_IndexedTriangle const*)trisA.data(), trisA.size(), Solidean_MeshType_Solid,
                                                     Solidean_Lifetime_UntilDirectRelease);
    Solidean_MeshOperand opMeshB;
    Solidean_Operation_ImportFromIndexedTrianglesF32(op, &opMeshB, (Solidean_Pos3_F32*)vertsB.data(), vertsB.size(),
                                                     (Solidean_IndexedTriangle const*)trisB.data(), trisB.size(), Solidean_MeshType_Solid,
                                                     Solidean_Lifetime_UntilDirectRelease);

    // Mesh operand for the mesh resulting from the difference operation
    // result = A - B
    Solidean_MeshOperand opResult;
    Solidean_Operation_Difference(op, &opResult, opMeshA, opMeshB);

    // Create a mesh handle for the output
    Solidean_MeshHandle resultMesh;
    Solidean_Operation_Output(op, &resultMesh, opResult);

    // Create a handle for the result data
    Solidean_TypedBlobHandle typedBlob;
    Solidean_TypedBlob_Create(ctx, &typedBlob);

    // Create and store result data as unrolled single-precision float triangles (9 floats per triangle)
    Solidean_Operation_ExportToTrianglesF32(op, &typedBlob, opResult);

    // Perform the actual operations (before, nothing is computed and result data must not be queried)
    // This is especially useful to create more complex computation constructs and defer the actual work
    Solidean_ExecuteResult_t ex_result;
    Solidean_Context_Execute(ctx, &ex_result, op, Solidean_ExecuteMode_Singlethreaded);

    // Determine data size
    Solidean_Byte const* blobData;
    uint64_t blobDataSize;
    Solidean_TypedBlob_GetData(typedBlob, &blobData, &blobDataSize, Solidean_DataSlot_TrianglesF32);

    size_t nTriangles = blobDataSize / sizeof(Solidean_Triangle_F32);

    std::cout << "Number of result triangles is " << nTriangles << std::endl;

    // Clean up
    Solidean_TypedBlob_Release(typedBlob);
    Solidean_Mesh_Release(resultMesh);
    Solidean_Operation_Release(op);
    Solidean_ExactArithmetic_Release(arithmetic);
    Solidean_Context_Destroy(ctx);

    return EXIT_SUCCESS;
}

Notes

  • The C API mirrors the C++ one, but uses plain handles and enums.
  • This API is the low level interface to Solidean, so it may appear more verbose than necessary.
    Typical application code uses one of the high-level APIs.
  • All handles must be released with the corresponding *_Release function.
  • Input triangles are arrays of 9 floats per triangle ([x0,y0,z0, x1,y1,z1, x2,y2,z2]).
  • See the API Reference for details on enums such as MeshType, Lifetime, and ExecuteMode.