#ifndef PERFORMCSGMESHBOOLEANS_HPP #define PERFORMCSGMESHBOOLEANS_HPP #include #include #include "CSGMesh.hpp" #include "libslic3r/Execution/ExecutionTBB.hpp" //#include "libslic3r/Execution/ExecutionSeq.hpp" #include "libslic3r/MeshBoolean.hpp" namespace Slic3r { namespace csg { enum class BooleanFailReason { OK, MeshEmpty, NotBoundAVolume, SelfIntersect, NoIntersection}; // This method can be overriden when a specific CSGPart type supports caching // of the voxel grid template MeshBoolean::cgal::CGALMeshPtr get_cgalmesh(const CSGPartT &csgpart) { const indexed_triangle_set *its = csg::get_mesh(csgpart); indexed_triangle_set dummy; if (!its) its = &dummy; MeshBoolean::cgal::CGALMeshPtr ret; indexed_triangle_set m = *its; its_transform(m, get_transform(csgpart), true); try { ret = MeshBoolean::cgal::triangle_mesh_to_cgal(m); } catch (...) { // errors are ignored, simply return null ret = nullptr; } return ret; } // This method can be overriden when a specific CSGPart type supports caching // of the voxel grid template MeshBoolean::mcut::McutMeshPtr get_mcutmesh(const CSGPartT& csgpart) { const indexed_triangle_set* its = csg::get_mesh(csgpart); indexed_triangle_set dummy; if (!its) its = &dummy; MeshBoolean::mcut::McutMeshPtr ret; indexed_triangle_set m = *its; its_transform(m, get_transform(csgpart), true); try { ret = MeshBoolean::mcut::triangle_mesh_to_mcut(m); } catch (...) { // errors are ignored, simply return null ret = nullptr; } return ret; } namespace detail_cgal { using MeshBoolean::cgal::CGALMeshPtr; inline void perform_csg(CSGType op, CGALMeshPtr &dst, CGALMeshPtr &src) { if (!dst && op == CSGType::Union && src) { dst = std::move(src); return; } if (!dst || !src) return; switch (op) { case CSGType::Union: MeshBoolean::cgal::plus(*dst, *src); break; case CSGType::Difference: MeshBoolean::cgal::minus(*dst, *src); break; case CSGType::Intersection: MeshBoolean::cgal::intersect(*dst, *src); break; } } template std::vector get_cgalptrs(Ex policy, const Range &csgrange) { std::vector ret(csgrange.size()); execution::for_each(policy, size_t(0), csgrange.size(), [&csgrange, &ret](size_t i) { auto it = csgrange.begin(); std::advance(it, i); auto &csgpart = *it; ret[i] = get_cgalmesh(csgpart); }); return ret; } } // namespace detail namespace detail_mcut { using MeshBoolean::mcut::McutMeshPtr; inline void perform_csg(CSGType op, McutMeshPtr& dst, McutMeshPtr& src) { if (!dst && op == CSGType::Union && src) { dst = std::move(src); return; } if (!dst || !src) return; switch (op) { case CSGType::Union: MeshBoolean::mcut::do_boolean(*dst, *src,"UNION"); break; case CSGType::Difference: MeshBoolean::mcut::do_boolean(*dst, *src,"A_NOT_B"); break; case CSGType::Intersection: MeshBoolean::mcut::do_boolean(*dst, *src,"INTERSECTION"); break; } } template std::vector get_mcutptrs(Ex policy, const Range& csgrange) { std::vector ret(csgrange.size()); execution::for_each(policy, size_t(0), csgrange.size(), [&csgrange, &ret](size_t i) { auto it = csgrange.begin(); std::advance(it, i); auto& csgpart = *it; ret[i] = get_mcutmesh(csgpart); }); return ret; } } // namespace mcut_detail // Process the sequence of CSG parts with CGAL. template void perform_csgmesh_booleans_cgal(MeshBoolean::cgal::CGALMeshPtr &cgalm, const Range &csgrange) { using MeshBoolean::cgal::CGALMesh; using MeshBoolean::cgal::CGALMeshPtr; using namespace detail_cgal; struct Frame { CSGType op; CGALMeshPtr cgalptr; explicit Frame(CSGType csgop = CSGType::Union) : op{ csgop } , cgalptr{ MeshBoolean::cgal::triangle_mesh_to_cgal(indexed_triangle_set{}) } {} }; std::stack opstack{ std::vector{} }; opstack.push(Frame{}); std::vector cgalmeshes = get_cgalptrs(ex_tbb, csgrange); size_t csgidx = 0; for (auto& csgpart : csgrange) { auto op = get_operation(csgpart); CGALMeshPtr& cgalptr = cgalmeshes[csgidx++]; if (get_stack_operation(csgpart) == CSGStackOp::Push) { opstack.push(Frame{ op }); op = CSGType::Union; } Frame* top = &opstack.top(); perform_csg(get_operation(csgpart), top->cgalptr, cgalptr); if (get_stack_operation(csgpart) == CSGStackOp::Pop) { CGALMeshPtr src = std::move(top->cgalptr); auto popop = opstack.top().op; opstack.pop(); CGALMeshPtr& dst = opstack.top().cgalptr; perform_csg(popop, dst, src); } } cgalm = std::move(opstack.top().cgalptr); } // Process the sequence of CSG parts with mcut. template void perform_csgmesh_booleans_mcut(MeshBoolean::mcut::McutMeshPtr& mcutm, const Range& csgrange) { using MeshBoolean::mcut::McutMesh; using MeshBoolean::mcut::McutMeshPtr; using namespace detail_mcut; struct Frame { CSGType op; McutMeshPtr mcutptr; explicit Frame(CSGType csgop = CSGType::Union) : op{ csgop } , mcutptr{ MeshBoolean::mcut::triangle_mesh_to_mcut(indexed_triangle_set{}) } {} }; std::stack opstack{ std::vector{} }; opstack.push(Frame{}); std::vector McutMeshes = get_mcutptrs(ex_tbb, csgrange); size_t csgidx = 0; for (auto& csgpart : csgrange) { auto op = get_operation(csgpart); McutMeshPtr& mcutptr = McutMeshes[csgidx++]; if (get_stack_operation(csgpart) == CSGStackOp::Push) { opstack.push(Frame{ op }); op = CSGType::Union; } Frame* top = &opstack.top(); perform_csg(get_operation(csgpart), top->mcutptr, mcutptr); if (get_stack_operation(csgpart) == CSGStackOp::Pop) { McutMeshPtr src = std::move(top->mcutptr); auto popop = opstack.top().op; opstack.pop(); McutMeshPtr& dst = opstack.top().mcutptr; perform_csg(popop, dst, src); } } mcutm = std::move(opstack.top().mcutptr); } template std::tuple check_csgmesh_booleans(const Range &csgrange, Visitor &&vfn) { using namespace detail_cgal; BooleanFailReason fail_reason = BooleanFailReason::OK; std::string fail_part_name; std::vector cgalmeshes(csgrange.size()); auto check_part = [&csgrange, &cgalmeshes,&fail_reason,&fail_part_name](size_t i) { auto it = csgrange.begin(); std::advance(it, i); auto &csgpart = *it; auto m = get_cgalmesh(csgpart); // mesh can be nullptr if this is a stack push or pull if (!get_mesh(csgpart) && get_stack_operation(csgpart) != CSGStackOp::Continue) { cgalmeshes[i] = MeshBoolean::cgal::triangle_mesh_to_cgal(indexed_triangle_set{}); return; } try { if (!m || MeshBoolean::cgal::empty(*m)) { BOOST_LOG_TRIVIAL(info) << "check_csgmesh_booleans fails! mesh " << i << "/" << csgrange.size() << " is empty, cannot do boolean!"; fail_reason= BooleanFailReason::MeshEmpty; fail_part_name = csgpart.name; return; } if (!MeshBoolean::cgal::does_bound_a_volume(*m)) { BOOST_LOG_TRIVIAL(info) << "check_csgmesh_booleans fails! mesh "< std::tuple check_csgmesh_booleans(const Range &csgrange, bool use_mcut=false) { if(!use_mcut) return check_csgmesh_booleans(csgrange, [](auto &) {}); else { using namespace detail_mcut; BooleanFailReason fail_reason = BooleanFailReason::OK; std::string fail_part_name; std::vector McutMeshes(csgrange.size()); auto check_part = [&csgrange, &McutMeshes,&fail_reason,&fail_part_name](size_t i) { auto it = csgrange.begin(); std::advance(it, i); auto& csgpart = *it; auto m = get_mcutmesh(csgpart); // mesh can be nullptr if this is a stack push or pull if (!get_mesh(csgpart) && get_stack_operation(csgpart) != CSGStackOp::Continue) { McutMeshes[i] = MeshBoolean::mcut::triangle_mesh_to_mcut(indexed_triangle_set{}); return; } try { if (!m || MeshBoolean::mcut::empty(*m)) { fail_reason=BooleanFailReason::MeshEmpty; fail_part_name = csgpart.name; return; } } catch (...) { return; } McutMeshes[i] = std::move(m); }; execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part); return { fail_reason,fail_part_name }; } } template MeshBoolean::cgal::CGALMeshPtr perform_csgmesh_booleans(const Range &csgparts) { auto ret = MeshBoolean::cgal::triangle_mesh_to_cgal(indexed_triangle_set{}); if (ret) perform_csgmesh_booleans_cgal(ret, csgparts); return ret; } template MeshBoolean::mcut::McutMeshPtr perform_csgmesh_booleans_mcut(const Range& csgparts) { auto ret = MeshBoolean::mcut::triangle_mesh_to_mcut(indexed_triangle_set{}); if (ret) perform_csgmesh_booleans_mcut(ret, csgparts); return ret; } } // namespace csg } // namespace Slic3r #endif // PERFORMCSGMESHBOOLEANS_HPP