diff --git a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp index da1ddce3f..733cdd1fe 100644 --- a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp +++ b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp @@ -1,8 +1,12 @@ #ifndef PERFORMCSGMESHBOOLEANS_HPP #define PERFORMCSGMESHBOOLEANS_HPP +#include +#include + #include "CSGMesh.hpp" +#include "libslic3r/Execution/ExecutionTBB.hpp" #include "libslic3r/MeshBoolean.hpp" namespace Slic3r { namespace csg { @@ -24,26 +28,134 @@ MeshBoolean::cgal::CGALMeshPtr get_cgalmesh(const CSGPartT &csgpart) return ret; } -template -void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMesh &cgalm, - const Range &csgparts) +namespace detail_cgal { + +using MeshBoolean::cgal::CGALMeshPtr; + +inline void perform_csg(CSGType op, CGALMeshPtr &dst, CGALMeshPtr &src) { - for (auto &csgpart : csgparts) { - auto m = get_cgalmesh(csgpart); - if (m) { - switch (get_operation(csgpart)) { - case CSGType::Union: - MeshBoolean::cgal::plus(cgalm, *m); - break; - case CSGType::Difference: - MeshBoolean::cgal::minus(cgalm, *m); - break; - case CSGType::Intersection: - MeshBoolean::cgal::intersect(cgalm, *m); - break; - } + 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 + +template +void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMeshPtr &cgalm, + const Range &csgrange) +{ + using MeshBoolean::cgal::CGALMesh; + using MeshBoolean::cgal::CGALMeshPtr; + using namespace detail_cgal; + + struct Frame { CSGType op = CSGType::Union; CGALMeshPtr cgalptr; }; + + std::stack opstack{std::vector{}}; + + if (!csgrange.empty() && + csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push) + opstack.push({}); + + 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({op, nullptr}); + op = CSGType::Union; + } + + Frame *top = &opstack.top(); + + if (!top->cgalptr && op == CSGType::Union) { + top->cgalptr = std::move(cgalptr); + } else if (top->cgalptr && cgalptr) { + 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); +} + +template +It check_csgmesh_booleans(const Range &csgrange, Visitor &&vfn) +{ + using namespace detail_cgal; + + std::vector cgalmeshes(csgrange.size()); + auto check_part = [&csgrange, &cgalmeshes](size_t i) + { + auto it = csgrange.begin(); + std::advance(it, i); + auto &csgpart = *it; + auto m = get_cgalmesh(csgpart); + + if (!m || MeshBoolean::cgal::empty(*m)) + return; + + if (!MeshBoolean::cgal::does_bound_a_volume(*m) || + MeshBoolean::cgal::does_self_intersect(*m)) + return; + + cgalmeshes[i] = std::move(m); + }; + execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part); + + It ret = csgrange.end(); + for (size_t i = 0; i < csgrange.size(); ++i) { + if (!cgalmeshes[i]) { + auto it = csgrange.begin(); + std::advance(it, i); + vfn(it); + + if (it == csgrange.end()) + ret = it; + } + } + + return csgrange.end(); +} + +template +It check_csgmesh_booleans(const Range &csgrange) +{ + return check_csgmesh_booleans(csgrange, [](auto &) {}); } template @@ -51,7 +163,7 @@ MeshBoolean::cgal::CGALMeshPtr perform_csgmesh_booleans(const Range &csgpart { auto ret = MeshBoolean::cgal::triangle_mesh_to_cgal(indexed_triangle_set{}); if (ret) - perform_csgmesh_booleans(*ret, csgparts); + perform_csgmesh_booleans(ret, csgparts); return ret; } diff --git a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp index 06302faa4..d9df97e95 100644 --- a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp @@ -11,6 +11,40 @@ namespace Slic3r { namespace csg { +namespace detail { + +void merge_slices(csg::CSGType op, size_t i, + std::vector &target, + std::vector &source) +{ + switch(op) { + case CSGType::Union: + for (ExPolygon &expoly : source[i]) + target[i].emplace_back(std::move(expoly)); + break; + case CSGType::Difference: + target[i] = diff_ex(target[i], source[i]); + break; + case CSGType::Intersection: + target[i] = intersection_ex(target[i], source[i]); + break; + } +} + +void collect_nonempty_indices(csg::CSGType op, + const std::vector &slicegrid, + const std::vector &slices, + std::vector indices) +{ + indices.clear(); + for (size_t i = 0; i < slicegrid.size(); ++i) { + if (op == CSGType::Intersection || !slices[i].empty()) + indices.emplace_back(i); + } +} + +} // namespace detail + template std::vector slice_csgmesh_ex( const Range &csgrange, @@ -18,6 +52,8 @@ std::vector slice_csgmesh_ex( const MeshSlicingParamsEx ¶ms, const std::function &throw_on_cancel = [] {}) { + using namespace detail; + struct Frame { CSGType op; std::vector slices; }; std::stack opstack{std::vector{}}; @@ -49,32 +85,13 @@ std::vector slice_csgmesh_ex( assert(slices.size() == slicegrid.size()); - nonempty_indices.clear(); - for (size_t i = 0; i < slicegrid.size(); ++i) { - if (op == CSGType::Intersection || !slices[i].empty()) - nonempty_indices.emplace_back(i); - } + collect_nonempty_indices(op, slicegrid, slices, nonempty_indices); - auto mergefn = [&csgpart, &slices, &top](size_t i){ - switch(get_operation(csgpart)) { - case CSGType::Union: - for (ExPolygon &expoly : slices[i]) - top->slices[i].emplace_back(std::move(expoly)); - - break; - case CSGType::Difference: - top->slices[i] = diff_ex(top->slices[i], slices[i]); - break; - case CSGType::Intersection: - top->slices[i] = intersection_ex(top->slices[i], slices[i]); - break; - } - }; - - execution::for_each(ex_tbb, - nonempty_indices.begin(), nonempty_indices.end(), - mergefn, - execution::max_concurrency(ex_tbb)); + execution::for_each( + ex_tbb, nonempty_indices.begin(), nonempty_indices.end(), + [op, &slices, &top](size_t i) { + merge_slices(op, i, top->slices, slices); + }, execution::max_concurrency(ex_tbb)); } if (get_stack_operation(csgpart) == CSGStackOp::Pop) { @@ -83,37 +100,19 @@ std::vector slice_csgmesh_ex( opstack.pop(); std::vector &prev_slices = opstack.top().slices; - nonempty_indices.clear(); - for (size_t i = 0; i < slicegrid.size(); ++i) { - if (popop == CSGType::Intersection || !popslices[i].empty()) - nonempty_indices.emplace_back(i); - } + collect_nonempty_indices(popop, slicegrid, popslices, nonempty_indices); - auto mergefn2 = [&popslices, &prev_slices, popop](size_t i){ - switch(popop) { - case CSGType::Union: - for (ExPolygon &expoly : popslices[i]) - prev_slices[i].emplace_back(std::move(expoly)); - - break; - case CSGType::Difference: - prev_slices[i] = diff_ex(prev_slices[i], popslices[i]); - break; - case CSGType::Intersection: - prev_slices[i] = intersection_ex(prev_slices[i], popslices[i]); - break; - } - }; - - execution::for_each(ex_tbb, - nonempty_indices.begin(), nonempty_indices.end(), - mergefn2, - execution::max_concurrency(ex_tbb)); + execution::for_each( + ex_tbb, nonempty_indices.begin(), nonempty_indices.end(), + [&popslices, &prev_slices, popop](size_t i) { + merge_slices(popop, i, prev_slices, popslices); + }, execution::max_concurrency(ex_tbb)); } } std::vector ret = std::move(opstack.top().slices); + // TODO: verify if this part can be omitted or not. execution::for_each(ex_tbb, ret.begin(), ret.end(), [](ExPolygons &slice) { auto it = std::remove_if(slice.begin(), slice.end(), [](const ExPolygon &p){ return p.area() < double(SCALED_EPSILON) * double(SCALED_EPSILON); diff --git a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp index ac54cf922..43019ef85 100644 --- a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp @@ -28,10 +28,31 @@ VoxelGridPtr get_voxelgrid(const CSGPartT &csgpart, VoxelizeParams params) return ret; } +namespace detail { + +inline void perform_csg(CSGType op, VoxelGridPtr &dst, VoxelGridPtr &src) +{ + switch (op) { + case CSGType::Union: + grid_union(*dst, *src); + break; + case CSGType::Difference: + grid_difference(*dst, *src); + break; + case CSGType::Intersection: + grid_intersection(*dst, *src); + break; + } +} + +} // namespace detail + template VoxelGridPtr voxelize_csgmesh(const Range &csgrange, const VoxelizeParams ¶ms = {}) { + using namespace detail; + VoxelGridPtr ret; std::vector grids (csgrange.size()); @@ -50,7 +71,8 @@ VoxelGridPtr voxelize_csgmesh(const Range &csgrange, struct Frame { CSGType op = CSGType::Union; VoxelGridPtr grid; }; std::stack opstack{std::vector{}}; - if (!csgrange.empty() && csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push) + if (!csgrange.empty() && + csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push) opstack.push({}); for (auto &csgpart : csgrange) { @@ -71,17 +93,7 @@ VoxelGridPtr voxelize_csgmesh(const Range &csgrange, if (!top->grid && op == CSGType::Union) { top->grid = std::move(partgrid); } else if (top->grid && partgrid) { - switch (get_operation(csgpart)) { - case CSGType::Union: - grid_union(*(top->grid), *partgrid); - break; - case CSGType::Difference: - grid_difference(*(top->grid), *partgrid); - break; - case CSGType::Intersection: - grid_intersection(*(top->grid), *partgrid); - break; - } + perform_csg(get_operation(csgpart), top->grid, partgrid); } if (get_stack_operation(csgpart) == CSGStackOp::Pop) { @@ -89,18 +101,7 @@ VoxelGridPtr voxelize_csgmesh(const Range &csgrange, auto popop = opstack.top().op; opstack.pop(); VoxelGridPtr &grid = opstack.top().grid; - - switch (popop) { - case CSGType::Union: - grid_union(*grid, *popgrid); - break; - case CSGType::Difference: - grid_difference(*grid, *popgrid); - break; - case CSGType::Intersection: - grid_intersection(*grid, *popgrid); - break; - } + perform_csg(popop, grid, popgrid); } } diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 623dee4e6..4e34ffbed 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -206,7 +206,15 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep step) { - po.m_preview_meshes[step] = TriangleMesh{generate_preview_vdb(po, step)}; + auto r = range(po.m_mesh_to_slice); + if (csg::check_csgmesh_booleans(r) == r.end()) { + auto cgalmesh = csg::perform_csgmesh_booleans(r); + po.m_preview_meshes[step] = MeshBoolean::cgal::cgal_to_triangle_mesh(*cgalmesh); + } else { + po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, + L("Can't do proper mesh booleans!")); + po.m_preview_meshes[step] = TriangleMesh{generate_preview_vdb(po, step)}; + } for (size_t i = size_t(step) + 1; i < slaposCount; ++i) {