From d75b951c39e8219b1f3deab8bf95a4719f8cabc3 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Wed, 21 Dec 2022 13:53:33 +0100
Subject: [PATCH] FInish csg operation stacking in cgal side

Broken slicing
---
 .../CSGMesh/PerformCSGMeshBooleans.hpp        | 148 +++++++++++++++---
 src/libslic3r/CSGMesh/SliceCSGMesh.hpp        |  99 ++++++------
 src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp     |  49 +++---
 src/libslic3r/SLAPrintSteps.cpp               |  10 +-
 4 files changed, 213 insertions(+), 93 deletions(-)

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 <stack>
+#include <vector>
+
 #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<class It>
-void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMesh &cgalm,
-                              const Range<It>             &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<class Ex, class It>
+std::vector<CGALMeshPtr> get_cgalptrs(Ex policy, const Range<It> &csgrange)
+{
+    std::vector<CGALMeshPtr> 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<class It>
+void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMeshPtr &cgalm,
+                              const Range<It>                &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<Frame>{}};
+
+    if (!csgrange.empty() &&
+        csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push)
+        opstack.push({});
+
+    std::vector<CGALMeshPtr> 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<class It, class Visitor>
+It check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
+{
+    using namespace detail_cgal;
+
+    std::vector<CGALMeshPtr> 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<class It>
+It check_csgmesh_booleans(const Range<It> &csgrange)
+{
+    return check_csgmesh_booleans(csgrange, [](auto &) {});
 }
 
 template<class It>
@@ -51,7 +163,7 @@ MeshBoolean::cgal::CGALMeshPtr perform_csgmesh_booleans(const Range<It> &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<ExPolygons> &target,
+                  std::vector<ExPolygons> &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<float>      &slicegrid,
+                              const std::vector<ExPolygons> &slices,
+                              std::vector<size_t>            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<class ItCSG>
 std::vector<ExPolygons> slice_csgmesh_ex(
     const Range<ItCSG>          &csgrange,
@@ -18,6 +52,8 @@ std::vector<ExPolygons> slice_csgmesh_ex(
     const MeshSlicingParamsEx   &params,
     const std::function<void()> &throw_on_cancel = [] {})
 {
+    using namespace detail;
+
     struct Frame { CSGType op; std::vector<ExPolygons> slices; };
 
     std::stack opstack{std::vector<Frame>{}};
@@ -49,32 +85,13 @@ std::vector<ExPolygons> 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<ExPolygons> slice_csgmesh_ex(
             opstack.pop();
             std::vector<ExPolygons> &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<ExPolygons> 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<class It>
 VoxelGridPtr voxelize_csgmesh(const Range<It>      &csgrange,
                               const VoxelizeParams &params = {})
 {
+    using namespace detail;
+
     VoxelGridPtr ret;
 
     std::vector<VoxelGridPtr> grids (csgrange.size());
@@ -50,7 +71,8 @@ VoxelGridPtr voxelize_csgmesh(const Range<It>      &csgrange,
     struct Frame { CSGType op = CSGType::Union; VoxelGridPtr grid; };
     std::stack opstack{std::vector<Frame>{}};
 
-    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<It>      &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<It>      &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)
     {