MeshLib Documentation
Loading...
Searching...
No Matches
Mesh Boolean

Example of boolean operation

The optional BooleanResultMapper passed to the operation maps faces, vertices and edges of the input meshes to the primitives of the result mesh, and its getNew2OldFaceMap maps the faces back.

▶ See it live in the browser

  • C++
    #include <MRMesh/MRMesh.h>
    #include <MRMesh/MRMeshBoolean.h>
    #include <MRMesh/MRMeshSave.h>
    #include <MRMesh/MRUVSphere.h>
    #include <iostream>
    int main()
    {
    // create first sphere with radius of 1 unit
    MR::Mesh sphere1 = MR::makeUVSphere( 1.0f, 64, 64 );
    // create second sphere by cloning the first sphere and moving it in X direction
    MR::Mesh sphere2 = sphere1;
    MR::AffineXf3f xf = MR::AffineXf3f::translation( MR::Vector3f( 0.7f, 0.0f, 0.0f ) );
    sphere2.transform( xf );
    // optional mapper relating the primitives of the input meshes to the primitives of the result
    MR::BooleanResultMapper mapper;
    // perform boolean operation
    MR::BooleanResult result = MR::boolean( sphere1, sphere2, MR::BooleanOperation::Intersection, { .mapper = &mapper } );
    if ( !result.valid() )
    std::cerr << result.errorString << std::endl;
    MR::Mesh resultMesh = *result;
    // find the faces of the result produced by each input sphere, and the faces the cut created
    using MapObject = MR::BooleanResultMapper::MapObject;
    MR::FaceBitSet facesOfSphere1 = mapper.map( sphere1.topology.getValidFaces(), MapObject::A );
    MR::FaceBitSet facesOfSphere2 = mapper.map( sphere2.topology.getValidFaces(), MapObject::B );
    MR::FaceBitSet newFaces = mapper.newFaces();
    std::cout << "faces from sphere1: " << facesOfSphere1.count() << "\n"
    << "faces from sphere2: " << facesOfSphere2.count() << "\n"
    << "faces created by the cut: " << newFaces.count() << std::endl;
    // map one particular face of sphere1 forward: the cut can split it in several faces of the
    // result, or drop it completely if that part of sphere1 is not in the result
    MR::FaceId faceOfSphere1( 793 );
    MR::FaceBitSet oneFace;
    oneFace.autoResizeSet( faceOfSphere1 );
    MR::FaceBitSet producedFaces = mapper.map( oneFace, MapObject::A );
    std::cout << "face " << faceOfSphere1 << " of sphere1 produced " << producedFaces.count() << " faces of the result:";
    for ( MR::FaceId f : producedFaces )
    std::cout << ' ' << f;
    std::cout << std::endl;
    // and backward: the face of sphere1 each face of the result came from
    // (invalid id for the faces that came from sphere2)
    MR::FaceMap new2OldFaces = mapper.getNew2OldFaceMap( MapObject::A );
    MR::FaceId resultFace = producedFaces.find_first();
    std::cout << "face " << resultFace << " of the result came from face " << new2OldFaces[resultFace] << " of sphere1" << std::endl;
    // save result to STL file
    if ( auto saveRes = MR::MeshSave::toAnySupportedFormat( resultMesh, "out_boolean.stl" ); !saveRes )
    {
    std::cerr << saveRes.error() << std::endl;
    return 1;
    }
    return 0;
    }
    int main()
    Definition LaplacianDeformation.cpp:4
    Source meshes
    Boolean intersection
  • Python
    import meshlib.mrmeshpy as mrmeshpy
    # create first sphere with radius of 1 unit
    sphere1 = mrmeshpy.makeUVSphere(1.0, 64, 64)
    # create second sphere by cloning the first sphere and moving it in X direction
    sphere2 = mrmeshpy.copyMesh(sphere1)
    xf = mrmeshpy.AffineXf3f.translation(mrmeshpy.Vector3f(0.7, 0.0, 0.0))
    sphere2.transform(xf)
    # optional mapper relating the primitives of the input meshes to the primitives of the result
    mapper = mrmeshpy.BooleanResultMapper()
    # perform boolean operation
    result = mrmeshpy.boolean(sphere1, sphere2, mrmeshpy.BooleanOperation.Intersection, None, mapper)
    if not result.valid():
    print(result.errorString)
    else:
    # find the faces of the result produced by each input sphere, and the faces the cut created
    map_object = mrmeshpy.BooleanResultMapper.MapObject
    faces_of_sphere1 = mapper.map(sphere1.topology.getValidFaces(), map_object.A)
    faces_of_sphere2 = mapper.map(sphere2.topology.getValidFaces(), map_object.B)
    new_faces = mapper.newFaces()
    print(f"faces from sphere1: {faces_of_sphere1.count()}")
    print(f"faces from sphere2: {faces_of_sphere2.count()}")
    print(f"faces created by the cut: {new_faces.count()}")
    # map one particular face of sphere1 forward: the cut can split it in several faces of the
    # result, or drop it completely if that part of sphere1 is not in the result
    face_of_sphere1 = mrmeshpy.FaceId(793)
    one_face = mrmeshpy.FaceBitSet()
    one_face.autoResizeSet(face_of_sphere1)
    produced_faces = mapper.map(one_face, map_object.A)
    print(f"face {face_of_sphere1.get()} of sphere1 produced {produced_faces.count()} faces of the result")
    # and backward: the face of sphere1 each face of the result came from
    # (invalid id for the faces that came from sphere2)
    new2old_faces = mapper.getNew2OldFaceMap(map_object.A)
    result_face = produced_faces.find_first()
    print(f"face {result_face.get()} of the result came from face {new2old_faces[result_face].get()} of sphere1")
    # save result to STL file
    mrmeshpy.saveMesh(result.mesh, "out_boolean.stl")
    Source meshes
    Boolean intersection
  • C
    #include <MRCMesh/MRAffineXf.h>
    #include <MRCMesh/MRBitSet.h>
    #include <MRCMesh/MRBooleanOperation.h>
    #include <MRCMesh/MRId.h>
    #include <MRCMesh/MRMakeSphereMesh.h>
    #include <MRCMesh/MRMesh.h>
    #include <MRCMesh/MRMeshBoolean.h>
    #include <MRCMesh/MRMeshSave.h>
    #include <MRCMesh/MRMeshTopology.h>
    #include <MRCMesh/MRVector.h>
    #include <MRCMesh/MRVector3.h>
    #include <MRCMisc/expected_void_std_string.h>
    #include <MRCMisc/std_string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int main( void )
    {
    int rc = EXIT_FAILURE;
    // First, create a unit sphere.
    float radius = 1.f; // Set radius for the test
    int horizontalResolution = 64; // Increase horizontal resolution
    int verticalResolution = 64; // Increase vertical resolution
    MR_Mesh* sphere1 = MR_makeUVSphere( &radius, &horizontalResolution, &verticalResolution );
    // Create a copy of this sphere and offset it.
    MR_Mesh* sphere2 = MR_Mesh_ConstructFromAnother( MR_PassBy_Copy, sphere1 );
    MR_Vector3f xfTranslation = {.x = 0.7f};
    MR_AffineXf3f xf = MR_AffineXf3f_translation( &xfTranslation );
    MR_Mesh_transform( sphere2, &xf, NULL );
    // Ask for an optional mapper relating the primitives of the input meshes to the primitives of the result.
    MR_BooleanResultMapper* mapper = MR_BooleanResultMapper_DefaultConstruct();
    MR_BooleanParameters* params = MR_BooleanParameters_DefaultConstruct();
    MR_BooleanParameters_Set_mapper( params, mapper );
    // Perform the boolean operation.
    MR_BooleanResult* result = MR_boolean_4_const_MR_Mesh_ref( sphere1, sphere2, MR_BooleanOperation_Intersection, params );
    MR_BooleanParameters_Destroy( params );
    if ( !MR_BooleanResult_valid( result ) )
    {
    fprintf( stderr, "Failed to perform boolean: %s\n", MR_std_string_data( MR_BooleanResult_Get_errorString( result ) ) );
    goto fail;
    }
    // Find the faces of the result produced by each input sphere, and the faces the cut created.
    MR_FaceBitSet* facesOfSphere1 = MR_BooleanResultMapper_map_MR_FaceBitSet(
    mapper, MR_MeshTopology_getValidFaces( MR_Mesh_Get_topology( sphere1 ) ), MR_BooleanResultMapper_MapObject_A );
    MR_FaceBitSet* facesOfSphere2 = MR_BooleanResultMapper_map_MR_FaceBitSet(
    mapper, MR_MeshTopology_getValidFaces( MR_Mesh_Get_topology( sphere2 ) ), MR_BooleanResultMapper_MapObject_B );
    MR_FaceBitSet* newFaces = MR_BooleanResultMapper_newFaces( mapper );
    printf( "faces from sphere1: %zu\n", MR_FaceBitSet_count( facesOfSphere1 ) );
    printf( "faces from sphere2: %zu\n", MR_FaceBitSet_count( facesOfSphere2 ) );
    printf( "faces created by the cut: %zu\n", MR_FaceBitSet_count( newFaces ) );
    MR_FaceBitSet_Destroy( newFaces );
    MR_FaceBitSet_Destroy( facesOfSphere2 );
    MR_FaceBitSet_Destroy( facesOfSphere1 );
    // Map one particular face of sphere1 forward: the cut can split it in several faces of the
    // result, or drop it completely if that part of sphere1 is not in the result.
    MR_FaceId faceOfSphere1 = { 793 };
    MR_FaceBitSet* oneFace = MR_FaceBitSet_DefaultConstruct();
    MR_FaceBitSet_autoResizeSet_2( oneFace, faceOfSphere1, NULL );
    MR_FaceBitSet* producedFaces = MR_BooleanResultMapper_map_MR_FaceBitSet( mapper, oneFace, MR_BooleanResultMapper_MapObject_A );
    printf( "face %d of sphere1 produced %zu faces of the result\n", faceOfSphere1.id_, MR_FaceBitSet_count( producedFaces ) );
    // And backward: the face of sphere1 each face of the result came from
    // (invalid id for the faces that came from sphere2).
    MR_FaceMap* new2OldFaces = MR_BooleanResultMapper_getNew2OldFaceMap( mapper, MR_BooleanResultMapper_MapObject_A );
    MR_FaceId resultFace = MR_FaceBitSet_find_first( producedFaces );
    printf( "face %d of the result came from face %d of sphere1\n",
    resultFace.id_, MR_FaceMap_index( new2OldFaces, resultFace )->id_ );
    MR_FaceMap_Destroy( new2OldFaces );
    MR_FaceBitSet_Destroy( producedFaces );
    MR_FaceBitSet_Destroy( oneFace );
    // Save result to an STL file.
    MR_expected_void_std_string* saveEx = MR_MeshSave_toAnySupportedFormat_3( MR_BooleanResult_Get_mesh( result ), "out_boolean.stl", NULL, NULL);
    if ( MR_expected_void_std_string_error( saveEx ) )
    {
    fprintf( stderr, "Failed to save mesh: %s\n", MR_std_string_data( MR_expected_void_std_string_error( saveEx ) ) );
    goto fail;
    }
    rc = EXIT_SUCCESS;
    fail:
    MR_BooleanResult_Destroy( result );
    MR_BooleanResultMapper_Destroy( mapper );
    MR_Mesh_Destroy( sphere2 );
    MR_Mesh_Destroy( sphere1 );
    return rc;
    }
    Source meshes
    Boolean intersection
  • C#
    public class MeshBooleanExample
    {
    public static void Main(string[] args)
    {
    try
    {
    MR.Mesh mesh_a, mesh_b;
    if (args.Length >= 2)
    {
    // load the two meshes given on the command line
    mesh_a = MR.MeshLoad.fromAnySupportedFormat(args[0]);
    mesh_b = MR.MeshLoad.fromAnySupportedFormat(args[1]);
    }
    else
    {
    // no input given: make two unit spheres shifted along X, so that they overlap
    mesh_a = MR.makeUVSphere(1.0f, 64, 64);
    mesh_b = MR.makeUVSphere(1.0f, 64, 64);
    mesh_b.transform(MR.AffineXf3f.translation(new MR.Vector3f(0.7f, 0.0f, 0.0f)));
    }
    // optional mapper relating the primitives of the input meshes to the primitives of the result
    var mapper = new MR.BooleanResultMapper();
    var parameters = new MR.BooleanParameters();
    parameters.mapper = mapper;
    // perform boolean operation
    MR.BooleanResult res = MR.boolean(mesh_a, mesh_b, MR.BooleanOperation.Intersection, parameters);
    if (!res.valid())
    {
    Console.WriteLine("Error: {0}", res.errorString);
    return;
    }
    // find the faces of the result produced by each input mesh, and the faces the cut created
    var facesOfA = mapper.map(mesh_a.topology.getValidFaces(), MR.BooleanResultMapper.MapObject.A);
    var facesOfB = mapper.map(mesh_b.topology.getValidFaces(), MR.BooleanResultMapper.MapObject.B);
    var newFaces = mapper.newFaces();
    Console.WriteLine("faces from mesh A: {0}", facesOfA.count());
    Console.WriteLine("faces from mesh B: {0}", facesOfB.count());
    Console.WriteLine("faces created by the cut: {0}", newFaces.count());
    // map one particular face of mesh A forward: the cut can split it in several faces of
    // the result, or drop it completely if that part of mesh A is not in the result
    var faceOfA = new MR.FaceId(793);
    var oneFace = new MR.FaceBitSet(794);
    oneFace.set(faceOfA);
    var producedFaces = mapper.map(oneFace, MR.BooleanResultMapper.MapObject.A);
    Console.WriteLine("face {0} of mesh A produced {1} faces of the result", faceOfA.id, producedFaces.count());
    // and backward: the face of mesh A each face of the result came from
    // (invalid id for the faces that came from mesh B)
    var new2OldFaces = mapper.getNew2OldFaceMap(MR.BooleanResultMapper.MapObject.A);
    for (int f = 0; f < (int)new2OldFaces.size(); ++f)
    {
    var resultFace = new MR.FaceId(f);
    if (!producedFaces.test(resultFace))
    continue;
    Console.WriteLine("face {0} of the result came from face {1} of mesh A",
    f, new2OldFaces[resultFace].id);
    break;
    }
    // save result to STL file
    MR.MeshSave.toAnySupportedFormat(res.mesh, "out_boolean.stl");
    }
    catch (Exception e)
    {
    Console.WriteLine("Error: {0}", e.Message);
    }
    }
    }
    Source meshes
    Boolean intersection
  • JavaScript
    import createMeshLib from '@meshinspector/meshlib';
    const ml = await createMeshLib();
    // create first sphere with radius of 1 unit
    using sphere1 = ml.makeUVSphere(1.0, 64, 64);
    // create second sphere (the JS bindings expose no cheap mesh copy, so regenerate an
    // identical sphere) and move it in the X direction
    using sphere2 = ml.makeUVSphere(1.0, 64, 64);
    using xf = ml.AffineXf3f.translation({ x: 0.7, y: 0.0, z: 0.0 });
    sphere2.transform(xf);
    // optional mapper relating the primitives of the input meshes to the primitives of the result
    using mapper = new ml.BooleanResultMapper();
    // perform boolean operation
    using result = ml.boolean(sphere1, sphere2, ml.BooleanOperation.Intersection, mapper);
    if (!result.valid())
    throw new Error(result.errorString);
    // find the faces of the result produced by each input sphere, and the faces the cut created
    using topology1 = sphere1.topology;
    using topology2 = sphere2.topology;
    using validFaces1 = topology1.getValidFaces();
    using validFaces2 = topology2.getValidFaces();
    using facesOfSphere1 = mapper.mapFaces(validFaces1, ml.BooleanMapObject.A);
    using facesOfSphere2 = mapper.mapFaces(validFaces2, ml.BooleanMapObject.B);
    using newFaces = mapper.newFaces();
    console.log(`faces from sphere1: ${facesOfSphere1.count()}`);
    console.log(`faces from sphere2: ${facesOfSphere2.count()}`);
    console.log(`faces created by the cut: ${newFaces.count()}`);
    // map one particular face of sphere1 forward: the cut can split it in several faces of the
    // result, or drop it completely if that part of sphere1 is not in the result
    const faceOfSphere1 = 793;
    using oneFace = ml.FaceBitSet.fromIndices([faceOfSphere1]);
    using producedFaces = mapper.mapFaces(oneFace, ml.BooleanMapObject.A);
    console.log(`face ${faceOfSphere1} of sphere1 produced ${producedFaces.count()} faces of the result`);
    // and backward: the face of sphere1 each face of the result came from
    // (4294967295, i.e. an invalid id, for the faces that came from sphere2)
    using new2OldFaces = mapper.getNew2OldFaceMap(ml.BooleanMapObject.A);
    const new2OldFacesArray = new2OldFaces.toArray();
    const resultFace = producedFaces.find_first();
    console.log(`face ${resultFace} of the result came from face ${new2OldFacesArray[resultFace]} of sphere1`);
    // save result to STL file
    using resultMesh = result.mesh;
    ml.MeshSave.toAnySupportedFormat(resultMesh, 'out_boolean.stl');
    Note
    The using declaration requires Node.js 24+ or a modern browser. On older runtimes, call .delete() on each object instead (see Memory Management in the JavaScript Setup Guide).
    Source meshes
    Boolean intersection