From 3b0e0aaed43389c66bf3eebed035353b94da2dc7 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Thu, 20 Jun 2019 13:01:48 +0200
Subject: [PATCH] Fixes for degenerate cases.

---
 src/libslic3r/SLA/SLABasePool.cpp    | 75 ++++++++++++++--------------
 src/libslic3r/SLA/SLACommon.hpp      |  1 +
 src/libslic3r/SLA/SLASupportTree.cpp | 59 +++++++++++++++-------
 src/libslic3r/SLAPrint.cpp           |  8 ---
 4 files changed, 80 insertions(+), 63 deletions(-)

diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp
index 48d615a29..ace9cdd85 100644
--- a/src/libslic3r/SLA/SLABasePool.cpp
+++ b/src/libslic3r/SLA/SLABasePool.cpp
@@ -239,48 +239,49 @@ void offset(ExPolygon& sh, coord_t distance, bool edgerounding = true) {
     }
 }
 
- void offset(Polygon& sh, coord_t distance, bool edgerounding = true) {
-     using ClipperLib::ClipperOffset;
-     using ClipperLib::jtRound;
-     using ClipperLib::jtMiter;
-     using ClipperLib::etClosedPolygon;
-     using ClipperLib::Paths;
-     using ClipperLib::Path;
+void offset(Polygon &sh, coord_t distance, bool edgerounding = true)
+{
+    using ClipperLib::ClipperOffset;
+    using ClipperLib::jtRound;
+    using ClipperLib::jtMiter;
+    using ClipperLib::etClosedPolygon;
+    using ClipperLib::Paths;
+    using ClipperLib::Path;
 
-     auto&& ctour = Slic3rMultiPoint_to_ClipperPath(sh);
+    auto &&ctour = Slic3rMultiPoint_to_ClipperPath(sh);
 
-     // If the input is not at least a triangle, we can not do this algorithm
-     if(ctour.size() < 3) {
-         BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
-         return;
-     }
+    // If the input is not at least a triangle, we can not do this algorithm
+    if (ctour.size() < 3) {
+        BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
+        return;
+    }
 
-     ClipperOffset offs;
-     offs.ArcTolerance = 0.01*scaled(1.);
-     Paths result;
-     offs.AddPath(ctour, edgerounding ? jtRound : jtMiter, etClosedPolygon);
-     offs.Execute(result, static_cast<double>(distance));
+    ClipperOffset offs;
+    offs.ArcTolerance = 0.01 * scaled(1.);
+    Paths result;
+    offs.AddPath(ctour, edgerounding ? jtRound : jtMiter, etClosedPolygon);
+    offs.Execute(result, static_cast<double>(distance));
 
-     // Offsetting reverts the orientation and also removes the last vertex
-     // so boost will not have a closed polygon.
+    // Offsetting reverts the orientation and also removes the last vertex
+    // so boost will not have a closed polygon.
 
-     bool found_the_contour = false;
-     for(auto& r : result) {
-         if(ClipperLib::Orientation(r)) {
-             // We don't like if the offsetting generates more than one contour
-             // but throwing would be an overkill. Instead, we should warn the
-             // caller about the inability to create correct geometries
-             if(!found_the_contour) {
-                 auto rr = ClipperPath_to_Slic3rPolygon(r);
-                 sh.points.swap(rr.points);
-                 found_the_contour = true;
-             } else {
-                 BOOST_LOG_TRIVIAL(warning)
-                         << "Warning: offsetting result is invalid!";
-             }
-         }
-     }
- }
+    bool found_the_contour = false;
+    for (auto &r : result) {
+        if (ClipperLib::Orientation(r)) {
+            // We don't like if the offsetting generates more than one contour
+            // but throwing would be an overkill. Instead, we should warn the
+            // caller about the inability to create correct geometries
+            if (!found_the_contour) {
+                auto rr = ClipperPath_to_Slic3rPolygon(r);
+                sh.points.swap(rr.points);
+                found_the_contour = true;
+            } else {
+                BOOST_LOG_TRIVIAL(warning)
+                    << "Warning: offsetting result is invalid!";
+            }
+        }
+    }
+}
 
 /// Unification of polygons (with clipper) preserving holes as well.
 ExPolygons unify(const ExPolygons& shapes) {
diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp
index 2666f2a9c..eb986a259 100644
--- a/src/libslic3r/SLA/SLACommon.hpp
+++ b/src/libslic3r/SLA/SLACommon.hpp
@@ -73,6 +73,7 @@ public:
 
     inline double ground_level() const { return m_ground_level + m_gnd_offset; }
     inline void ground_level_offset(double o) { m_gnd_offset = o; }
+    inline double ground_level_offset() const { return m_gnd_offset; }
 
     inline const Eigen::MatrixXd& V() const { return m_V; }
     inline const Eigen::MatrixXi& F() const { return m_F; }
diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index cfb5c2e74..4de5c4c59 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -530,6 +530,7 @@ struct CompactBridge {
                   const Vec3d& ep,
                   const Vec3d& n,
                   double r,
+                  bool endball = true,
                   size_t steps = 45)
     {
         Vec3d startp = sp + r * n;
@@ -543,12 +544,14 @@ struct CompactBridge {
         double fa = 2*PI/steps;
         auto upperball = sphere(r, Portion{PI / 2 - fa, PI}, fa);
         for(auto& p : upperball.points) p += startp;
-
-        auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa);
-        for(auto& p : lowerball.points) p += endp;
-
+        
+        if(endball) {
+            auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa);
+            for(auto& p : lowerball.points) p += endp;
+            mesh.merge(lowerball);
+        }
+        
         mesh.merge(upperball);
-        mesh.merge(lowerball);
     }
 };
 
@@ -594,13 +597,17 @@ struct Pad {
             // base silhouette. Create the offsetted version and punch the
             // breaksticks across its perimeter.
             
-            ExPolygons modelbase_sticks = modelbase;
-
+            ExPolygons modelbase_offs = modelbase;
+            
             if (pcfg.embed_object.object_gap_mm > 0.0)
-                modelbase_sticks
-                    = offset_ex(modelbase_sticks,
+                modelbase_offs
+                    = offset_ex(modelbase_offs,
                                 float(scaled(pcfg.embed_object.object_gap_mm)));
             
+            // Create a spatial index of the support silhouette polygons.
+            // This will be used to check for intersections with the model
+            // silhouette polygons. If there is no intersection, then a certain
+            // part of the pad is redundant as it does not host any supports.
             BoxIndex bindex;
             {
                 unsigned idx = 0;
@@ -611,14 +618,27 @@ struct Pad {
                 }
             }
             
+            // Punching the breaksticks across the offsetted polygon perimeters
             ExPolygons pad_stickholes; pad_stickholes.reserve(modelbase.size());
-            for(auto& poly : modelbase_sticks) {
+            for(auto& poly : modelbase_offs) {
                 
-                if (!bindex.query(poly.contour.bounding_box(),
-                                  BoxIndex::qtIntersects).empty()) {
+                std::vector<BoxIndexEl> qres =
+                    bindex.query(poly.contour.bounding_box(),
+                                 BoxIndex::qtIntersects);
+                    
+                if (!qres.empty()) {
+                    
+                    // The model silhouette polygon 'poly' HAS an intersection
+                    // with the support silhouettes. Include this polygon
+                    // in the pad holes with the breaksticks and merge the
+                    // original (offsetted) version with the rest of the pad
+                    // base plate.
                     
                     basep.emplace_back(poly.contour);
                     
+                    // The holes of 'poly' will become positive parts of the
+                    // pad, so they has to be checked for intersections as well
+                    // and erased if there is no intersection with the supports
                     auto it = poly.holes.begin();
                     while(it != poly.holes.end()) {
                         if (bindex.query(it->bounding_box(),
@@ -627,7 +647,8 @@ struct Pad {
                         else
                             ++it;
                     }
-
+                    
+                    // Punch the breaksticks
                     sla::breakstick_holes(
                         poly,
                         pcfg.embed_object.object_gap_mm,   // padding
@@ -638,7 +659,7 @@ struct Pad {
                     pad_stickholes.emplace_back(poly);
                 }
             }
-
+            
             create_base_pool(basep, tmesh, pad_stickholes, cfg);
         } else {
             for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour);
@@ -1495,12 +1516,13 @@ class SLASupportTree::Algorithm {
             Vec3d pgnd   = {endp(X), endp(Y), gndlvl};
             can_add_base = result.score > min_dist;
             
+            double gnd_offs = m_mesh.ground_level_offset();
             auto abort_in_shame =
-                [&normal_mode, &can_add_base, &endp, jp, gndlvl]()
+                [gnd_offs, &normal_mode, &can_add_base, &endp, jp, gndlvl]()
             {
                 normal_mode  = true;
                 can_add_base = false;   // Nothing left to do, hope for the best
-                endp         = {jp(X), jp(Y), gndlvl};
+                endp         = {jp(X), jp(Y), gndlvl - gnd_offs };
             };
 
             // We have to check if the bridge is feasible.
@@ -2317,7 +2339,8 @@ public:
             double idist = bridge_mesh_intersect(sph, dir, R, true);
             double dist = ray_mesh_intersect(sj, dir);
             if (std::isinf(dist))
-                dist = sph(Z) - m_result.ground_level - HWIDTH_MM;
+                dist = sph(Z) - m_mesh.ground_level()
+                       + m_mesh.ground_level_offset();
 
             if(std::isnan(idist) || idist < 2*R ||
                std::isnan(dist)  || dist  < 2*R)
@@ -2329,7 +2352,7 @@ public:
             }
 
             Vec3d ej = sj + (dist + HWIDTH_MM)* dir;
-            m_result.add_compact_bridge(sp, ej, n, R);
+            m_result.add_compact_bridge(sp, ej, n, R, !std::isinf(dist));
         }
     }
 };
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 789a8120f..9e8b4bea2 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -1084,9 +1084,6 @@ void SLAPrint::process()
         using ClipperPolygons = std::vector<ClipperPolygon>;
         namespace sl = libnest2d::shapelike;    // For algorithms
 
-        // If the raster has vertical orientation, we will flip the coordinates
-//        bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait;
-
         // Set up custom union and diff functions for clipper polygons
         auto polyunion = [] (const ClipperPolygons& subjects)
         {
@@ -1194,11 +1191,6 @@ void SLAPrint::process()
                     sl::translate(poly, ClipperPoint{instances[i].shift(X),
                                                      instances[i].shift(Y)});
 
-//                    if (flpXY) {
-//                        for(auto& p : poly.Contour) std::swap(p.X, p.Y);
-//                        for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y);
-//                    }
-
                     polygons.emplace_back(std::move(poly));
                 }
             }