graphicscomputational-geometrycgalcsglibigl

How to use CSGTree for multiple boolean operations


I'm trying to make use of Csg Tree by libigl to do multiple boolean operations at once. In the linked tutorial, there is just this one line:

// Compute result of (A ∩ B) \ ((C ∪ D) ∪ E)
igl::copyleft::cgal::CSGTree<MatrixXi> CSGTree =
  {{{VA,FA},{VB,FB},"i"},{{{VC,FC},{VD,FD},"u"},{VE,FE},"u"},"m"};

I couldn't figure out how to use the CSGTree class API. Can anybody help ideally by a boilerplate example?


Solution

  • According to the libigl tutorial:

    Libigl uses exact arithmetic internally to construct the intermediary boolean results robustly. “Rounding” this result to floating point (even double precision) would cause problems if re-injected into a further boolean operation. To facilitate CSG tree operations and encourage callers not to call igl::copyleft::cgal::mesh_boolean multiple times explicitly, libigl implements a class igl::copyleft::cgal::CSGTree.

    In simple words - you can construct CSG meshes using the mesh_boolean function, but in this case you have to explicitly take care of robustness of intermediate calculations. The CSGTree class does that for you automatically because it uses the CGAL exact arithmetic for all the intermediate calculations with coordinates. Another benefit of the CSGTree class - you can construct a multilevel CSG tree in just one line of code. An example below shows how to construct a simplest CSG tree from two meshes and visualize it:

    #include <Eigen/Dense>
    
    #include <igl/copyleft/cgal/CSGTree.h>
    #include <igl/opengl/glfw/Viewer.h>
    #include <igl/read_triangle_mesh.h>
    
    int main()
    {
      // ------ load mesh #1
      Eigen::MatrixXd V1;
      Eigen::MatrixXi F1;
      igl::read_triangle_mesh("data/sphere.obj", V1, F1);
      // ------ load mesh #2
      Eigen::MatrixXd V2;
      Eigen::MatrixXi F2;
      igl::read_triangle_mesh("data/xcylinder.obj", V2, F2);
      // ------ combine meshes #1 and #2
      const igl::copyleft::cgal::CSGTree t{{V1, F1}, {V2, F2}, "union"};
      // ------ plot the combined mesh
      const auto V = t.cast_V<Eigen::MatrixXd>();
      const auto F = t.F();
      igl::opengl::glfw::Viewer viewer;
      viewer.data().set_mesh(V, F);
      viewer.launch();
    }
    

    Such a one-line construction is possible because the CSGTree has the following constructor (among others):

    CSGTree(const CSGTree & A, const CSGTree & B, const MeshBooleanType & type)
    

    As you can see above - the resulting array of coordinates can be converted to an array of doubles by the cast_V templated function if it's necessary - for example, for visualization. The result of this visualization is below:

    SphereUnionXCylinder