From fada7224f17a189d5ea749633da16b81cb35a9e2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 16 Jan 2020 13:20:17 +0100 Subject: [PATCH] MeshBooleans with CGAL only --- sandboxes/CMakeLists.txt | 2 +- sandboxes/meshboolean/CMakeLists.txt | 8 +- sandboxes/meshboolean/MeshBoolean.cpp | 131 +++++++++++------------ src/libslic3r/MeshBoolean.cpp | 143 ++++++++++++++++++++++---- src/libslic3r/MeshBoolean.hpp | 21 ++++ 5 files changed, 209 insertions(+), 96 deletions(-) diff --git a/sandboxes/CMakeLists.txt b/sandboxes/CMakeLists.txt index 181c70d48..a2bd13bb0 100644 --- a/sandboxes/CMakeLists.txt +++ b/sandboxes/CMakeLists.txt @@ -1,4 +1,4 @@ #add_subdirectory(slasupporttree) #add_subdirectory(openvdb) -#add_subdirectory(meshboolean) +add_subdirectory(meshboolean) add_subdirectory(opencsg) diff --git a/sandboxes/meshboolean/CMakeLists.txt b/sandboxes/meshboolean/CMakeLists.txt index 17e876573..55fb42fd1 100644 --- a/sandboxes/meshboolean/CMakeLists.txt +++ b/sandboxes/meshboolean/CMakeLists.txt @@ -1,12 +1,6 @@ -if (SLIC3R_STATIC) - set(CGAL_Boost_USE_STATIC_LIBS ON) -endif () - -find_package(CGAL REQUIRED) - add_executable(meshboolean MeshBoolean.cpp) -target_link_libraries(meshboolean libslic3r CGAL::CGAL) +target_link_libraries(meshboolean libslic3r) if (WIN32) prusaslicer_copy_dlls(meshboolean) diff --git a/sandboxes/meshboolean/MeshBoolean.cpp b/sandboxes/meshboolean/MeshBoolean.cpp index 1aaa4d2b8..3fd45ccff 100644 --- a/sandboxes/meshboolean/MeshBoolean.cpp +++ b/sandboxes/meshboolean/MeshBoolean.cpp @@ -1,85 +1,76 @@ -#include -#undef PI -#include -//#undef IGL_STATIC_LIBRARY -#include - -#include #include +#include -#include +#include +#include +#include +#include +#include + +#include -#include #include namespace Slic3r { -bool its_write_obj(const Eigen::MatrixXd &V, Eigen::MatrixXi &F, const char *file) -{ - - FILE *fp = boost::nowide::fopen(file, "w"); - if (fp == nullptr) { - BOOST_LOG_TRIVIAL(error) << "stl_write_obj: Couldn't open " << file << " for writing"; - return false; - } - - for (size_t i = 0; i < V.rows(); ++ i) - fprintf(fp, "v %lf %lf %lf\n", V(i, 0), V(i, 1), V(i, 2)); - for (size_t i = 0; i < F.rows(); ++ i) - fprintf(fp, "f %d %d %d\n", F(i, 0) + 1, F(i, 1) + 1, F(i, 2) + 1); - fclose(fp); - return true; -} - -void mesh_boolean_test(const std::string &fname) -{ - using namespace Eigen; - using namespace std; -// igl::readOFF(TUTORIAL_SHARED_PATH "/cheburashka.off",VA,FA); -// igl::readOFF(TUTORIAL_SHARED_PATH "/decimated-knight.off",VB,FB); - // Plot the mesh with pseudocolors -// igl::opengl::glfw::Viewer viewer; - - // Initialize -// update(viewer); - - //igl::copyleft::cgal::mesh_boolean(VA,FA,VB,FB,boolean_type,VC,FC,J); - - - std::cout << fname.c_str() << std::endl; - TriangleMesh mesh; - - mesh.ReadSTLFile(fname.c_str()); - mesh.repair(true); - its_write_obj(mesh.its, (fname + "-imported0.obj").c_str()); - - - Eigen::MatrixXd VA,VB,VC; - Eigen::VectorXi J,I; - Eigen::MatrixXi FA,FB,FC; - igl::MeshBooleanType boolean_type(igl::MESH_BOOLEAN_TYPE_UNION); - - - typedef Eigen::Map> MapMatrixXfUnaligned; - typedef Eigen::Map> MapMatrixXiUnaligned; - - Eigen::MatrixXd V = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), mesh.its.vertices.size(), 3).cast(); - Eigen::MatrixXi F = MapMatrixXiUnaligned(mesh.its.indices.front().data(), mesh.its.indices.size(), 3); - - its_write_obj(V, F, (fname + "-imported.obj").c_str()); - // Self-union to clean up - igl::copyleft::cgal::mesh_boolean(V, F, Eigen::MatrixXd(), Eigen::MatrixXi(), boolean_type, VC, FC); - - its_write_obj(VC, FC, (fname + "-fixed.obj").c_str()); -} - } // namespace Slic3r int main(const int argc, const char * argv[]) { - if (argc < 1) return -1; + using namespace Slic3r; - Slic3r::mesh_boolean_test(argv[1]); + if (argc < 1) return EXIT_FAILURE; + + DynamicPrintConfig cfg; + auto model = Model::read_from_file(argv[1], &cfg); + + if (model.objects.empty()) return EXIT_SUCCESS; + + SLAPrint print; + print.apply(model, cfg); + PrintBase::TaskParams task; + task.to_object_step = slaposHollowing; + + print.set_task(task); + print.process(); + + Benchmark bench; + + for (SLAPrintObject *po : print.objects()) { + TriangleMesh holes; + sla::DrainHoles holepts = po->transformed_drainhole_points(); + + for (auto &hole: holepts) + holes.merge(sla::to_triangle_mesh(hole.to_mesh())); + + TriangleMesh hollowed_mesh = po->transformed_mesh(); + hollowed_mesh.merge(po->hollowed_interior_mesh()); + + hollowed_mesh.require_shared_vertices(); + holes.require_shared_vertices(); + + TriangleMesh drilled_mesh_igl = hollowed_mesh; + bench.start(); + MeshBoolean::minus(drilled_mesh_igl, holes); + bench.stop(); + + std::cout << "Mesh boolean duration with IGL: " << bench.getElapsedSec() << std::endl; + + TriangleMesh drilled_mesh_cgal = hollowed_mesh; + bench.start(); + MeshBoolean::cgal::self_union(drilled_mesh_cgal); + MeshBoolean::cgal::minus(drilled_mesh_cgal, holes); + bench.stop(); + + std::cout << "Mesh boolean duration with CGAL: " << bench.getElapsedSec() << std::endl; + + std::string name("obj"), outf; + outf = name + "igl" + std::to_string(po->model_object()->id().id) + ".obj"; + drilled_mesh_igl.WriteOBJFile(outf.c_str()); + + outf = name + "cgal" + std::to_string(po->model_object()->id().id) + ".obj"; + drilled_mesh_cgal.WriteOBJFile(outf.c_str()); + } return 0; } diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index e639319a2..69db96d3f 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -1,11 +1,15 @@ #include "MeshBoolean.hpp" +#include "libslic3r/TriangleMesh.hpp" +#undef PI // Include igl first. It defines "L" macro which then clashes with our localization #include #undef L -#include "libslic3r/TriangleMesh.hpp" - +// CGAL headers +#include +#include +#include namespace Slic3r { namespace MeshBoolean { @@ -13,27 +17,41 @@ namespace MeshBoolean { typedef Eigen::Map> MapMatrixXfUnaligned; typedef Eigen::Map> MapMatrixXiUnaligned; +typedef std::pair EigenMesh; + static TriangleMesh eigen_to_triangle_mesh(const Eigen::MatrixXd& VC, const Eigen::MatrixXi& FC) { - Pointf3s vertices; - for (size_t i=0; i facets; - for (size_t i=0; i facets(size_t(FC.rows())); + + for (Eigen::Index i = 0; i < VC.rows(); ++i) + points[size_t(i)] = VC.row(i); + + for (Eigen::Index i = 0; i < FC.rows(); ++i) + facets[size_t(i)] = FC.row(i); + + TriangleMesh out{points, facets}; out.require_shared_vertices(); return out; } +static EigenMesh triangle_mesh_to_eigen_mesh(const TriangleMesh &mesh) +{ + EigenMesh emesh; + emesh.first = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), + Eigen::Index(mesh.its.vertices.size()), + 3).cast(); + + emesh.second = MapMatrixXiUnaligned(mesh.its.indices.front().data(), + Eigen::Index(mesh.its.indices.size()), + 3); + return emesh; +} + void minus(TriangleMesh& A, const TriangleMesh& B) { - Eigen::MatrixXd VA = MapMatrixXfUnaligned(A.its.vertices.front().data(), A.its.vertices.size(), 3).cast(); - Eigen::MatrixXi FA = MapMatrixXiUnaligned(A.its.indices.front().data(), A.its.indices.size(), 3); - Eigen::MatrixXd VB = MapMatrixXfUnaligned(B.its.vertices.front().data(), B.its.vertices.size(), 3).cast(); - Eigen::MatrixXi FB = MapMatrixXiUnaligned(B.its.indices.front().data(), B.its.indices.size(), 3); + auto [VA, FA] = triangle_mesh_to_eigen_mesh(A); + auto [VB, FB] = triangle_mesh_to_eigen_mesh(B); Eigen::MatrixXd VC; Eigen::MatrixXi FC; @@ -43,21 +61,110 @@ void minus(TriangleMesh& A, const TriangleMesh& B) A = eigen_to_triangle_mesh(VC, FC); } - void self_union(TriangleMesh& mesh) { - Eigen::MatrixXd V = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), mesh.its.vertices.size(), 3).cast(); - Eigen::MatrixXi F = MapMatrixXiUnaligned(mesh.its.indices.front().data(), mesh.its.indices.size(), 3); + auto [V, F] = triangle_mesh_to_eigen_mesh(mesh); Eigen::MatrixXd VC; Eigen::MatrixXi FC; igl::MeshBooleanType boolean_type(igl::MESH_BOOLEAN_TYPE_UNION); igl::copyleft::cgal::mesh_boolean(V, F, Eigen::MatrixXd(), Eigen::MatrixXi(), boolean_type, VC, FC); + mesh = eigen_to_triangle_mesh(VC, FC); } +namespace cgal { +namespace CGALProc = CGAL::Polygon_mesh_processing; +namespace CGALParams = CGAL::Polygon_mesh_processing::parameters; + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using _CGALMesh = CGAL::Surface_mesh; + +struct CGALMesh { _CGALMesh m; }; + +static void triangle_mesh_to_cgal(const TriangleMesh &M, _CGALMesh &out) +{ + for (const Vec3f &v : M.its.vertices) + out.add_vertex(_CGALMesh::Point(v.x(), v.y(), v.z())); + + for (const Vec3crd &face : M.its.indices) { + auto f = face.cast(); + out.add_face(f(0), f(1), f(2)); + } +} + +static TriangleMesh cgal_to_triangle_mesh(const _CGALMesh &cgalmesh) +{ + Pointf3s points; + std::vector facets; + points.reserve(cgalmesh.num_vertices()); + facets.reserve(cgalmesh.num_faces()); + + for (auto &vi : cgalmesh.vertices()) { + auto &v = cgalmesh.point(vi); // Don't ask... + points.emplace_back(v.x(), v.y(), v.z()); + } + + for (auto &face : cgalmesh.faces()) { + auto vtc = cgalmesh.vertices_around_face(cgalmesh.halfedge(face)); + int i = 0; + Vec3crd trface; + for (auto v : vtc) trface(i++) = int(v.idx()); + facets.emplace_back(trface); + } + + TriangleMesh out{points, facets}; + out.require_shared_vertices(); + return out; +} + +std::unique_ptr triangle_mesh_to_cgal(const TriangleMesh &M) +{ + auto out = std::make_unique(); + triangle_mesh_to_cgal(M, out->m); + return out; +} + +void cgal_to_triangle_mesh(const CGALMesh &cgalmesh, TriangleMesh &out) +{ + out = cgal_to_triangle_mesh(cgalmesh.m); +} + +void minus(CGALMesh &A, CGALMesh &B) +{ + CGALProc::corefine_and_compute_difference(A.m, B.m, A.m); +} + +void self_union(CGALMesh &A) +{ + CGALProc::corefine(A.m, A.m); +} + +void minus(TriangleMesh &A, const TriangleMesh &B) +{ + CGALMesh meshA; + CGALMesh meshB; + triangle_mesh_to_cgal(A, meshA.m); + triangle_mesh_to_cgal(B, meshB.m); + + CGALMesh meshResult; + CGALProc::corefine_and_compute_difference(meshA.m, meshB.m, meshResult.m); + + A = cgal_to_triangle_mesh(meshResult.m); +} + +void self_union(TriangleMesh &m) +{ + _CGALMesh cgalmesh; + triangle_mesh_to_cgal(m, cgalmesh); + CGALProc::corefine(cgalmesh, cgalmesh); + + m = cgal_to_triangle_mesh(cgalmesh); +} + +} // namespace cgal } // namespace MeshBoolean } // namespace Slic3r diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp index 783bde3f5..14e3d3b7b 100644 --- a/src/libslic3r/MeshBoolean.hpp +++ b/src/libslic3r/MeshBoolean.hpp @@ -1,6 +1,7 @@ #ifndef libslic3r_MeshBoolean_hpp_ #define libslic3r_MeshBoolean_hpp_ +#include namespace Slic3r { @@ -11,6 +12,26 @@ namespace MeshBoolean { void minus(TriangleMesh& A, const TriangleMesh& B); void self_union(TriangleMesh& mesh); +namespace cgal { + +struct CGALMesh; + +std::unique_ptr triangle_mesh_to_cgal(const TriangleMesh &M); +void cgal_to_triangle_mesh(const CGALMesh &cgalmesh, TriangleMesh &out); + +// Do boolean mesh difference with CGAL bypassing igl. +void minus(TriangleMesh &A, const TriangleMesh &B); + +// Do self union only with CGAL. +void self_union(TriangleMesh& mesh); + +// does A = A - B +// CGAL takes non-const objects as arguments. I suppose it doesn't change B but +// there is no official garantee. +void minus(CGALMesh &A, CGALMesh &B); +void self_union(CGALMesh &A); + +} } // namespace MeshBoolean } // namespace Slic3r