From ddd0a9abb69ca7715641efaaa34dbbf030b552ef Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 11 Jun 2019 12:40:07 +0200
Subject: [PATCH 01/14] SPE-742: Builtin pad feature in zero elevation mode.

---
 .clang-format                        |   2 +-
 sandboxes/slabasebed/slabasebed.cpp  |  32 +--
 src/libslic3r/SLA/SLABasePool.cpp    | 321 ++++++++++++++++++----
 src/libslic3r/SLA/SLABasePool.hpp    |  17 +-
 src/libslic3r/SLA/SLACommon.hpp      |   7 +
 src/libslic3r/SLA/SLASupportTree.cpp | 391 ++++++++++++++++++++-------
 src/libslic3r/SLA/SLASupportTree.hpp |  21 +-
 src/libslic3r/SLAPrint.cpp           | 126 ++++++---
 8 files changed, 685 insertions(+), 232 deletions(-)

diff --git a/.clang-format b/.clang-format
index d5740f689..9a2c3ce1d 100644
--- a/.clang-format
+++ b/.clang-format
@@ -46,7 +46,7 @@ BreakConstructorInitializersBeforeComma: false
 BreakConstructorInitializers: BeforeComma
 BreakAfterJavaFieldAnnotations: false
 BreakStringLiterals: true
-ColumnLimit:     75
+ColumnLimit:     78
 CommentPragmas:  '^ IWYU pragma:'
 CompactNamespaces: false
 ConstructorInitializerAllOnOneLineOrOnePerLine: true
diff --git a/sandboxes/slabasebed/slabasebed.cpp b/sandboxes/slabasebed/slabasebed.cpp
index 0c34eb9f5..5393f61fd 100644
--- a/sandboxes/slabasebed/slabasebed.cpp
+++ b/sandboxes/slabasebed/slabasebed.cpp
@@ -15,7 +15,8 @@ const std::string USAGE_STR = {
 
 namespace Slic3r { namespace sla {
 
-Contour3D create_base_pool(const ExPolygons &ground_layer, 
+Contour3D create_base_pool(const Polygons &ground_layer, 
+                           const Polygons &holes = {},
                            const PoolConfig& cfg = PoolConfig());
 
 Contour3D walls(const Polygon& floor_plate, const Polygon& ceiling,
@@ -42,37 +43,28 @@ int main(const int argc, const char *argv[]) {
     model.ReadSTLFile(argv[1]);
     model.align_to_origin();
 
-    ExPolygons ground_slice;
-    sla::Contour3D mesh;
-//    TriangleMesh basepool;
-
+    Polygons ground_slice;
     sla::base_plate(model, ground_slice, 0.1f);
-
     if(ground_slice.empty()) return EXIT_FAILURE;
 
-//    ExPolygon bottom_plate = ground_slice.front();
-//    ExPolygon top_plate = bottom_plate;
-//    sla::offset(top_plate, coord_t(3.0/SCALING_FACTOR));
-//    sla::offset(bottom_plate, coord_t(1.0/SCALING_FACTOR));
+    Polygon gndfirst; gndfirst = ground_slice.front();
+    sla::offset_with_breakstick_holes(gndfirst, 0.5, 10, 0.3);
+
+    sla::Contour3D mesh;
+
 
     bench.start();
 
-//    TriangleMesh pool;
     sla::PoolConfig cfg;
     cfg.min_wall_height_mm = 0;
-    cfg.edge_radius_mm = 0.2;
-    mesh = sla::create_base_pool(ground_slice, cfg);
-    
-//    mesh.merge(triangulate_expolygon_3d(top_plate, 3.0, false));
-//    mesh.merge(triangulate_expolygon_3d(bottom_plate, 0.0, true));
-//    mesh = sla::walls(bottom_plate.contour, top_plate.contour, 0, 3, 2.0, [](){});
-    
+    cfg.edge_radius_mm = 0;
+    mesh = sla::create_base_pool(ground_slice, {}, cfg);
+
     bench.stop();
 
     cout << "Base pool creation time: " << std::setprecision(10)
          << bench.getElapsedSec() << " seconds." << endl;
-    
-//    auto point = []()
+
     for(auto& trind : mesh.indices) {
         Vec3d p0 = mesh.points[size_t(trind[0])];
         Vec3d p1 = mesh.points[size_t(trind[1])];
diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp
index 171d2b8d0..9b3f80f1a 100644
--- a/src/libslic3r/SLA/SLABasePool.cpp
+++ b/src/libslic3r/SLA/SLABasePool.cpp
@@ -7,9 +7,9 @@
 #include "Tesselate.hpp"
 
 // For debugging:
-//#include <fstream>
-//#include <libnest2d/tools/benchmark.h>
-//#include "SVG.hpp"
+// #include <fstream>
+// #include <libnest2d/tools/benchmark.h>
+#include "SVG.hpp"
 
 namespace Slic3r { namespace sla {
 
@@ -180,9 +180,10 @@ Contour3D walls(const Polygon& lower, const Polygon& upper,
 }
 
 /// Offsetting with clipper and smoothing the edges into a curvature.
-void offset(ExPolygon& sh, coord_t distance) {
+void offset(ExPolygon& 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;
@@ -199,11 +200,13 @@ void offset(ExPolygon& sh, coord_t distance) {
         return;
     }
 
+    auto jointype = edgerounding? jtRound : jtMiter;
+    
     ClipperOffset offs;
     offs.ArcTolerance = 0.01*mm(1);
     Paths result;
-    offs.AddPath(ctour, jtRound, etClosedPolygon);
-    offs.AddPaths(holes, jtRound, etClosedPolygon);
+    offs.AddPath(ctour, jointype, etClosedPolygon);
+    offs.AddPaths(holes, jointype, etClosedPolygon);
     offs.Execute(result, static_cast<double>(distance));
 
     // Offsetting reverts the orientation and also removes the last vertex
@@ -233,6 +236,49 @@ void offset(ExPolygon& sh, coord_t distance) {
     }
 }
 
+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);
+
+    // 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*mm(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.
+
+    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) {
     using ClipperLib::ptSubject;
@@ -303,6 +349,118 @@ ExPolygons unify(const ExPolygons& shapes) {
     return retv;
 }
 
+Polygons unify(const Polygons& shapes) {
+    using ClipperLib::ptSubject;
+    
+    bool closed = true;
+    bool valid = true;
+
+    ClipperLib::Clipper clipper;
+
+    for(auto& path : shapes) {
+        auto clipperpath = Slic3rMultiPoint_to_ClipperPath(path);
+
+        if(!clipperpath.empty())
+            valid &= clipper.AddPath(clipperpath, ptSubject, closed);
+    }
+
+    if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!";
+
+    ClipperLib::Paths result;
+    clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero);
+
+    Polygons ret;
+    for (ClipperLib::Path &p : result) {
+        Polygon pp = ClipperPath_to_Slic3rPolygon(p);
+        if (!pp.is_clockwise()) ret.emplace_back(std::move(pp));
+    }
+    
+    return ret;
+}
+
+// Function to cut tiny connector cavities for a given polygon. The input poly
+// will be offsetted by "padding" and small rectangle shaped cavities will be
+// inserted along the perimeter in every "stride" distance. The stick rectangles
+// will have a with about "stick_width". The input dimensions are in world 
+// measure, not the scaled clipper units.
+void offset_with_breakstick_holes(ExPolygon& poly,
+                                  double padding,
+                                  double stride,
+                                  double stick_width,
+                                  double penetration)
+{
+    // We do the basic offsetting first
+    const bool dont_round_edges = false;
+    offset(poly, coord_t(padding / SCALING_FACTOR), dont_round_edges);
+
+    SVG svg("bridgestick_plate.svg");
+    svg.draw(poly);
+
+    auto transf = [stick_width, penetration, padding, stride](Points &pts) {
+        // The connector stick will be a small rectangle with dimensions
+        // stick_width x (penetration + padding) to have some penetration
+        // into the input polygon.
+
+        Points out;
+        out.reserve(2 * pts.size()); // output polygon points
+
+        // stick bottom and right edge dimensions
+        double sbottom = stick_width / SCALING_FACTOR;
+        double sright  = (penetration + padding) / SCALING_FACTOR;
+
+        // scaled stride distance
+        double sstride = stride / SCALING_FACTOR;
+        double t       = 0;
+
+        // process pairs of vertices as an edge, start with the last and
+        // first point
+        for (size_t i = pts.size() - 1, j = 0; j < pts.size(); i = j, ++j) {
+            // Get vertices and the direction vectors
+            const Point &a = pts[i], &b = pts[j];
+            Vec2d        dir = b.cast<double>() - a.cast<double>();
+            double       nrm = dir.norm();
+            dir /= nrm;
+            Vec2d dirp(-dir(Y), dir(X));
+
+            // Insert start point
+            out.emplace_back(a);
+
+            // dodge the start point, do not make sticks on the joins
+            while (t < sright) t += sright;
+            double tend = nrm - sright;
+
+            while (t < tend) { // insert the stick on the polygon perimeter
+
+                // calculate the stick rectangle vertices and insert them
+                // into the output.
+                Point p1 = a + (t * dir).cast<coord_t>();
+                Point p2 = p1 + (sright * dirp).cast<coord_t>();
+                Point p3 = p2 + (sbottom * dir).cast<coord_t>();
+                Point p4 = p3 + (sright * -dirp).cast<coord_t>();
+                out.insert(out.end(), {p1, p2, p3, p4});
+
+                // continue along the perimeter
+                t += sstride;
+            }
+
+            t = t - nrm;
+
+            // Insert edge endpoint
+            out.emplace_back(b);
+        }
+        
+        // move the new points
+        out.shrink_to_fit();
+        pts.swap(out);
+    };
+
+    transf(poly.contour.points);
+    for (auto &h : poly.holes) transf(h.points);
+    
+    svg.draw(poly);
+    svg.Close();
+}
+
 /// Only a debug function to generate top and bottom plates from a 2D shape.
 /// It is not used in the algorithm directly.
 inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) {
@@ -467,41 +625,38 @@ inline Point centroid(Points& pp) {
     return c;
 }
 
-inline Point centroid(const ExPolygon& poly) {
-    return poly.contour.centroid();
+inline Point centroid(const Polygon& poly) {
+    return poly.centroid();
 }
 
 /// A fake concave hull that is constructed by connecting separate shapes
 /// with explicit bridges. Bridges are generated from each shape's centroid
 /// to the center of the "scene" which is the centroid calculated from the shape
 /// centroids (a star is created...)
-ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
-                        ThrowOnCancel throw_on_cancel = [](){})
+Polygons concave_hull(const Polygons& polys, double max_dist_mm = 50,
+                      ThrowOnCancel throw_on_cancel = [](){})
 {
     namespace bgi = boost::geometry::index;
-    using SpatElement = std::pair<BoundingBox, unsigned>;
+    using SpatElement = std::pair<Point, unsigned>;
     using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
 
-    if(polys.empty()) return ExPolygons();
+    if(polys.empty()) return Polygons();
+    
+    const double max_dist = mm(max_dist_mm);
 
-    ExPolygons punion = unify(polys);   // could be redundant
+    Polygons punion = unify(polys);   // could be redundant
 
     if(punion.size() == 1) return punion;
 
     // We get the centroids of all the islands in the 2D slice
     Points centroids; centroids.reserve(punion.size());
     std::transform(punion.begin(), punion.end(), std::back_inserter(centroids),
-                   [](const ExPolygon& poly) { return centroid(poly); });
-
-
-    SpatIndex boxindex; unsigned idx = 0;
-    std::for_each(punion.begin(), punion.end(),
-                  [&boxindex, &idx](const ExPolygon& expo) {
-        BoundingBox bb(expo);
-        boxindex.insert(std::make_pair(bb, idx++));
-    });
-
+                   [](const Polygon& poly) { return centroid(poly); });
 
+    SpatIndex ctrindex;
+    unsigned  idx = 0;
+    for(const Point &ct : centroids) ctrindex.insert(std::make_pair(ct, idx++));
+    
     // Centroid of the centroids of islands. This is where the additional
     // connector sticks are routed.
     Point cc = centroid(centroids);
@@ -511,25 +666,32 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
     idx = 0;
     std::transform(centroids.begin(), centroids.end(),
                    std::back_inserter(punion),
-                   [&punion, &boxindex, cc, max_dist_mm, &idx, throw_on_cancel]
+                   [&centroids, &ctrindex, cc, max_dist, &idx, throw_on_cancel]
                    (const Point& c)
     {
         throw_on_cancel();
         double dx = x(c) - x(cc), dy = y(c) - y(cc);
         double l = std::sqrt(dx * dx + dy * dy);
         double nx = dx / l, ny = dy / l;
-        double max_dist = mm(max_dist_mm);
-
-        ExPolygon& expo = punion[idx++];
-        BoundingBox querybb(expo);
-
-        querybb.offset(max_dist);
+        
+        Point& ct = centroids[idx];
+        
         std::vector<SpatElement> result;
-        boxindex.query(bgi::intersects(querybb), std::back_inserter(result));
-        if(result.size() <= 1) return ExPolygon();
+        ctrindex.query(bgi::nearest(ct, 2), std::back_inserter(result));
 
-        ExPolygon r;
-        auto& ctour = r.contour.points;
+        double dist = max_dist;
+        for (const SpatElement &el : result)
+            if (el.second != idx) {
+                dist = Line(el.first, ct).length();
+                break;
+            }
+        
+        idx++;
+        
+        if (dist >= max_dist) return Polygon();
+        
+        Polygon r;
+        auto& ctour = r.points;
 
         ctour.reserve(3);
         ctour.emplace_back(cc);
@@ -538,7 +700,7 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
         ctour.emplace_back(c + Point( -y(d),  x(d) ));
         ctour.emplace_back(c + Point(  y(d), -x(d) ));
         offset(r, mm(1));
-
+            
         return r;
     });
 
@@ -576,13 +738,14 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
 
     ExPolygons utmp = unify(tmp);
 
-    for(auto& o : utmp) {
-        auto&& smp = o.simplify(0.1/SCALING_FACTOR);
+    for(ExPolygon& o : utmp) {
+        auto&& smp = o.simplify(0.1/SCALING_FACTOR); // TODO: is this important?
         output.insert(output.end(), smp.begin(), smp.end());
     }
 }
 
-Contour3D create_base_pool(const ExPolygons &ground_layer, 
+Contour3D create_base_pool(const Polygons &ground_layer, 
+                           const ExPolygons &obj_self_pad = {},
                            const PoolConfig& cfg = PoolConfig()) 
 {
     // for debugging:
@@ -597,7 +760,7 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
     // serve as the bottom plate of the pad. We will offset this concave hull
     // and then offset back the result with clipper with rounding edges ON. This
     // trick will create a nice rounded pad shape.
-    ExPolygons concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel);
+    Polygons concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel);
 
     const double thickness      = cfg.min_wall_thickness_mm;
     const double wingheight     = cfg.min_wall_height_mm;
@@ -617,42 +780,37 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
 
     Contour3D pool;
 
-    for(ExPolygon& concaveh : concavehs) {
-        if(concaveh.contour.points.empty()) return pool;
-
-        // Get rid of any holes in the concave hull output.
-        concaveh.holes.clear();
+    for(Polygon& concaveh : concavehs) {
+        if(concaveh.points.empty()) return pool;
 
         // Here lies the trick that does the smoothing only with clipper offset
         // calls. The offset is configured to round edges. Inner edges will
         // be rounded because we offset twice: ones to get the outer (top) plate
         // and again to get the inner (bottom) plate
         auto outer_base = concaveh;
-        outer_base.holes.clear();
         offset(outer_base, s_safety_dist + s_wingdist + s_thickness);
 
-        ExPolygon bottom_poly = outer_base;
-        bottom_poly.holes.clear();
+        ExPolygon bottom_poly; bottom_poly.contour = outer_base;
         offset(bottom_poly, -s_bottom_offs);
 
         // Punching a hole in the top plate for the cavity
         ExPolygon top_poly;
         ExPolygon middle_base;
         ExPolygon inner_base;
-        top_poly.contour = outer_base.contour;
+        top_poly.contour = outer_base;
 
         if(wingheight > 0) {
-            inner_base = outer_base;
+            inner_base.contour = outer_base;
             offset(inner_base, -(s_thickness + s_wingdist + s_eradius));
 
-            middle_base = outer_base;
+            middle_base.contour = outer_base;
             offset(middle_base, -s_thickness);
             top_poly.holes.emplace_back(middle_base.contour);
             auto& tph = top_poly.holes.back().points;
             std::reverse(tph.begin(), tph.end());
         }
 
-        ExPolygon ob = outer_base; double wh = 0;
+        ExPolygon ob; ob.contour = outer_base; double wh = 0;
 
         // now we will calculate the angle or portion of the circle from
         // pi/2 that will connect perfectly with the bottom plate.
@@ -713,11 +871,56 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
                              wh, -wingdist, thrcl));
         }
 
-        // Now we need to triangulate the top and bottom plates as well as the
-        // cavity bottom plate which is the same as the bottom plate but it is
-        // elevated by the thickness.
-        pool.merge(triangulate_expolygon_3d(top_poly));
-        pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true));
+        if (cfg.embed_object) {
+            ExPolygons pp = diff_ex(to_polygons(bottom_poly),
+                                    to_polygons(obj_self_pad));
+
+            // Generate outer walls
+            auto fp = [](const Point &p, Point::coord_type z) {
+                return unscale(x(p), y(p), z);
+            };
+
+            auto straight_walls = [&pool, s_thickness, fp](const Polygon &cntr)
+            {
+                auto lines = cntr.lines();
+                bool cclk   = cntr.is_counter_clockwise();
+                
+                for (auto &l : lines) {
+                    auto s = coord_t(pool.points.size());
+                    pool.points.emplace_back(fp(l.a, -s_thickness));
+                    pool.points.emplace_back(fp(l.b, -s_thickness));
+                    pool.points.emplace_back(fp(l.a, 0));
+                    pool.points.emplace_back(fp(l.b, 0));
+                    
+                    if(cclk) {
+                        pool.indices.emplace_back(s + 3, s + 1, s);
+                        pool.indices.emplace_back(s + 2, s + 3, s);
+                    } else {
+                        pool.indices.emplace_back(s, s + 1, s + 3);
+                        pool.indices.emplace_back(s, s + 3, s + 2);
+                    }
+                }
+            };
+
+            for (ExPolygon &ep : pp) {
+                pool.merge(triangulate_expolygon_3d(ep));
+                pool.merge(triangulate_expolygon_3d(ep, -fullheight, true));
+
+                for (auto &h : ep.holes) straight_walls(h);
+            }
+            
+            // Skip the outer contour. TODO: make sure the first in the list
+            // IS the outer contour.
+            for (auto it = std::next(pp.begin()); it != pp.end(); ++it)
+                straight_walls(it->contour);
+            
+        } else {
+            // Now we need to triangulate the top and bottom plates as well as
+            // the cavity bottom plate which is the same as the bottom plate
+            // but it is elevated by the thickness.
+            pool.merge(triangulate_expolygon_3d(top_poly));
+            pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true));
+        }
 
         if(wingheight > 0)
             pool.merge(triangulate_expolygon_3d(inner_base, -wingheight));
@@ -727,8 +930,8 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
     return pool;
 }
 
-void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
-                      const PoolConfig& cfg)
+void create_base_pool(const Polygons &ground_layer, TriangleMesh& out,
+                      const ExPolygons &holes, const PoolConfig& cfg)
 {
     
 
@@ -738,7 +941,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
     // std::fstream fout("pad_debug.obj", std::fstream::out);
     // if(fout.good()) pool.to_obj(fout);
 
-    out.merge(mesh(create_base_pool(ground_layer, cfg)));
+    out.merge(mesh(create_base_pool(ground_layer, holes, cfg)));
 }
 
 }
diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp
index 3c88e58c8..0ed26b6e7 100644
--- a/src/libslic3r/SLA/SLABasePool.hpp
+++ b/src/libslic3r/SLA/SLABasePool.hpp
@@ -8,7 +8,9 @@
 namespace Slic3r {
 
 class ExPolygon;
+class Polygon;
 using ExPolygons = std::vector<ExPolygon>;
+using Polygons = std::vector<Polygon>;
 
 class TriangleMesh;
 
@@ -23,12 +25,24 @@ void base_plate(const TriangleMesh& mesh,       // input mesh
                 float layerheight = 0.05f,      // The sampling height
                 ThrowOnCancel thrfn = [](){});  // Will be called frequently
 
+// Function to cut tiny connector cavities for a given polygon. The input poly
+// will be offsetted by "padding" and small rectangle shaped cavities will be
+// inserted along the perimeter in every "stride" distance. The stick rectangles
+// will have a with about "stick_width". The input dimensions are in world 
+// measure, not the scaled clipper units.
+void offset_with_breakstick_holes(ExPolygon& poly,
+                                  double padding,
+                                  double stride,
+                                  double stick_width,
+                                  double penetration = 0.0);
+
 struct PoolConfig {
     double min_wall_thickness_mm = 2;
     double min_wall_height_mm = 5;
     double max_merge_distance_mm = 50;
     double edge_radius_mm = 1;
     double wall_slope = std::atan(1.0);          // Universal constant for Pi/4
+    bool   embed_object = false;
 
     ThrowOnCancel throw_on_cancel = [](){};
 
@@ -42,8 +56,9 @@ struct PoolConfig {
 };
 
 /// Calculate the pool for the mesh for SLA printing
-void create_base_pool(const ExPolygons& base_plate,
+void create_base_pool(const Polygons& base_plate,
                       TriangleMesh& output_mesh,
+                      const ExPolygons& holes,
                       const PoolConfig& = PoolConfig());
 
 /// TODO: Currently the base plate of the pool will have half the height of the
diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp
index 855802759..2b72aa92c 100644
--- a/src/libslic3r/SLA/SLACommon.hpp
+++ b/src/libslic3r/SLA/SLACommon.hpp
@@ -72,6 +72,7 @@ public:
     ~EigenMesh3D();
 
     inline double ground_level() const { return m_ground_level; }
+    inline double& ground_level() { return m_ground_level; }
 
     inline const Eigen::MatrixXd& V() const { return m_V; }
     inline const Eigen::MatrixXi& F() const { return m_F; }
@@ -149,6 +150,12 @@ public:
 #endif /* SLIC3R_SLA_NEEDS_WINDTREE */
 
     double squared_distance(const Vec3d& p, int& i, Vec3d& c) const;
+    inline double squared_distance(const Vec3d &p) const
+    {
+        int   i;
+        Vec3d c;
+        return squared_distance(p, i, c);
+    }
 };
 
 
diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index cb2001024..43ffaed86 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -13,6 +13,7 @@
 #include <libslic3r/Model.hpp>
 
 #include <libnest2d/optimizers/nlopt/genetic.hpp>
+#include <libnest2d/optimizers/nlopt/subplex.hpp>
 #include <boost/log/trivial.hpp>
 #include <tbb/parallel_for.h>
 #include <libslic3r/I18N.hpp>
@@ -71,6 +72,8 @@ const double SupportConfig::normal_cutoff_angle = 150.0 * M_PI / 180.0;
 // The shortest distance of any support structure from the model surface
 const double SupportConfig::safety_distance_mm = 0.5;
 
+const double SupportConfig::pillar_base_safety_distance_mm = 0.5;
+
 const double SupportConfig::max_solo_pillar_height_mm = 15.0;
 const double SupportConfig::max_dual_pillar_height_mm = 35.0;
 const double   SupportConfig::optimizer_rel_score_diff = 1e-6;
@@ -413,7 +416,7 @@ struct Pillar {
         assert(steps > 0);
 
         height = jp(Z) - endp(Z);
-        if(height > 0) { // Endpoint is below the starting point
+        if(height > EPSILON) { // Endpoint is below the starting point
 
             // We just create a bridge geometry with the pillar parameters and
             // move the data.
@@ -556,28 +559,47 @@ struct Pad {
     PoolConfig cfg;
     double zlevel = 0;
 
-    Pad() {}
+    Pad() = default;
 
     Pad(const TriangleMesh& object_support_mesh,
-        const ExPolygons& baseplate,
+        const ExPolygons& modelbase,
         double ground_level,
         const PoolConfig& pcfg) :
         cfg(pcfg),
-        zlevel(ground_level +
-               (sla::get_pad_fullheight(pcfg) - sla::get_pad_elevation(pcfg)) )
+        zlevel(ground_level + 
+               sla::get_pad_fullheight(pcfg) -
+               sla::get_pad_elevation(pcfg))
     {
-        ExPolygons basep;
+        Polygons basep;
         cfg.throw_on_cancel();
-
+        
         // The 0.1f is the layer height with which the mesh is sampled and then
         // the layers are unified into one vector of polygons.
-        base_plate(object_support_mesh, basep,
+        ExPolygons platetmp;
+        base_plate(object_support_mesh, platetmp,
                    float(cfg.min_wall_height_mm + cfg.min_wall_thickness_mm),
                    0.1f, pcfg.throw_on_cancel);
+        
+        // We don't need the holes for the base plate from the supports
+        for (const ExPolygon &bp : platetmp)  basep.emplace_back(bp.contour);
+        for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour);
+        
+        if(pcfg.embed_object) {
+            
+            auto modelbase_sticks = modelbase;
+            for(auto& poly : modelbase_sticks)
+                sla::offset_with_breakstick_holes(
+                    poly,
+                    SupportConfig::pillar_base_safety_distance_mm, // padding
+                    10,     // stride (mm)
+                    0.3,    // stick_width (mm)
+                    0.1);   // penetration (mm)
 
-        for(auto& bp : baseplate) basep.emplace_back(bp);
+            create_base_pool(basep, tmesh, modelbase_sticks, cfg);
+        } else {
+            create_base_pool(basep, tmesh, {}, cfg);
+        }
 
-        create_base_pool(basep, tmesh, cfg);
         tmesh.translate(0, 0, float(zlevel));
     }
 
@@ -763,9 +785,9 @@ public:
     }
 
     const Pad& create_pad(const TriangleMesh& object_supports,
-                          const ExPolygons& baseplate,
+                          const ExPolygons& modelbase,
                           const PoolConfig& cfg) {
-        m_pad = Pad(object_supports, baseplate, ground_level, cfg);
+        m_pad = Pad(object_supports, modelbase, ground_level, cfg);
         return m_pad;
     }
 
@@ -1149,7 +1171,7 @@ class SLASupportTree::Algorithm {
             auto hr = m.query_ray_hit(p + sd*dir, dir);
 
             if(ins_check && hr.is_inside()) {
-                if(hr.distance() > r + sd) hits[i] = HitResult(0.0);
+                if(hr.distance() > 2 * r + sd) hits[i] = HitResult(0.0);
                 else {
                     // re-cast the ray from the outside of the object
                     auto hr2 =
@@ -1264,9 +1286,12 @@ class SLASupportTree::Algorithm {
 
     // For connecting a head to a nearby pillar.
     bool connect_to_nearpillar(const Head& head, long nearpillar_id) {
-
-        auto nearpillar = [this, nearpillar_id]() { return m_result.pillar(nearpillar_id); };
-        if(nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false;
+        
+        auto nearpillar = [this, nearpillar_id]() {
+            return m_result.pillar(nearpillar_id);
+        };
+        
+        if (nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false;
 
         Vec3d headjp = head.junction_point();
         Vec3d nearjp_u = nearpillar().startpoint();
@@ -1369,6 +1394,108 @@ class SLASupportTree::Algorithm {
 
         return nearest_id >= 0;
     }
+    
+    // This is a proxy function for pillar creation which will mind the gap
+    // between the pad and the model bottom in zero elevation mode.
+    void create_ground_pillar(const Vec3d &jp,
+                              const Vec3d &sourcedir,
+                              double       radius,
+                              int          head_id = -1)
+    {
+        // People were killed for this number (seriously)
+        static const double SQR2 = std::sqrt(2.0);
+
+        double gndlvl       = m_result.ground_level;
+        Vec3d  endp         = {jp(X), jp(Y), gndlvl};
+        double sd           = SupportConfig::pillar_base_safety_distance_mm;
+        int    pillar_id    = -1;
+        double min_dist     = sd + m_cfg.base_radius_mm + EPSILON;
+        double dist         = 0;
+        bool   can_add_base = true;
+        bool   normal_mode  = true;
+
+        if (m_cfg.object_elevation_mm < EPSILON
+            && (dist = std::sqrt(m_mesh.squared_distance(endp))) < min_dist) {
+            // Get the distance from the mesh. This can be later optimized
+            // to get the distance in 2D plane because we are dealing with
+            // the ground level only.
+
+            normal_mode     = false;
+            double mv       = min_dist - dist;
+            double azimuth  = std::atan2(sourcedir(Y), sourcedir(X));
+            double sinpolar = std::sin(PI - m_cfg.bridge_slope);
+            double cospolar = std::cos(PI - m_cfg.bridge_slope);
+            double cosazm   = std::cos(azimuth);
+            double sinazm   = std::sin(azimuth);
+
+            auto dir = Vec3d(cosazm * sinpolar, sinazm * sinpolar, cospolar)
+                           .normalized();
+
+            using namespace libnest2d::opt;
+            StopCriteria scr;
+            scr.stop_score = min_dist;
+            SubplexOptimizer solver(scr);
+
+            auto result = solver.optimize_max(
+                [this, dir, jp, gndlvl](double mv) {
+                    Vec3d endp = jp + SQR2 * mv * dir;
+                    endp(Z)    = gndlvl;
+                    return std::sqrt(m_mesh.squared_distance(endp));
+                },
+                initvals(mv), bound(0.0, 2 * min_dist));
+
+            mv           = std::get<0>(result.optimum);
+            endp         = jp + std::sqrt(2) * mv * dir;
+            Vec3d pgnd   = {endp(X), endp(Y), gndlvl};
+            can_add_base = result.score > min_dist;
+
+            // We have to check if the bridge is feasible.
+            if (bridge_mesh_intersect(jp, dir, radius) < (endp - jp).norm()) {
+                normal_mode = true;
+                endp        = {jp(X), jp(Y), gndlvl};
+            }
+            else {
+                // If the new endpoint is below ground, do not make a pillar
+                if (endp(Z) < gndlvl)
+                    endp = endp - SQR2 * (gndlvl - endp(Z)) * dir; // back off
+                else {
+                    Pillar &plr = m_result.add_pillar(endp, pgnd, radius);
+
+                    if (can_add_base)
+                        plr.add_base(m_cfg.base_height_mm,
+                                     m_cfg.base_radius_mm);
+
+                    pillar_id = plr.id;
+                }
+
+                m_result.add_bridge(jp, endp, radius);
+                m_result.add_junction(endp, radius);
+
+                // Add a degenerated pillar and the bridge.
+                // The degenerate pillar will have zero length and it will
+                // prevent from queries of head_pillar() to have non-existing
+                // pillar when the head should have one.
+                if (head_id >= 0)
+                    m_result.add_pillar(unsigned(head_id), jp, radius);
+            }
+        }
+        
+        if (normal_mode) {
+            Pillar &plr = head_id >= 0
+                              ? m_result.add_pillar(unsigned(head_id),
+                                                    endp,
+                                                    radius)
+                              : m_result.add_pillar(jp, endp, radius);
+
+            if (can_add_base)
+                plr.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm);
+
+            pillar_id = plr.id;
+        } 
+            
+        if(pillar_id >= 0) // Save the pillar endpoint in the spatial index
+            m_pillar_index.insert(endp, pillar_id);
+    }
 
 public:
 
@@ -1447,9 +1574,9 @@ public:
             // (Quaternion::FromTwoVectors) and apply the rotation to the
             // arrow head.
 
-            double z = n(2);
-            double r = 1.0;     // for normalized vector
-            double polar = std::acos(z / r);
+            double z       = n(2);
+            double r       = 1.0; // for normalized vector
+            double polar   = std::acos(z / r);
             double azimuth = std::atan2(n(1), n(0));
 
             // skip if the tilt is not sane
@@ -1473,14 +1600,14 @@ public:
                                 std::cos(polar)).normalized();
 
                 // check available distance
-                double t = pinhead_mesh_intersect(
-                                  hp, // touching point
-                                  nn, // normal
-                                  pin_r,
-                                  m_cfg.head_back_radius_mm,
-                                  w);
+                EigenMesh3D::hit_result t
+                    = pinhead_mesh_intersect(hp, // touching point
+                                             nn, // normal
+                                             pin_r,
+                                             m_cfg.head_back_radius_mm,
+                                             w);
 
-                if(t <= w) {
+                if(t.distance() <= w) {
 
                     // Let's try to optimize this angle, there might be a
                     // viable normal that doesn't collide with the model
@@ -1523,12 +1650,17 @@ public:
                 // save the verified and corrected normal
                 m_support_nmls.row(fidx) = nn;
 
-                if(t > w) {
-                    // mark the point for needing a head.
-                    m_iheads.emplace_back(fidx);
-                } else if( polar >= 3*PI/4 ) {
-                    // Headless supports do not tilt like the headed ones so
-                    // the normal should point almost to the ground.
+                if (t.distance() > w) {
+                    // Check distance from ground, we might have zero elevation.
+                    if (hp(Z) + w * nn(Z) < m_result.ground_level) {
+                        m_iheadless.emplace_back(fidx);
+                    } else {
+                        // mark the point for needing a head.
+                        m_iheads.emplace_back(fidx);
+                    }
+                } else if (polar >= 3 * PI / 4) {
+                    // Headless supports do not tilt like the headed ones
+                    // so the normal should point almost to the ground.
                     m_iheadless.emplace_back(fidx);
                 }
             }
@@ -1594,16 +1726,22 @@ public:
         // from each other in the XY plane to not cross their pillar bases
         // These clusters of support points will join in one pillar,
         // possibly in their centroid support point.
+        
         auto pointfn = [this](unsigned i) {
             return m_result.head(i).junction_point();
         };
-        auto predicate = [this](const SpatElement& e1, const SpatElement& e2) {
+
+        auto predicate = [this](const SpatElement &e1,
+                                const SpatElement &e2) {
             double d2d = distance(to_2d(e1.first), to_2d(e2.first));
             double d3d = distance(e1.first, e2.first);
-            return d2d < 2 * m_cfg.base_radius_mm &&
-                   d3d < m_cfg.max_bridge_length_mm;
+            return d2d < 2 * m_cfg.base_radius_mm
+                   && d3d < m_cfg.max_bridge_length_mm;
         };
-        m_pillar_clusters = cluster(ground_head_indices, pointfn, predicate,
+
+        m_pillar_clusters = cluster(ground_head_indices,
+                                    pointfn,
+                                    predicate,
                                     m_cfg.max_bridges_on_pillar);
     }
 
@@ -1615,7 +1753,7 @@ public:
     void routing_to_ground()
     {
         const double pradius = m_cfg.head_back_radius_mm;
-        const double gndlvl = m_result.ground_level;
+        // const double gndlvl = m_result.ground_level;
 
         ClusterEl cl_centroids;
         cl_centroids.reserve(m_pillar_clusters.size());
@@ -1648,13 +1786,8 @@ public:
 
             Head& h = m_result.head(hid);
             h.transform();
-            Vec3d p = h.junction_point(); p(Z) = gndlvl;
-            auto& plr = m_result.add_pillar(hid, p, h.r_back_mm)
-                                .add_base(m_cfg.base_height_mm,
-                                          m_cfg.base_radius_mm);
 
-            // Save the pillar endpoint and the pillar id in the spatial index
-            m_pillar_index.insert(plr.endpoint(), unsigned(plr.id));
+            create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id);
         }
 
         // now we will go through the clusters ones again and connect the
@@ -1681,15 +1814,12 @@ public:
                    !search_pillar_and_connect(sidehead))
                 {
                     Vec3d pstart = sidehead.junction_point();
-                    Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl};
+                    //Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl};
                     // Could not find a pillar, create one
-                    auto& pillar = m_result.add_pillar(unsigned(sidehead.id),
-                                                       pend, pradius)
-                                           .add_base(m_cfg.base_height_mm,
-                                                     m_cfg.base_radius_mm);
-
-                    // connects to ground, eligible for bridging
-                    m_pillar_index.insert(pend, unsigned(pillar.id));
+                    create_ground_pillar(pstart,
+                                         sidehead.dir,
+                                         pradius,
+                                         sidehead.id);
                 }
             }
         }
@@ -1718,12 +1848,7 @@ public:
             m_result.add_bridge(hjp, endp, head.r_back_mm);
             m_result.add_junction(endp, head.r_back_mm);
 
-            auto groundp = endp;
-            groundp(Z) = m_result.ground_level;
-            auto& newpillar = m_result.add_pillar(endp, groundp, head.r_back_mm)
-                                      .add_base(m_cfg.base_height_mm,
-                                                m_cfg.base_radius_mm);
-            m_pillar_index.insert(groundp, unsigned(newpillar.id));
+            this->create_ground_pillar(endp, dir, head.r_back_mm);
         };
 
         std::vector<unsigned> modelpillars;
@@ -1883,6 +2008,28 @@ public:
             m_pillar_index.insert(pillar.endpoint(), pillid);
         }
     }
+    
+    // Helper function for interconnect_pillars where pairs of already connected
+    // pillars should be checked for not to be processed again. This can be done
+    // in O(log) or even constant time with a set or an unordered set of hash
+    // values uniquely representing a pair of integers. The order of numbers
+    // within the pair should not matter, it has the same unique hash.
+    template<class I> static I pairhash(I a, I b)
+    {
+        using std::ceil; using std::log2; using std::max; using std::min;
+        
+        static_assert(std::is_integral<I>::value,
+                      "This function works only for integral types.");
+
+        I g = min(a, b), l = max(a, b);
+        
+        auto bits_g = g ? int(ceil(log2(g))) : 0;
+
+        // Assume the hash will fit into the output variable
+        assert((l ? (ceil(log2(l))) : 0) + bits_g < int(sizeof(I) * CHAR_BIT));
+        
+        return (l << bits_g) + g;
+    }
 
     void interconnect_pillars() {
         // Now comes the algorithm that connects pillars with each other.
@@ -1900,17 +2047,23 @@ public:
         double min_height_ratio = 0.5;
 
         std::set<unsigned long> pairs;
-
+        
+        // A function to connect one pillar with its neighbors. THe number of
+        // neighbors is given in the configuration. This function if called
+        // for every pillar in the pillar index. A pair of pillar will not
+        // be connected multiple times this is ensured by the 'pairs' set which
+        // remembers the processed pillar pairs
         auto cascadefn =
                 [this, d, &pairs, min_height_ratio, H1] (const SpatElement& el)
         {
-            Vec3d qp = el.first;
-
-            const Pillar& pillar = m_result.pillar(el.second);
+            Vec3d qp = el.first;    // endpoint of the pillar
 
+            const Pillar& pillar = m_result.pillar(el.second); // actual pillar
+            
+            // Get the max number of neighbors a pillar should connect to
             unsigned neighbors = m_cfg.pillar_cascade_neighbors;
 
-            // connections are enough for one pillar
+            // connections are already enough for the pillar
             if(pillar.links >= neighbors) return;
 
             // Query all remaining points within reach
@@ -1924,21 +2077,21 @@ public:
                 return distance(e1.first, qp) < distance(e2.first, qp);
             });
 
-            for(auto& re : qres) {
+            for(auto& re : qres) { // process the queried neighbors
 
-                if(re.second == el.second) continue;
+                if(re.second == el.second) continue; // Skip self
 
                 auto a = el.second, b = re.second;
 
-                // I hope that the area of a square is never equal to its
-                // circumference
-                auto hashval = 2 * (a + b) + a * b;
-
+                // Get unique hash for the given pair (order doesn't matter)
+                auto hashval = pairhash(a, b);
+                
+                // Search for the pair amongst the remembered pairs
                 if(pairs.find(hashval) != pairs.end()) continue;
 
                 const Pillar& neighborpillar = m_result.pillars()[re.second];
 
-                // this neighbor is occupied
+                // this neighbor is occupied, skip
                 if(neighborpillar.links >= neighbors) continue;
 
                 if(interconnect(pillar, neighborpillar)) {
@@ -1960,47 +2113,75 @@ public:
                 if(pillar.links >= neighbors) break;
             }
         };
-
+        
+        // Run the cascade for the pillars in the index
         m_pillar_index.foreach(cascadefn);
-
+       
+        // We would be done here if we could allow some pillars to not be
+        // connected with any neighbors. But this might leave the support tree
+        // unprintable.
+        //
+        // The current solution is to insert additional pillars next to these
+        // lonely pillars. One or even two additional pillar might get inserted
+        // depending on the length of the lonely pillar.
+        
         size_t pillarcount = m_result.pillars().size();
-
+        
+        // Again, go through all pillars, this time in the whole support tree
+        // not just the index.
         for(size_t pid = 0; pid < pillarcount; pid++) {
             auto pillar = [this, pid]() { return m_result.pillar(pid); };
-
+           
+            // Decide how many additional pillars will be needed:
+            
             unsigned needpillars = 0;
-            if(pillar().bridges > m_cfg.max_bridges_on_pillar) needpillars = 3;
-            else if(pillar().links < 2 && pillar().height > H2) {
+            if (pillar().bridges > m_cfg.max_bridges_on_pillar)
+                needpillars = 3;
+            else if (pillar().links < 2 && pillar().height > H2) {
                 // Not enough neighbors to support this pillar
                 needpillars = 2 - pillar().links;
-            }
-            else if(pillar().links < 1 && pillar().height > H1) {
+            } else if (pillar().links < 1 && pillar().height > H1) {
                 // No neighbors could be found and the pillar is too long.
                 needpillars = 1;
             }
 
-            // Search for new pillar locations
-            bool found = false;
-            double alpha = 0; // goes to 2Pi
-            double r = 2 * m_cfg.base_radius_mm;
-            Vec3d pillarsp = pillar().startpoint();
+            // Search for new pillar locations:
+
+            bool   found    = false;
+            double alpha    = 0; // goes to 2Pi
+            double r        = 2 * m_cfg.base_radius_mm;
+            Vec3d  pillarsp = pillar().startpoint();
+
+            // temp value for starting point detection
             Vec3d sp(pillarsp(X), pillarsp(Y), pillarsp(Z) - r);
-            std::vector<bool> tv(needpillars, false);
-            std::vector<Vec3d> spts(needpillars);
 
+            // A vector of bool for placement feasbility
+            std::vector<bool>  canplace(needpillars, false);
+            std::vector<Vec3d> spts(needpillars); // vector of starting points
+
+            double gnd      = m_result.ground_level;
+            double min_dist = SupportConfig::pillar_base_safety_distance_mm +
+                              m_cfg.base_radius_mm + EPSILON;
+            
             while(!found && alpha < 2*PI) {
-
-                for(unsigned n = 0; n < needpillars; n++) {
-                    double a = alpha + n * PI/3;
-                    Vec3d s = sp;
+                for (unsigned n = 0; n < needpillars; n++) {
+                    double a = alpha + n * PI / 3;
+                    Vec3d  s = sp;
                     s(X) += std::cos(a) * r;
                     s(Y) += std::sin(a) * r;
                     spts[n] = s;
+                    
+                    // Check the path vertically down                    
                     auto hr = bridge_mesh_intersect(s, {0, 0, -1}, pillar().r);
-                    tv[n] = std::isinf(hr.distance());
+                    
+                    // If the path is clear, check for pillar base collisions
+                    canplace[n] = std::isinf(hr.distance())
+                                  && m_mesh.squared_distance({s(X), s(Y), gnd})
+                                         > min_dist;
                 }
 
-                found = std::all_of(tv.begin(), tv.end(), [](bool v){return v;});
+                found = std::all_of(canplace.begin(), canplace.end(),
+                                    [](bool v) { return v; });
 
                 // 20 angles will be tried...
                 alpha += 0.1 * PI;
@@ -2010,7 +2191,7 @@ public:
             newpills.reserve(needpillars);
 
             if(found) for(unsigned n = 0; n < needpillars; n++) {
-                Vec3d s = spts[n]; double gnd = m_result.ground_level;
+                Vec3d s = spts[n]; 
                 Pillar p(s, Vec3d(s(X), s(Y), gnd), pillar().r);
                 p.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm);
 
@@ -2075,9 +2256,12 @@ public:
             // This is only for checking
             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;
 
-            if(std::isinf(idist) || std::isnan(idist) || idist < 2*R ||
-               std::isinf(dist)  || std::isnan(dist)   || dist < 2*R) {
+            if(std::isnan(idist) || idist < 2*R ||
+               std::isnan(dist)  || dist  < 2*R)
+            {
                 BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless"
                                            << " support stick at: "
                                            << sj.transpose();
@@ -2214,7 +2398,9 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
     return pc == ABORT;
 }
 
-SLASupportTree::SLASupportTree(): m_impl(new Impl()) {}
+SLASupportTree::SLASupportTree(double gnd_lvl): m_impl(new Impl()) {
+    m_impl->ground_level = gnd_lvl;
+}
 
 const TriangleMesh &SLASupportTree::merged_mesh() const
 {
@@ -2226,7 +2412,7 @@ void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const {
     outmesh.merge(get_pad());
 }
 
-SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
+std::vector<ExPolygons> SLASupportTree::slice(float layerh, float init_layerh) const
 {
     if(init_layerh < 0) init_layerh = layerh;
     auto& stree = get();
@@ -2247,34 +2433,29 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
     fullmesh.merge(get_pad());
     fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this
     TriangleMeshSlicer slicer(&fullmesh);
-    SlicedSupports ret;
+    std::vector<ExPolygons> ret;
     slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn);
 
     return ret;
 }
 
-SlicedSupports SLASupportTree::slice(const std::vector<float> &heights,
+std::vector<ExPolygons> SLASupportTree::slice(const std::vector<float> &heights,
                                      float cr) const
 {
     TriangleMesh fullmesh = m_impl->merged_mesh();
     fullmesh.merge(get_pad());
     fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this
     TriangleMeshSlicer slicer(&fullmesh);
-    SlicedSupports ret;
+    std::vector<ExPolygons> ret;
     slicer.slice(heights, cr, &ret, get().ctl().cancelfn);
 
     return ret;
 }
 
-const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate,
+const TriangleMesh &SLASupportTree::add_pad(const ExPolygons& modelbase,
                                             const PoolConfig& pcfg) const
 {
-//    PoolConfig pcfg;
-//    pcfg.min_wall_thickness_mm = min_wall_thickness_mm;
-//    pcfg.min_wall_height_mm    = min_wall_height_mm;
-//    pcfg.max_merge_distance_mm = max_merge_distance_mm;
-//    pcfg.edge_radius_mm        = edge_radius_mm;
-    return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh;
+    return m_impl->create_pad(merged_mesh(), modelbase, pcfg).tmesh;
 }
 
 const TriangleMesh &SLASupportTree::get_pad() const
diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp
index 66677e4d7..1602316e8 100644
--- a/src/libslic3r/SLA/SLASupportTree.hpp
+++ b/src/libslic3r/SLA/SLASupportTree.hpp
@@ -24,10 +24,11 @@ class TriangleMesh;
 class Model;
 class ModelInstance;
 class ModelObject;
+class Polygon;
 class ExPolygon;
 
-using SliceLayer = std::vector<ExPolygon>;
-using SlicedSupports = std::vector<SliceLayer>;
+using Polygons = std::vector<Polygon>;
+using ExPolygons = std::vector<ExPolygon>;
 
 namespace sla {
 
@@ -90,6 +91,10 @@ struct SupportConfig {
 
     // The shortest distance of any support structure from the model surface
     static const double safety_distance_mm;
+    
+    // The shortest distance between a pillar base perimeter from the model
+    // body. This is only useful when elevation is set to zero.
+    static const double pillar_base_safety_distance_mm;
 
     static const double max_solo_pillar_height_mm;
     static const double max_dual_pillar_height_mm;
@@ -160,7 +165,7 @@ class SLASupportTree {
 
 public:
 
-    SLASupportTree();
+    SLASupportTree(double ground_level = 0.0);
 
     SLASupportTree(const std::vector<SupportPoint>& pts,
                    const EigenMesh3D& em,
@@ -179,12 +184,16 @@ public:
     void merged_mesh_with_pad(TriangleMesh&) const;
 
     /// Get the sliced 2d layers of the support geometry.
-    SlicedSupports slice(float layerh, float init_layerh = -1.0) const;
+    std::vector<ExPolygons> slice(float layerh, float init_layerh = -1.0) const;
 
-    SlicedSupports slice(const std::vector<float>&, float closing_radius) const;
+    std::vector<ExPolygons> slice(const std::vector<float>&, float closing_radius) const;
 
     /// Adding the "pad" (base pool) under the supports
-    const TriangleMesh& add_pad(const SliceLayer& baseplate,
+    /// modelbase will be used according to the embed_object flag in PoolConfig.
+    /// If set, the plate will interpreted as the model's intrinsic pad. 
+    /// Otherwise, the modelbase will be unified with the base plate calculated
+    /// from the supports.
+    const TriangleMesh& add_pad(const ExPolygons& modelbase,
                                 const PoolConfig& pcfg) const;
 
     /// Get the pad geometry
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index f6a1c429e..f4599a266 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -32,8 +32,8 @@ class SLAPrintObject::SupportData {
 public:
     sla::EigenMesh3D emesh;              // index-triangle representation
     std::vector<sla::SupportPoint> support_points;     // all the support points (manual/auto)
-    SupportTreePtr   support_tree_ptr;   // the supports
-    SlicedSupports   support_slices;     // sliced supports
+    SupportTreePtr                 support_tree_ptr;   // the supports
+    std::vector<ExPolygons>        support_slices;     // sliced supports
 
     inline SupportData(const TriangleMesh& trmesh): emesh(trmesh) {}
 };
@@ -471,7 +471,7 @@ void SLAPrint::set_task(const TaskParams &params)
 
     int n_object_steps = int(params.to_object_step) + 1;
     if (n_object_steps == 0)
-        n_object_steps = (int)slaposCount;
+        n_object_steps = int(slaposCount);
 
     if (params.single_model_object.valid()) {
         // Find the print object to be processed with priority.
@@ -486,7 +486,7 @@ void SLAPrint::set_task(const TaskParams &params)
         // Find out whether the priority print object is being currently processed.
         bool running = false;
         for (int istep = 0; istep < n_object_steps; ++ istep) {
-            if (! print_object->m_stepmask[istep])
+            if (! print_object->m_stepmask[size_t(istep)])
                 // Step was skipped, cancel.
                 break;
             if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) {
@@ -502,7 +502,7 @@ void SLAPrint::set_task(const TaskParams &params)
         if (params.single_model_instance_only) {
             // Suppress all the steps of other instances.
             for (SLAPrintObject *po : m_objects)
-                for (int istep = 0; istep < (int)slaposCount; ++ istep)
+                for (size_t istep = 0; istep < slaposCount; ++ istep)
                     po->m_stepmask[istep] = false;
         } else if (! running) {
             // Swap the print objects, so that the selected print_object is first in the row.
@@ -512,15 +512,15 @@ void SLAPrint::set_task(const TaskParams &params)
         }
         // and set the steps for the current object.
         for (int istep = 0; istep < n_object_steps; ++ istep)
-            print_object->m_stepmask[istep] = true;
-        for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep)
-            print_object->m_stepmask[istep] = false;
+            print_object->m_stepmask[size_t(istep)] = true;
+        for (int istep = n_object_steps; istep < int(slaposCount); ++ istep)
+            print_object->m_stepmask[size_t(istep)] = false;
     } else {
         // Slicing all objects.
         bool running = false;
         for (SLAPrintObject *print_object : m_objects)
             for (int istep = 0; istep < n_object_steps; ++ istep) {
-                if (! print_object->m_stepmask[istep]) {
+                if (! print_object->m_stepmask[size_t(istep)]) {
                     // Step may have been skipped. Restart.
                     goto loop_end;
                 }
@@ -536,8 +536,8 @@ void SLAPrint::set_task(const TaskParams &params)
             this->call_cancel_callback();
         for (SLAPrintObject *po : m_objects) {
             for (int istep = 0; istep < n_object_steps; ++ istep)
-                po->m_stepmask[istep] = true;
-            for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep)
+                po->m_stepmask[size_t(istep)] = true;
+            for (auto istep = size_t(n_object_steps); istep < slaposCount; ++ istep)
                 po->m_stepmask[istep] = false;
         }
     }
@@ -555,9 +555,9 @@ void SLAPrint::set_task(const TaskParams &params)
 void SLAPrint::finalize()
 {
     for (SLAPrintObject *po : m_objects)
-        for (int istep = 0; istep < (int)slaposCount; ++ istep)
+        for (size_t istep = 0; istep < slaposCount; ++ istep)
             po->m_stepmask[istep] = true;
-    for (int istep = 0; istep < (int)slapsCount; ++ istep)
+    for (size_t istep = 0; istep < slapsCount; ++ istep)
         m_stepmask[istep] = true;
 }
 
@@ -599,17 +599,29 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
     return scfg;
 }
 
+bool use_builtin_pad(const SLAPrintObjectConfig& c) { 
+    return c.support_object_elevation.getFloat() <= EPSILON && 
+           c.pad_enable.getBool();
+}
+
 sla::PoolConfig make_pool_config(const SLAPrintObjectConfig& c) {
     sla::PoolConfig pcfg;
 
     pcfg.min_wall_thickness_mm = c.pad_wall_thickness.getFloat();
-    pcfg.wall_slope = c.pad_wall_slope.getFloat();
-    pcfg.edge_radius_mm = c.pad_edge_radius.getFloat();
+    pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0;
+    
+    // We do not support radius for now
+    pcfg.edge_radius_mm = 0.0; //c.pad_edge_radius.getFloat();
+    
     pcfg.max_merge_distance_mm = c.pad_max_merge_distance.getFloat();
     pcfg.min_wall_height_mm = c.pad_wall_height.getFloat();
 
+    // set builtin pad implicitly ON
+    pcfg.embed_object = use_builtin_pad(c);
+    
     return pcfg;
 }
+
 }
 
 std::string SLAPrint::validate() const
@@ -632,8 +644,10 @@ std::string SLAPrint::validate() const
                 cfg.head_width_mm +
                 2 * cfg.head_back_radius_mm -
                 cfg.head_penetration_mm;
+        
+        double elv = cfg.object_elevation_mm;
 
-        if(supports_en && pinhead_width > cfg.object_elevation_mm)
+        if(supports_en && elv > EPSILON && elv < pinhead_width )
             return L("Elevation is too low for object.");
     }
 
@@ -818,23 +832,55 @@ void SLAPrint::process()
             BOOST_LOG_TRIVIAL(debug) << "Automatic support points: "
                                      << po.m_supportdata->support_points.size();
 
-            // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass the update status to GLGizmoSlaSupports
-            m_report_status(*this, -1, L("Generating support points"), SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
+            // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass
+            // the update status to GLGizmoSlaSupports
+            m_report_status(*this,
+                            -1,
+                            L("Generating support points"),
+                            SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
         }
         else {
-            // There are either some points on the front-end, or the user removed them on purpose. No calculation will be done.
+            // There are either some points on the front-end, or the user
+            // removed them on purpose. No calculation will be done.
             po.m_supportdata->support_points = po.transformed_support_points();
         }
+        
+        // If the builtin pad mode is engaged, we have to filter out all the
+        // points that are on the bottom of the object
+        if(use_builtin_pad(po.m_config)) {
+            double gnd   = po.m_supportdata->emesh.ground_level();
+            auto & pts   = po.m_supportdata->support_points;
+            
+            // get iterator to the reorganized vector end
+            auto endit = std::remove_if(
+                pts.begin(),
+                pts.end(),
+                [&po, gnd](const sla::SupportPoint &sp) {
+                    double diff = std::abs(gnd - double(sp.pos(Z)));
+                    return diff <= po.m_config.pad_wall_thickness.getFloat();
+                });
+            
+            // erase all elements after the new end
+            pts.erase(endit, pts.end());
+        }
     };
 
     // In this step we create the supports
     auto support_tree = [this, ostepd](SLAPrintObject& po)
     {
         if(!po.m_supportdata) return;
+        
+        sla::PoolConfig pcfg = make_pool_config(po.m_config);
+        
+        if(pcfg.embed_object)
+            po.m_supportdata->emesh.ground_level() += pcfg.min_wall_thickness_mm;
 
         if(!po.m_config.supports_enable.getBool()) {
+            
             // Generate empty support tree. It can still host a pad
-            po.m_supportdata->support_tree_ptr.reset(new SLASupportTree());
+            po.m_supportdata->support_tree_ptr.reset(
+                    new SLASupportTree(po.m_supportdata->emesh.ground_level()));
+            
             return;
         }
 
@@ -856,7 +902,7 @@ void SLAPrint::process()
 
         ctl.stopcondition = [this](){ return canceled(); };
         ctl.cancelfn = [this]() { throw_if_canceled(); };
-
+        
         po.m_supportdata->support_tree_ptr.reset(
                     new SLASupportTree(po.m_supportdata->support_points,
                                        po.m_supportdata->emesh, scfg, ctl));
@@ -894,27 +940,26 @@ void SLAPrint::process()
 
         if(po.m_config.pad_enable.getBool())
         {
-            double wt = po.m_config.pad_wall_thickness.getFloat();
-            double h =  po.m_config.pad_wall_height.getFloat();
-            double md = po.m_config.pad_max_merge_distance.getFloat();
-            // Radius is disabled for now...
-            double er = 0; // po.m_config.pad_edge_radius.getFloat();
-            double tilt = po.m_config.pad_wall_slope.getFloat()  * PI / 180.0;
-            double lh = po.m_config.layer_height.getFloat();
-            double elevation = po.m_config.support_object_elevation.getFloat();
-            if(!po.m_config.supports_enable.getBool()) elevation = 0;
-            sla::PoolConfig pcfg(wt, h, md, er, tilt);
+            // Get the distilled pad configuration from the config
+            sla::PoolConfig pcfg = make_pool_config(po.m_config);
 
-            ExPolygons bp;
-            double pad_h = sla::get_pad_fullheight(pcfg);
-            auto&& trmesh = po.transformed_mesh();
+            ExPolygons bp; // This will store the base plate of the pad.
+            double   pad_h             = sla::get_pad_fullheight(pcfg);
+            const TriangleMesh &trmesh = po.transformed_mesh();
 
             // This call can get pretty time consuming
             auto thrfn = [this](){ throw_if_canceled(); };
 
-            if(elevation < pad_h) {
-                // we have to count with the model geometry for the base plate
-                sla::base_plate(trmesh, bp, float(pad_h), float(lh), thrfn);
+            if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) {
+                // No support (thus no elevation) or zero elevation mode
+                // we sometimes call it "builtin pad" is enabled so we will
+                // get a sample from the bottom of the mesh and use it for pad
+                // creation.
+                sla::base_plate(trmesh,
+                                bp,
+                                float(pad_h),
+                                float(po.m_config.layer_height.getFloat()),
+                                thrfn);
             }
 
             pcfg.throw_on_cancel = thrfn;
@@ -1647,7 +1692,7 @@ double SLAPrintObject::get_elevation() const {
         // will be in the future, we provide the config to the get_pad_elevation
         // method and we will have the correct value
         sla::PoolConfig pcfg = make_pool_config(m_config);
-        ret += sla::get_pad_elevation(pcfg);
+        if(!pcfg.embed_object) ret += sla::get_pad_elevation(pcfg);
     }
 
     return ret;
@@ -1661,8 +1706,9 @@ double SLAPrintObject::get_current_elevation() const
 
     if(!has_supports && !has_pad)
         return 0;
-    else if(has_supports && !has_pad)
+    else if(has_supports && !has_pad) {
         return se ? m_config.support_object_elevation.getFloat() : 0;
+    }
 
     return get_elevation();
 }
@@ -1786,7 +1832,7 @@ std::vector<sla::SupportPoint> SLAPrintObject::transformed_support_points() cons
     ret.reserve(spts.size());
 
     for(sla::SupportPoint& sp : spts) {
-        Vec3d transformed_pos = trafo() * Vec3d(sp.pos(0), sp.pos(1), sp.pos(2));
+        Vec3d transformed_pos = trafo() * Vec3d(double(sp.pos(0)), double(sp.pos(1)), double(sp.pos(2)));
         ret.emplace_back(transformed_pos(0), transformed_pos(1), transformed_pos(2), sp.head_front_radius, sp.is_new_island);
     }
 

From b7e3ee0709c9ca3a6da49f4c933d1f079dba27a0 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 11 Jun 2019 15:35:09 +0200
Subject: [PATCH 02/14] Refactor, fix wall normals and gap detection.

---
 src/libslic3r/SLA/SLABasePool.cpp    | 66 +++++++++++++++-------------
 src/libslic3r/SLA/SLASupportTree.cpp | 12 +++--
 2 files changed, 43 insertions(+), 35 deletions(-)

diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp
index 9b3f80f1a..a882769f6 100644
--- a/src/libslic3r/SLA/SLABasePool.cpp
+++ b/src/libslic3r/SLA/SLABasePool.cpp
@@ -857,6 +857,7 @@ Contour3D create_base_pool(const Polygons &ground_layer,
         if(wingheight > 0) {
             // Generate the smoothed edge geometry
             wh = 0;
+            ob = middle_base;
             if(s_eradius) pool.merge(round_edges(middle_base,
                                    r,
                                    phi - 90, // from tangent lines
@@ -872,55 +873,58 @@ Contour3D create_base_pool(const Polygons &ground_layer,
         }
 
         if (cfg.embed_object) {
-            ExPolygons pp = diff_ex(to_polygons(bottom_poly),
-                                    to_polygons(obj_self_pad));
+            ExPolygons bttms = diff_ex(to_polygons(bottom_poly),
+                                       to_polygons(obj_self_pad));
+            
+            assert(!bttms.empty());
+            
+            std::sort(bttms.begin(), bttms.end(),
+                      [](const ExPolygon& e1, const ExPolygon& e2) {
+                          return e1.contour.area() > e2.contour.area();
+                      });
+            
+            if(wingheight > 0) inner_base.holes = bttms.front().holes;
+            else top_poly.holes = bttms.front().holes;
 
-            // Generate outer walls
-            auto fp = [](const Point &p, Point::coord_type z) {
-                return unscale(x(p), y(p), z);
-            };
-
-            auto straight_walls = [&pool, s_thickness, fp](const Polygon &cntr)
-            {
+            auto straight_walls =
+                [&pool](const Polygon &cntr, coord_t z_low, coord_t z_high) {
+                    
                 auto lines = cntr.lines();
-                bool cclk   = cntr.is_counter_clockwise();
                 
                 for (auto &l : lines) {
                     auto s = coord_t(pool.points.size());
-                    pool.points.emplace_back(fp(l.a, -s_thickness));
-                    pool.points.emplace_back(fp(l.b, -s_thickness));
-                    pool.points.emplace_back(fp(l.a, 0));
-                    pool.points.emplace_back(fp(l.b, 0));
+                    auto& pts = pool.points;
+                    pts.emplace_back(unscale(l.a.x(), l.a.y(), z_low));
+                    pts.emplace_back(unscale(l.b.x(), l.b.y(), z_low));
+                    pts.emplace_back(unscale(l.a.x(), l.a.y(), z_high));
+                    pts.emplace_back(unscale(l.b.x(), l.b.y(), z_high));
                     
-                    if(cclk) {
-                        pool.indices.emplace_back(s + 3, s + 1, s);
-                        pool.indices.emplace_back(s + 2, s + 3, s);
-                    } else {
-                        pool.indices.emplace_back(s, s + 1, s + 3);
-                        pool.indices.emplace_back(s, s + 3, s + 2);
-                    }
+                    pool.indices.emplace_back(s, s + 1, s + 3);
+                    pool.indices.emplace_back(s, s + 3, s + 2);
                 }
             };
-
-            for (ExPolygon &ep : pp) {
-                pool.merge(triangulate_expolygon_3d(ep));
+            
+            coord_t z_lo = -mm(fullheight), z_hi = -mm(wingheight);
+            for (ExPolygon &ep : bttms) {
                 pool.merge(triangulate_expolygon_3d(ep, -fullheight, true));
-
-                for (auto &h : ep.holes) straight_walls(h);
+                for (auto &h : ep.holes) straight_walls(h, z_lo, z_hi);
             }
             
-            // Skip the outer contour. TODO: make sure the first in the list
-            // IS the outer contour.
-            for (auto it = std::next(pp.begin()); it != pp.end(); ++it)
-                straight_walls(it->contour);
+            // Skip the outer contour, triangulate the holes
+            for (auto it = std::next(bttms.begin()); it != bttms.end(); ++it) {
+                pool.merge(triangulate_expolygon_3d(*it, -wingheight));
+                straight_walls(it->contour, z_lo, z_hi);
+            }
             
         } else {
             // Now we need to triangulate the top and bottom plates as well as
             // the cavity bottom plate which is the same as the bottom plate
             // but it is elevated by the thickness.
-            pool.merge(triangulate_expolygon_3d(top_poly));
+            
             pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true));
         }
+        
+        pool.merge(triangulate_expolygon_3d(top_poly));
 
         if(wingheight > 0)
             pool.merge(triangulate_expolygon_3d(inner_base, -wingheight));
diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index 43ffaed86..9705e8600 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -2164,7 +2164,10 @@ public:
                               m_cfg.base_radius_mm + EPSILON;
             
             while(!found && alpha < 2*PI) {
-                for (unsigned n = 0; n < needpillars; n++) {
+                for (unsigned n = 0;
+                     n < needpillars && (!n || canplace[n - 1]);
+                     n++)
+                {
                     double a = alpha + n * PI / 3;
                     Vec3d  s = sp;
                     s(X) += std::cos(a) * r;
@@ -2173,11 +2176,12 @@ public:
                     
                     // Check the path vertically down                    
                     auto hr = bridge_mesh_intersect(s, {0, 0, -1}, pillar().r);
+                    Vec3d gndsp{s(X), s(Y), gnd};
                     
                     // If the path is clear, check for pillar base collisions
-                    canplace[n] = std::isinf(hr.distance())
-                                  && m_mesh.squared_distance({s(X), s(Y), gnd})
-                                         > min_dist;
+                    canplace[n] = std::isinf(hr.distance()) &&
+                                  std::sqrt(m_mesh.squared_distance(gndsp)) >
+                                      min_dist;
                 }
 
                 found = std::all_of(canplace.begin(), canplace.end(),

From 6877c075dc14c24d8b036ade7cf540ce228dd061 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 11 Jun 2019 17:57:39 +0200
Subject: [PATCH 03/14] SPE-742: Parameter layer for zero elevation feature.

---
 src/libslic3r/PrintConfig.cpp        | 47 ++++++++++++++-
 src/libslic3r/PrintConfig.hpp        | 26 ++++++++-
 src/libslic3r/SLA/SLABasePool.hpp    | 15 +++--
 src/libslic3r/SLA/SLACommon.hpp      |  6 +-
 src/libslic3r/SLA/SLASupportTree.cpp | 14 ++---
 src/libslic3r/SLA/SLASupportTree.hpp | 11 ++--
 src/libslic3r/SLAPrint.cpp           | 31 +++++++---
 src/slic3r/GUI/Preset.cpp            |  4 ++
 src/slic3r/GUI/Tab.cpp               | 85 ++++++++++++++++++----------
 9 files changed, 179 insertions(+), 60 deletions(-)

diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 87ea26301..45ff20e78 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -2490,6 +2490,19 @@ void PrintConfigDef::init_sla_params()
     def->min = 0;
     def->mode = comAdvanced;
     def->set_default_value(new ConfigOptionFloat(1.0));
+    
+    def = this->add("support_base_safety_distance", coFloat);
+    def->label = L("Support base safety distance");
+    def->category = L("Supports");
+    def->tooltip  = L(
+        "The minimum distance of the pillar base from the model in mm. "
+        "Makes sense in zero elevation mode where a gap according "
+        "to this parameter is inserted between the model and the pad.");
+    def->sidetext = L("mm");
+    def->min = 0;
+    def->max = 10;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(0.5));
 
     def = this->add("support_critical_angle", coFloat);
     def->label = L("Critical angle");
@@ -2523,7 +2536,9 @@ void PrintConfigDef::init_sla_params()
     def = this->add("support_object_elevation", coFloat);
     def->label = L("Object elevation");
     def->category = L("Supports");
-    def->tooltip = L("How much the supports should lift up the supported object.");
+    def->tooltip = L("How much the supports should lift up the supported object. "
+                     "If this value is zero, the bottom of the model geometry "
+                     "will be considered as part of the pad.");
     def->sidetext = L("mm");
     def->min = 0;
     def->max = 150; // This is the max height of print on SL1
@@ -2609,6 +2624,36 @@ void PrintConfigDef::init_sla_params()
     def->max = 90;
     def->mode = comAdvanced;
     def->set_default_value(new ConfigOptionFloat(45.0));
+    
+    def = this->add("pad_object_connector_stride", coFloat);
+    def->label = L("Pad object connector stride");
+    def->category = L("Pad");
+    def->tooltip = L("Distance between two connector sticks between "
+                     "the object pad and the generated pad.");
+    def->sidetext = L("mm");
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(10));
+    
+    def = this->add("pad_object_connector_width", coFloat);
+    def->label = L("Pad object connector width");
+    def->category = L("Pad");
+    def->tooltip  = L("The width of the connectors sticks which connect the "
+                      "object pad and the generated pad.");
+    def->sidetext = L("mm");
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(0.3));
+    
+    def = this->add("pad_object_connector_penetration", coFloat);
+    def->label = L("Pad object connector penetration");
+    def->category = L("Pad");
+    def->tooltip  = L(
+        "How much should the tiny connectors penetrate into the model body.");
+    def->sidetext = L("mm");
+    def->min = 0;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(0.3));
 }
 
 void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 1da22b377..e632e6946 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -983,6 +983,9 @@ public:
 
     // The height of the pillar base cone in mm.
     ConfigOptionFloat support_base_height /*= 1.0*/;
+    
+    // The minimum distance of the pillar base from the model in mm.
+    ConfigOptionFloat support_base_safety_distance; /*= 1.0*/;
 
     // The default angle for connecting support sticks and junctions.
     ConfigOptionFloat support_critical_angle /*= 45*/;
@@ -996,7 +999,7 @@ public:
     // The elevation in Z direction upwards. This is the space between the pad
     // and the model object's bounding box bottom. Units in mm.
     ConfigOptionFloat support_object_elevation /*= 5.0*/;
-
+    
     /////// Following options influence automatic support points placement:
     ConfigOptionInt support_points_density_relative;
     ConfigOptionFloat support_points_minimal_distance;
@@ -1021,6 +1024,23 @@ public:
 
     // The slope of the pad wall...
     ConfigOptionFloat pad_wall_slope;
+    
+    // /////////////////////////////////////////////////////////////////////////
+    // Zero elevation mode parameters:
+    //    - The object pad will be derived from the the model geometry.
+    //    - There will be a gap between the object pad and the generated pad
+    //      according to the support_base_safety_distance parameter.
+    //    - The two pads will be connected with tiny connector sticks
+    // /////////////////////////////////////////////////////////////////////////
+    
+    // How far to place the connector sticks on the object pad perimeter
+    ConfigOptionFloat pad_object_connector_stride;
+    
+    // The width of the connectors sticks
+    ConfigOptionFloat pad_object_connector_width;
+    
+    // How much should the tiny connectors penetrate into the model body
+    ConfigOptionFloat pad_object_connector_penetration;
 
 protected:
     void initialize(StaticCacheBase &cache, const char *base_ptr)
@@ -1038,6 +1058,7 @@ protected:
         OPT_PTR(support_pillar_widening_factor);
         OPT_PTR(support_base_diameter);
         OPT_PTR(support_base_height);
+        OPT_PTR(support_base_safety_distance);
         OPT_PTR(support_critical_angle);
         OPT_PTR(support_max_bridge_length);
         OPT_PTR(support_max_pillar_link_distance);
@@ -1050,6 +1071,9 @@ protected:
         OPT_PTR(pad_max_merge_distance);
         OPT_PTR(pad_edge_radius);
         OPT_PTR(pad_wall_slope);
+        OPT_PTR(pad_object_connector_stride);
+        OPT_PTR(pad_object_connector_width);
+        OPT_PTR(pad_object_connector_penetration);
     }
 };
 
diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp
index 0ed26b6e7..129f7ccd4 100644
--- a/src/libslic3r/SLA/SLABasePool.hpp
+++ b/src/libslic3r/SLA/SLABasePool.hpp
@@ -42,7 +42,14 @@ struct PoolConfig {
     double max_merge_distance_mm = 50;
     double edge_radius_mm = 1;
     double wall_slope = std::atan(1.0);          // Universal constant for Pi/4
-    bool   embed_object = false;
+    struct EmbedObject {
+        double object_gap_mm = 0.5;
+        double stick_stride_mm = 10;
+        double stick_width_mm = 0.3;
+        double stick_penetration_mm = 0.1;
+        bool enabled = false;
+        operator bool() const { return enabled; }
+    } embed_object;
 
     ThrowOnCancel throw_on_cancel = [](){};
 
@@ -61,11 +68,7 @@ void create_base_pool(const Polygons& base_plate,
                       const ExPolygons& holes,
                       const PoolConfig& = PoolConfig());
 
-/// TODO: Currently the base plate of the pool will have half the height of the
-/// whole pool. So the carved out space has also half the height. This is not
-/// a particularly elegant solution, the thickness should be exactly
-/// min_wall_thickness and it should be corrected in the future. This method
-/// will return the correct value for further processing.
+/// Returns the elevation needed for compensating the pad.
 inline double get_pad_elevation(const PoolConfig& cfg) {
     return cfg.min_wall_thickness_mm;
 }
diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp
index 2b72aa92c..2666f2a9c 100644
--- a/src/libslic3r/SLA/SLACommon.hpp
+++ b/src/libslic3r/SLA/SLACommon.hpp
@@ -60,7 +60,7 @@ class EigenMesh3D {
 
     Eigen::MatrixXd m_V;
     Eigen::MatrixXi m_F;
-    double m_ground_level = 0;
+    double m_ground_level = 0, m_gnd_offset = 0;
 
     std::unique_ptr<AABBImpl> m_aabb;
 public:
@@ -71,8 +71,8 @@ public:
 
     ~EigenMesh3D();
 
-    inline double ground_level() const { return m_ground_level; }
-    inline double& ground_level() { return m_ground_level; }
+    inline double ground_level() const { return m_ground_level + m_gnd_offset; }
+    inline void ground_level_offset(double o) { m_gnd_offset = o; }
 
     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 9705e8600..b9f2cc14e 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -72,8 +72,6 @@ const double SupportConfig::normal_cutoff_angle = 150.0 * M_PI / 180.0;
 // The shortest distance of any support structure from the model surface
 const double SupportConfig::safety_distance_mm = 0.5;
 
-const double SupportConfig::pillar_base_safety_distance_mm = 0.5;
-
 const double SupportConfig::max_solo_pillar_height_mm = 15.0;
 const double SupportConfig::max_dual_pillar_height_mm = 35.0;
 const double   SupportConfig::optimizer_rel_score_diff = 1e-6;
@@ -590,10 +588,10 @@ struct Pad {
             for(auto& poly : modelbase_sticks)
                 sla::offset_with_breakstick_holes(
                     poly,
-                    SupportConfig::pillar_base_safety_distance_mm, // padding
-                    10,     // stride (mm)
-                    0.3,    // stick_width (mm)
-                    0.1);   // penetration (mm)
+                    pcfg.embed_object.object_gap_mm,   // padding
+                    pcfg.embed_object.stick_stride_mm,
+                    pcfg.embed_object.stick_width_mm,
+                    pcfg.embed_object.stick_penetration_mm);
 
             create_base_pool(basep, tmesh, modelbase_sticks, cfg);
         } else {
@@ -1407,7 +1405,7 @@ class SLASupportTree::Algorithm {
 
         double gndlvl       = m_result.ground_level;
         Vec3d  endp         = {jp(X), jp(Y), gndlvl};
-        double sd           = SupportConfig::pillar_base_safety_distance_mm;
+        double sd           = m_cfg.pillar_base_safety_distance_mm;
         int    pillar_id    = -1;
         double min_dist     = sd + m_cfg.base_radius_mm + EPSILON;
         double dist         = 0;
@@ -2160,7 +2158,7 @@ public:
             std::vector<Vec3d> spts(needpillars); // vector of starting points
 
             double gnd      = m_result.ground_level;
-            double min_dist = SupportConfig::pillar_base_safety_distance_mm +
+            double min_dist = m_cfg.pillar_base_safety_distance_mm +
                               m_cfg.base_radius_mm + EPSILON;
             
             while(!found && alpha < 2*PI) {
diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp
index 1602316e8..93627d10c 100644
--- a/src/libslic3r/SLA/SLASupportTree.hpp
+++ b/src/libslic3r/SLA/SLASupportTree.hpp
@@ -81,6 +81,10 @@ struct SupportConfig {
     // The elevation in Z direction upwards. This is the space between the pad
     // and the model object's bounding box bottom.
     double object_elevation_mm = 10;
+    
+    // The shortest distance between a pillar base perimeter from the model
+    // body. This is only useful when elevation is set to zero.
+    const double pillar_base_safety_distance_mm = 0.5;
 
     // /////////////////////////////////////////////////////////////////////////
     // Compile time configuration values (candidates for runtime)
@@ -91,10 +95,6 @@ struct SupportConfig {
 
     // The shortest distance of any support structure from the model surface
     static const double safety_distance_mm;
-    
-    // The shortest distance between a pillar base perimeter from the model
-    // body. This is only useful when elevation is set to zero.
-    static const double pillar_base_safety_distance_mm;
 
     static const double max_solo_pillar_height_mm;
     static const double max_dual_pillar_height_mm;
@@ -186,7 +186,8 @@ public:
     /// Get the sliced 2d layers of the support geometry.
     std::vector<ExPolygons> slice(float layerh, float init_layerh = -1.0) const;
 
-    std::vector<ExPolygons> slice(const std::vector<float>&, float closing_radius) const;
+    std::vector<ExPolygons> slice(const std::vector<float> &,
+                                  float closing_radius) const;
 
     /// Adding the "pad" (base pool) under the supports
     /// modelbase will be used according to the embed_object flag in PoolConfig.
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index f4599a266..14cf2b6ff 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -599,9 +599,20 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
     return scfg;
 }
 
-bool use_builtin_pad(const SLAPrintObjectConfig& c) { 
-    return c.support_object_elevation.getFloat() <= EPSILON && 
-           c.pad_enable.getBool();
+sla::PoolConfig::EmbedObject use_builtin_pad(const SLAPrintObjectConfig& c) {
+    sla::PoolConfig::EmbedObject ret;
+    
+    ret.enabled = c.support_object_elevation.getFloat() <= EPSILON && 
+                  c.pad_enable.getBool();
+    
+    if(ret.enabled) {
+        ret.object_gap_mm   = c.support_base_safety_distance.getFloat();
+        ret.stick_width_mm  = c.pad_object_connector_width.getFloat();
+        ret.stick_stride_mm = c.pad_object_connector_stride.getFloat();
+        ret.stick_width_mm  = c.pad_object_connector_penetration.getFloat();
+    }
+    
+    return ret;
 }
 
 sla::PoolConfig make_pool_config(const SLAPrintObjectConfig& c) {
@@ -871,9 +882,10 @@ void SLAPrint::process()
         if(!po.m_supportdata) return;
         
         sla::PoolConfig pcfg = make_pool_config(po.m_config);
-        
-        if(pcfg.embed_object)
-            po.m_supportdata->emesh.ground_level() += pcfg.min_wall_thickness_mm;
+
+        if (pcfg.embed_object)
+            po.m_supportdata->emesh.ground_level_offset(
+                pcfg.min_wall_thickness_mm);
 
         if(!po.m_config.supports_enable.getBool()) {
             
@@ -1635,13 +1647,18 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
             || opt_key == "support_critical_angle"
             || opt_key == "support_max_bridge_length"
             || opt_key == "support_max_pillar_link_distance"
+            || opt_key == "support_base_safety_distance"
             ) {
             steps.emplace_back(slaposSupportTree);
         } else if (
                opt_key == "pad_wall_height"
             || opt_key == "pad_max_merge_distance"
             || opt_key == "pad_wall_slope"
-            || opt_key == "pad_edge_radius") {
+            || opt_key == "pad_edge_radius"
+            || opt_key == "pad_object_connector_stride"
+            || opt_key == "pad_object_connector_width"
+            || opt_key == "pad_object_connector_penetration"
+            ) {
             steps.emplace_back(slaposBasePool);
         } else {
             // All keys should be covered.
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index 22ddf3449..86eee1902 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -461,6 +461,7 @@ const std::vector<std::string>& Preset::sla_print_options()
             "support_pillar_widening_factor",
             "support_base_diameter",
             "support_base_height",
+            "support_base_safety_distance",
             "support_critical_angle",
             "support_max_bridge_length",
             "support_max_pillar_link_distance",
@@ -474,6 +475,9 @@ const std::vector<std::string>& Preset::sla_print_options()
             "pad_max_merge_distance",
             "pad_edge_radius",
             "pad_wall_slope",
+            "pad_object_connector_stride",
+            "pad_object_connector_width",
+            "pad_object_connector_penetration",
             "output_filename_format", 
             "default_sla_print_profile",
             "compatible_printers",
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 6cd270e5b..8ca47f1b1 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -3481,6 +3481,7 @@ void TabSLAPrint::build()
     // optgroup->append_single_option_line("support_pillar_widening_factor");
     optgroup->append_single_option_line("support_base_diameter");
     optgroup->append_single_option_line("support_base_height");
+    optgroup->append_single_option_line("support_base_safety_distance");
     optgroup->append_single_option_line("support_object_elevation");
 
     optgroup = page->new_optgroup(_(L("Connection of the support sticks and junctions")));
@@ -3501,7 +3502,11 @@ void TabSLAPrint::build()
     // TODO: Disabling this parameter for the beta release
 //    optgroup->append_single_option_line("pad_edge_radius");
     optgroup->append_single_option_line("pad_wall_slope");
-
+    
+    optgroup->append_single_option_line("pad_object_connector_stride");
+    optgroup->append_single_option_line("pad_object_connector_width");
+    optgroup->append_single_option_line("pad_object_connector_penetration");
+    
 	page = add_options_page(_(L("Advanced")), "wrench");
 	optgroup = page->new_optgroup(_(L("Slicing")));
 	optgroup->append_single_option_line("slice_closing_radius");
@@ -3541,42 +3546,64 @@ void TabSLAPrint::reload_config()
 
 void TabSLAPrint::update()
 {
-    if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF)
+    if (m_preset_bundle->printers.get_selected_preset().printer_technology()
+        == ptFFF)
         return; // #ys_FIXME
 
-// #ys_FIXME
-     m_update_cnt++;
+    // #ys_FIXME
+    m_update_cnt++;
 
-     double head_penetration = m_config->opt_float("support_head_penetration");
-     double head_width = m_config->opt_float("support_head_width");
-     if(head_penetration > head_width) {
-         wxString msg_text = _(L("Head penetration should not be greater than the head width."));
-         auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK);
-         DynamicPrintConfig new_conf = *m_config;
-         if (dialog->ShowModal() == wxID_OK) {
-             new_conf.set_key_value("support_head_penetration", new ConfigOptionFloat(head_width));
-         }
+    double head_penetration = m_config->opt_float("support_head_penetration");
+    double head_width       = m_config->opt_float("support_head_width");
+    if (head_penetration > head_width) {
+        wxString msg_text = _(
+            L("Head penetration should not be greater than the head width."));
 
-         load_config(new_conf);
-     }
+        auto dialog = new wxMessageDialog(parent(),
+                                          msg_text,
+                                          _(L("Invalid Head penetration")),
+                                          wxICON_WARNING | wxOK);
 
-     double pinhead_d = m_config->opt_float("support_head_front_diameter");
-     double pillar_d     = m_config->opt_float("support_pillar_diameter");
-     if(pinhead_d > pillar_d) {
-         wxString msg_text = _(L("Pinhead diameter should be smaller than the pillar diameter."));
-         auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK);
-         DynamicPrintConfig new_conf = *m_config;
-         if (dialog->ShowModal() == wxID_OK) {
-             new_conf.set_key_value("support_head_front_diameter", new ConfigOptionFloat(pillar_d / 2.0));
-         }
+        DynamicPrintConfig new_conf = *m_config;
+        if (dialog->ShowModal() == wxID_OK) {
+            new_conf.set_key_value("support_head_penetration",
+                                   new ConfigOptionFloat(head_width));
+        }
 
-         load_config(new_conf);
-     }
+        load_config(new_conf);
+    }
 
-     m_update_cnt--;
+    double pinhead_d = m_config->opt_float("support_head_front_diameter");
+    double pillar_d  = m_config->opt_float("support_pillar_diameter");
+    if (pinhead_d > pillar_d) {
+        wxString msg_text = _(L(
+            "Pinhead diameter should be smaller than the pillar diameter."));
 
-     if (m_update_cnt == 0)
-    wxGetApp().mainframe->on_config_changed(m_config);
+        auto dialog = new wxMessageDialog(parent(),
+                                          msg_text,
+                                          _(L("Invalid pinhead diameter")),
+                                          wxICON_WARNING | wxOK);
+
+        DynamicPrintConfig new_conf = *m_config;
+        if (dialog->ShowModal() == wxID_OK) {
+            new_conf.set_key_value("support_head_front_diameter",
+                                   new ConfigOptionFloat(pillar_d / 2.0));
+        }
+
+        load_config(new_conf);
+    }
+    
+    // if(m_config->opt_float("support_object_elevation") < EPSILON &&
+    //    m_config->opt_bool("pad_enable")) {
+    //     // TODO: disable editding of:
+    //     // pad_object_connector_stride
+    //     // pad_object_connector_width
+    //     // pad_object_connector_penetration
+    // }
+
+    m_update_cnt--;
+
+    if (m_update_cnt == 0) wxGetApp().mainframe->on_config_changed(m_config);
 }
 
 } // GUI

From c80aae1bdb3d87a2a27c973232eda33f9444f153 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 11 Jun 2019 18:19:58 +0200
Subject: [PATCH 04/14] Fixes for the parameter layer

- Elevation value satisfied with no supports as well
- Removed debug svg writing
- Gap and sticks made optional in zero elevation pad.
---
 src/libslic3r/SLA/SLABasePool.cpp    | 24 ++++++++++++++----------
 src/libslic3r/SLA/SLASupportTree.hpp |  2 +-
 src/libslic3r/SLAPrint.cpp           | 12 ++++++------
 3 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp
index a882769f6..62a078cb7 100644
--- a/src/libslic3r/SLA/SLABasePool.cpp
+++ b/src/libslic3r/SLA/SLABasePool.cpp
@@ -9,7 +9,7 @@
 // For debugging:
 // #include <fstream>
 // #include <libnest2d/tools/benchmark.h>
-#include "SVG.hpp"
+// #include "SVG.hpp"
 
 namespace Slic3r { namespace sla {
 
@@ -390,11 +390,13 @@ void offset_with_breakstick_holes(ExPolygon& poly,
                                   double penetration)
 {
     // We do the basic offsetting first
-    const bool dont_round_edges = false;
-    offset(poly, coord_t(padding / SCALING_FACTOR), dont_round_edges);
+    static const bool dont_round_edges = false;
+    
+    if(padding > 0.0)
+        offset(poly, coord_t(padding / SCALING_FACTOR), dont_round_edges);
 
-    SVG svg("bridgestick_plate.svg");
-    svg.draw(poly);
+    // SVG svg("bridgestick_plate.svg");
+    // svg.draw(poly);
 
     auto transf = [stick_width, penetration, padding, stride](Points &pts) {
         // The connector stick will be a small rectangle with dimensions
@@ -453,12 +455,14 @@ void offset_with_breakstick_holes(ExPolygon& poly,
         out.shrink_to_fit();
         pts.swap(out);
     };
-
-    transf(poly.contour.points);
-    for (auto &h : poly.holes) transf(h.points);
     
-    svg.draw(poly);
-    svg.Close();
+    if(stride > 0.0 && stick_width > 0.0 && padding > 0.0) {
+        transf(poly.contour.points);
+        for (auto &h : poly.holes) transf(h.points);
+    }
+    
+    // svg.draw(poly);
+    // svg.Close();
 }
 
 /// Only a debug function to generate top and bottom plates from a 2D shape.
diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp
index 93627d10c..8602d8a46 100644
--- a/src/libslic3r/SLA/SLASupportTree.hpp
+++ b/src/libslic3r/SLA/SLASupportTree.hpp
@@ -84,7 +84,7 @@ struct SupportConfig {
     
     // The shortest distance between a pillar base perimeter from the model
     // body. This is only useful when elevation is set to zero.
-    const double pillar_base_safety_distance_mm = 0.5;
+    double pillar_base_safety_distance_mm = 0.5;
 
     // /////////////////////////////////////////////////////////////////////////
     // Compile time configuration values (candidates for runtime)
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 14cf2b6ff..78bd50220 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -595,7 +595,10 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
     scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat();
     scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat();
     scfg.base_height_mm = c.support_base_height.getFloat();
-
+    scfg.pillar_base_safety_distance_mm =
+        c.support_base_safety_distance.getFloat() < EPSILON ?
+        scfg.safety_distance_mm : c.support_base_safety_distance.getFloat();
+    
     return scfg;
 }
 
@@ -1699,10 +1702,8 @@ bool SLAPrintObject::invalidate_all_steps()
 }
 
 double SLAPrintObject::get_elevation() const {
-    bool se = m_config.supports_enable.getBool();
-    double ret = se? m_config.support_object_elevation.getFloat() : 0;
+    double ret = m_config.support_object_elevation.getFloat();
 
-    // if the pad is enabled, then half of the pad height is its base plate
     if(m_config.pad_enable.getBool()) {
         // Normally the elevation for the pad itself would be the thickness of
         // its walls but currently it is half of its thickness. Whatever it
@@ -1717,14 +1718,13 @@ double SLAPrintObject::get_elevation() const {
 
 double SLAPrintObject::get_current_elevation() const
 {
-    bool se = m_config.supports_enable.getBool();
     bool has_supports = is_step_done(slaposSupportTree);
     bool has_pad = is_step_done(slaposBasePool);
 
     if(!has_supports && !has_pad)
         return 0;
     else if(has_supports && !has_pad) {
-        return se ? m_config.support_object_elevation.getFloat() : 0;
+        return m_config.support_object_elevation.getFloat();
     }
 
     return get_elevation();

From 12396c3051a7abe26d33afe244bcabc938653e3e Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Wed, 12 Jun 2019 13:15:42 +0200
Subject: [PATCH 05/14] Fine tuning parameters and fixing pad wings when
 greater gaps are used.

---
 src/libslic3r/PrintConfig.cpp        | 15 +++++++++++++--
 src/libslic3r/PrintConfig.hpp        |  4 ++++
 src/libslic3r/SLA/SLABasePool.cpp    | 22 ++++++++--------------
 src/libslic3r/SLA/SLABasePool.hpp    | 10 +++++-----
 src/libslic3r/SLA/SLASupportTree.cpp | 18 +++++++++++++-----
 src/libslic3r/SLAPrint.cpp           | 26 +++++++++++++++++++-------
 src/slic3r/GUI/Preset.cpp            |  1 +
 src/slic3r/GUI/Tab.cpp               |  1 +
 8 files changed, 64 insertions(+), 33 deletions(-)

diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 45ff20e78..377d53bef 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -2502,7 +2502,7 @@ void PrintConfigDef::init_sla_params()
     def->min = 0;
     def->max = 10;
     def->mode = comExpert;
-    def->set_default_value(new ConfigOptionFloat(0.5));
+    def->set_default_value(new ConfigOptionFloat(1));
 
     def = this->add("support_critical_angle", coFloat);
     def->label = L("Critical angle");
@@ -2625,6 +2625,17 @@ void PrintConfigDef::init_sla_params()
     def->mode = comAdvanced;
     def->set_default_value(new ConfigOptionFloat(45.0));
     
+    def = this->add("pad_object_gap", coFloat);
+    def->label = L("Pad object gap");
+    def->category = L("Pad");
+    def->tooltip  = L("The gap between the object bottom and the generated "
+                      "pad in zero elevation mode.");
+    def->sidetext = L("mm");
+    def->min = 0;
+    def->max = 10;
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionFloat(1));
+    
     def = this->add("pad_object_connector_stride", coFloat);
     def->label = L("Pad object connector stride");
     def->category = L("Pad");
@@ -2643,7 +2654,7 @@ void PrintConfigDef::init_sla_params()
     def->sidetext = L("mm");
     def->min = 0;
     def->mode = comExpert;
-    def->set_default_value(new ConfigOptionFloat(0.3));
+    def->set_default_value(new ConfigOptionFloat(0.5));
     
     def = this->add("pad_object_connector_penetration", coFloat);
     def->label = L("Pad object connector penetration");
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index e632e6946..b5ddc4f13 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -1033,6 +1033,9 @@ public:
     //    - The two pads will be connected with tiny connector sticks
     // /////////////////////////////////////////////////////////////////////////
     
+    // This is the gap between the object bottom and the generated pad
+    ConfigOptionFloat pad_object_gap;
+    
     // How far to place the connector sticks on the object pad perimeter
     ConfigOptionFloat pad_object_connector_stride;
     
@@ -1071,6 +1074,7 @@ protected:
         OPT_PTR(pad_max_merge_distance);
         OPT_PTR(pad_edge_radius);
         OPT_PTR(pad_wall_slope);
+        OPT_PTR(pad_object_gap);
         OPT_PTR(pad_object_connector_stride);
         OPT_PTR(pad_object_connector_width);
         OPT_PTR(pad_object_connector_penetration);
diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp
index 62a078cb7..4a1259b5a 100644
--- a/src/libslic3r/SLA/SLABasePool.cpp
+++ b/src/libslic3r/SLA/SLABasePool.cpp
@@ -383,18 +383,12 @@ Polygons unify(const Polygons& shapes) {
 // inserted along the perimeter in every "stride" distance. The stick rectangles
 // will have a with about "stick_width". The input dimensions are in world 
 // measure, not the scaled clipper units.
-void offset_with_breakstick_holes(ExPolygon& poly,
-                                  double padding,
-                                  double stride,
-                                  double stick_width,
-                                  double penetration)
-{
-    // We do the basic offsetting first
-    static const bool dont_round_edges = false;
-    
-    if(padding > 0.0)
-        offset(poly, coord_t(padding / SCALING_FACTOR), dont_round_edges);
-
+void breakstick_holes(ExPolygon& poly,
+                      double padding,
+                      double stride,
+                      double stick_width,
+                      double penetration)
+{   
     // SVG svg("bridgestick_plate.svg");
     // svg.draw(poly);
 
@@ -428,8 +422,8 @@ void offset_with_breakstick_holes(ExPolygon& poly,
             out.emplace_back(a);
 
             // dodge the start point, do not make sticks on the joins
-            while (t < sright) t += sright;
-            double tend = nrm - sright;
+            while (t < sbottom) t += sbottom;
+            double tend = nrm - sbottom;
 
             while (t < tend) { // insert the stick on the polygon perimeter
 
diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp
index 129f7ccd4..8aa4f5f41 100644
--- a/src/libslic3r/SLA/SLABasePool.hpp
+++ b/src/libslic3r/SLA/SLABasePool.hpp
@@ -30,11 +30,11 @@ void base_plate(const TriangleMesh& mesh,       // input mesh
 // inserted along the perimeter in every "stride" distance. The stick rectangles
 // will have a with about "stick_width". The input dimensions are in world 
 // measure, not the scaled clipper units.
-void offset_with_breakstick_holes(ExPolygon& poly,
-                                  double padding,
-                                  double stride,
-                                  double stick_width,
-                                  double penetration = 0.0);
+void breakstick_holes(ExPolygon &poly,
+                      double     padding,
+                      double     stride,
+                      double     stick_width,
+                      double     penetration = 0.0);
 
 struct PoolConfig {
     double min_wall_thickness_mm = 2;
diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index b9f2cc14e..ba14b1811 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -578,23 +578,31 @@ struct Pad {
                    float(cfg.min_wall_height_mm + cfg.min_wall_thickness_mm),
                    0.1f, pcfg.throw_on_cancel);
         
-        // We don't need the holes for the base plate from the supports
         for (const ExPolygon &bp : platetmp)  basep.emplace_back(bp.contour);
-        for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour);
+        
         
         if(pcfg.embed_object) {
-            
             auto modelbase_sticks = modelbase;
-            for(auto& poly : modelbase_sticks)
-                sla::offset_with_breakstick_holes(
+            
+            if (pcfg.embed_object.object_gap_mm > 0.0)
+                modelbase_sticks
+                    = offset_ex(modelbase_sticks,
+                                coord_t(pcfg.embed_object.object_gap_mm
+                                        / SCALING_FACTOR));
+            
+            for(auto& poly : modelbase_sticks) {
+                basep.emplace_back(poly);
+                sla::breakstick_holes(
                     poly,
                     pcfg.embed_object.object_gap_mm,   // padding
                     pcfg.embed_object.stick_stride_mm,
                     pcfg.embed_object.stick_width_mm,
                     pcfg.embed_object.stick_penetration_mm);
+            }
 
             create_base_pool(basep, tmesh, modelbase_sticks, cfg);
         } else {
+            for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour);
             create_base_pool(basep, tmesh, {}, cfg);
         }
 
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 78bd50220..bff4c9587 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -602,17 +602,18 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
     return scfg;
 }
 
-sla::PoolConfig::EmbedObject use_builtin_pad(const SLAPrintObjectConfig& c) {
+sla::PoolConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) {
     sla::PoolConfig::EmbedObject ret;
     
     ret.enabled = c.support_object_elevation.getFloat() <= EPSILON && 
                   c.pad_enable.getBool();
     
     if(ret.enabled) {
-        ret.object_gap_mm   = c.support_base_safety_distance.getFloat();
-        ret.stick_width_mm  = c.pad_object_connector_width.getFloat();
-        ret.stick_stride_mm = c.pad_object_connector_stride.getFloat();
-        ret.stick_width_mm  = c.pad_object_connector_penetration.getFloat();
+        ret.object_gap_mm        = c.pad_object_gap.getFloat();
+        ret.stick_width_mm       = c.pad_object_connector_width.getFloat();
+        ret.stick_stride_mm      = c.pad_object_connector_stride.getFloat();
+        ret.stick_penetration_mm = c.pad_object_connector_penetration
+                                    .getFloat();
     }
     
     return ret;
@@ -631,7 +632,7 @@ sla::PoolConfig make_pool_config(const SLAPrintObjectConfig& c) {
     pcfg.min_wall_height_mm = c.pad_wall_height.getFloat();
 
     // set builtin pad implicitly ON
-    pcfg.embed_object = use_builtin_pad(c);
+    pcfg.embed_object = builtin_pad_cfg(c);
     
     return pcfg;
 }
@@ -663,6 +664,16 @@ std::string SLAPrint::validate() const
 
         if(supports_en && elv > EPSILON && elv < pinhead_width )
             return L("Elevation is too low for object.");
+        
+        sla::PoolConfig::EmbedObject builtinpad = builtin_pad_cfg(po->config());
+        if(supports_en && builtinpad.enabled &&
+           cfg.pillar_base_safety_distance_mm < builtinpad.object_gap_mm) {
+            return L(
+                "The endings of the support pillars will be deployed on the "
+                "gap between the object and the pad. 'Support base safety "
+                "distance' has to be greater than the 'Pad object gap' "
+                "parameter to avoid this.");
+        }
     }
 
     return "";
@@ -861,7 +872,7 @@ void SLAPrint::process()
         
         // If the builtin pad mode is engaged, we have to filter out all the
         // points that are on the bottom of the object
-        if(use_builtin_pad(po.m_config)) {
+        if(builtin_pad_cfg(po.m_config)) {
             double gnd   = po.m_supportdata->emesh.ground_level();
             auto & pts   = po.m_supportdata->support_points;
             
@@ -1658,6 +1669,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
             || opt_key == "pad_max_merge_distance"
             || opt_key == "pad_wall_slope"
             || opt_key == "pad_edge_radius"
+            || opt_key == "pad_object_gap"
             || opt_key == "pad_object_connector_stride"
             || opt_key == "pad_object_connector_width"
             || opt_key == "pad_object_connector_penetration"
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index 86eee1902..4b4597033 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -475,6 +475,7 @@ const std::vector<std::string>& Preset::sla_print_options()
             "pad_max_merge_distance",
             "pad_edge_radius",
             "pad_wall_slope",
+            "pad_object_gap",
             "pad_object_connector_stride",
             "pad_object_connector_width",
             "pad_object_connector_penetration",
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 8ca47f1b1..032bf95df 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -3503,6 +3503,7 @@ void TabSLAPrint::build()
 //    optgroup->append_single_option_line("pad_edge_radius");
     optgroup->append_single_option_line("pad_wall_slope");
     
+    optgroup->append_single_option_line("pad_object_gap");
     optgroup->append_single_option_line("pad_object_connector_stride");
     optgroup->append_single_option_line("pad_object_connector_width");
     optgroup->append_single_option_line("pad_object_connector_penetration");

From 10897524df042dae63939cec8251f93940de7916 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Wed, 12 Jun 2019 15:29:24 +0200
Subject: [PATCH 06/14] Fixes for gap detection and case with no pad, but zero
 elevation.

---
 src/libslic3r/SLA/SLASupportTree.cpp | 21 ++++++++++++++++-----
 src/libslic3r/SLAPrint.cpp           | 21 +++++++++++----------
 2 files changed, 27 insertions(+), 15 deletions(-)

diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index ba14b1811..c2540ba28 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -1410,6 +1410,7 @@ class SLASupportTree::Algorithm {
     {
         // People were killed for this number (seriously)
         static const double SQR2 = std::sqrt(2.0);
+        static const Vec3d  DOWN = {0.0, 0.0, -1.0};
 
         double gndlvl       = m_result.ground_level;
         Vec3d  endp         = {jp(X), jp(Y), gndlvl};
@@ -1451,20 +1452,30 @@ class SLASupportTree::Algorithm {
                 initvals(mv), bound(0.0, 2 * min_dist));
 
             mv           = std::get<0>(result.optimum);
-            endp         = jp + std::sqrt(2) * mv * dir;
+            endp         = jp + SQR2 * mv * dir;
             Vec3d pgnd   = {endp(X), endp(Y), gndlvl};
             can_add_base = result.score > min_dist;
+            
+            auto abort_in_shame =
+                [&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};
+            };
 
             // We have to check if the bridge is feasible.
-            if (bridge_mesh_intersect(jp, dir, radius) < (endp - jp).norm()) {
-                normal_mode = true;
-                endp        = {jp(X), jp(Y), gndlvl};
-            }
+            if (bridge_mesh_intersect(jp, dir, radius) < (endp - jp).norm())
+                abort_in_shame();
             else {
                 // If the new endpoint is below ground, do not make a pillar
                 if (endp(Z) < gndlvl)
                     endp = endp - SQR2 * (gndlvl - endp(Z)) * dir; // back off
                 else {
+                    
+                    if (!std::isinf(bridge_mesh_intersect(endp, DOWN, radius)))
+                        abort_in_shame();
+
                     Pillar &plr = m_result.add_pillar(endp, pgnd, radius);
 
                     if (can_add_base)
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index bff4c9587..99e2915ea 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -439,12 +439,10 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
         update_apply_status(this->invalidate_all_steps());
         m_objects = print_objects_new;
         // Delete the PrintObjects marked as Unknown or Deleted.
-        bool deleted_objects = false;
         for (auto &pos : print_object_status)
             if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
                 update_apply_status(pos.print_object->invalidate_all_steps());
                 delete pos.print_object;
-                deleted_objects = true;
             }
         if (new_objects)
             update_apply_status(false);
@@ -870,19 +868,22 @@ void SLAPrint::process()
             po.m_supportdata->support_points = po.transformed_support_points();
         }
         
-        // If the builtin pad mode is engaged, we have to filter out all the
+        // If the zero elevation mode is engaged, we have to filter out all the
         // points that are on the bottom of the object
-        if(builtin_pad_cfg(po.m_config)) {
-            double gnd   = po.m_supportdata->emesh.ground_level();
-            auto & pts   = po.m_supportdata->support_points;
-            
+        if (po.config().support_object_elevation.getFloat() <= EPSILON) {
+            double gnd       = po.m_supportdata->emesh.ground_level();
+            auto & pts       = po.m_supportdata->support_points;
+            double tolerance = po.config().pad_enable.getBool()
+                                   ? po.m_config.pad_wall_thickness.getFloat()
+                                   : po.m_config.support_base_height.getFloat();
+
             // get iterator to the reorganized vector end
             auto endit = std::remove_if(
                 pts.begin(),
                 pts.end(),
-                [&po, gnd](const sla::SupportPoint &sp) {
+                [tolerance, gnd](const sla::SupportPoint &sp) {
                     double diff = std::abs(gnd - double(sp.pos(Z)));
-                    return diff <= po.m_config.pad_wall_thickness.getFloat();
+                    return diff <= tolerance;
                 });
             
             // erase all elements after the new end
@@ -1352,7 +1353,7 @@ void SLAPrint::process()
     };
 
     // Rasterizing the model objects, and their supports
-    auto rasterize = [this, max_objstatus]() {
+    auto rasterize = [this]() {
         if(canceled()) return;
 
         // collect all the keys

From 4ffe3278bee9bd7441fd3a5410376cd0586947db Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Wed, 12 Jun 2019 17:09:40 +0200
Subject: [PATCH 07/14] Hotfix for pad shape deduction.

---
 src/libslic3r/SLA/SLASupportTree.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index c2540ba28..4910226cc 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -580,9 +580,8 @@ struct Pad {
         
         for (const ExPolygon &bp : platetmp)  basep.emplace_back(bp.contour);
         
-        
         if(pcfg.embed_object) {
-            auto modelbase_sticks = modelbase;
+            ExPolygons modelbase_sticks = modelbase;
             
             if (pcfg.embed_object.object_gap_mm > 0.0)
                 modelbase_sticks
@@ -591,7 +590,7 @@ struct Pad {
                                         / SCALING_FACTOR));
             
             for(auto& poly : modelbase_sticks) {
-                basep.emplace_back(poly);
+                basep.emplace_back(poly.contour);
                 sla::breakstick_holes(
                     poly,
                     pcfg.embed_object.object_gap_mm,   // padding

From d1ed3d40c1c1da26f6d0b0da9e2d54a928050796 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Wed, 12 Jun 2019 17:23:12 +0200
Subject: [PATCH 08/14] Fix build on windows.

This issue is annoying.
---
 src/libslic3r/SLAPrint.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 99e2915ea..3c7341656 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -1353,7 +1353,7 @@ void SLAPrint::process()
     };
 
     // Rasterizing the model objects, and their supports
-    auto rasterize = [this]() {
+    auto rasterize = [this, max_objstatus]() {
         if(canceled()) return;
 
         // collect all the keys

From e4cb75eddeaecb6a6c2b6d9694113c8fd82b59f4 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Wed, 12 Jun 2019 17:33:04 +0200
Subject: [PATCH 09/14] Fix build on Mac

---
 src/libslic3r/SLA/SLASupportTree.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index 4910226cc..b74f73d17 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -1472,8 +1472,8 @@ class SLASupportTree::Algorithm {
                     endp = endp - SQR2 * (gndlvl - endp(Z)) * dir; // back off
                 else {
                     
-                    if (!std::isinf(bridge_mesh_intersect(endp, DOWN, radius)))
-                        abort_in_shame();
+                    auto hit = bridge_mesh_intersect(endp, DOWN, radius);
+                    if (!std::isinf(hit.distance())) abort_in_shame();
 
                     Pillar &plr = m_result.add_pillar(endp, pgnd, radius);
 

From 90a854f7045b4b4295d2e0a31a5007175c7267bc Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Mon, 17 Jun 2019 13:02:49 +0200
Subject: [PATCH 10/14] Fix levitation when supports are disabled.

---
 src/libslic3r/SLAPrint.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index d3c931d34..9fe0687b5 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -604,8 +604,8 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
 sla::PoolConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) {
     sla::PoolConfig::EmbedObject ret;
     
-    ret.enabled = c.support_object_elevation.getFloat() <= EPSILON && 
-                  c.pad_enable.getBool();
+    ret.enabled = c.support_object_elevation.getFloat() <= EPSILON &&
+                  c.pad_enable.getBool() && c.supports_enable.getBool();
     
     if(ret.enabled) {
         ret.object_gap_mm        = c.pad_object_gap.getFloat();
@@ -1737,7 +1737,8 @@ bool SLAPrintObject::invalidate_all_steps()
 }
 
 double SLAPrintObject::get_elevation() const {
-    double ret = m_config.support_object_elevation.getFloat();
+    bool   en  = m_config.supports_enable.getBool();
+    double ret = en ? m_config.support_object_elevation.getFloat() : 0.;
 
     if(m_config.pad_enable.getBool()) {
         // Normally the elevation for the pad itself would be the thickness of

From 778b2cf293d3082162b97a2c7d40add410f0fde7 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Mon, 17 Jun 2019 18:06:52 +0200
Subject: [PATCH 11/14] WIP on removing unused parts of pad

---
 src/libslic3r/SLA/SLABasePool.cpp       |  45 +++++---
 src/libslic3r/SLA/SLABasePool.hpp       |   7 +-
 src/libslic3r/SLA/SLASpatIndex.hpp      |  65 ++++++++---
 src/libslic3r/SLA/SLASupportTree.cpp    |  93 +++++++++++-----
 src/libslic3r/SLA/SLASupportTreeIGL.cpp | 136 ++++++++++++++++++------
 5 files changed, 254 insertions(+), 92 deletions(-)

diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp
index 4e1e03018..48d615a29 100644
--- a/src/libslic3r/SLA/SLABasePool.cpp
+++ b/src/libslic3r/SLA/SLABasePool.cpp
@@ -666,24 +666,19 @@ Polygons concave_hull(const Polygons& polys, double max_dist_mm = 50,
     return punion;
 }
 
-void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
-                float layerh, ThrowOnCancel thrfn)
+void base_plate(const TriangleMesh &      mesh,
+                ExPolygons &              output,
+                const std::vector<float> &heights,
+                ThrowOnCancel             thrfn)
 {
-    TriangleMesh m = mesh;
-    m.require_shared_vertices(); // TriangleMeshSlicer needs this
-    TriangleMeshSlicer slicer(&m);
-
-    auto bb = mesh.bounding_box();
-    float gnd = float(bb.min(Z));
-    std::vector<float> heights = {float(bb.min(Z))};
-    for(float hi = gnd + layerh; hi <= gnd + h; hi += layerh)
-        heights.emplace_back(hi);
-
-    std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh)));
+    //    m.require_shared_vertices(); // TriangleMeshSlicer needs this    
+    TriangleMeshSlicer slicer(&mesh);
+    
+    std::vector<ExPolygons> out; out.reserve(heights.size());
     slicer.slice(heights, 0.f, &out, thrfn);
-
+    
     size_t count = 0; for(auto& o : out) count += o.size();
-
+    
     // Now we have to unify all slice layers which can be an expensive operation
     // so we will try to simplify the polygons
     ExPolygons tmp; tmp.reserve(count);
@@ -692,15 +687,31 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
             auto&& exss = e.simplify(scaled(0.1));
             for(ExPolygon& ep : exss) tmp.emplace_back(std::move(ep));
         }
-
+    
     ExPolygons utmp = unify(tmp);
-
+    
     for(auto& o : utmp) {
         auto&& smp = o.simplify(scaled(0.1));
         output.insert(output.end(), smp.begin(), smp.end());
     }
 }
 
+void base_plate(const TriangleMesh &mesh,
+                ExPolygons &        output,
+                float               h,
+                float               layerh,
+                ThrowOnCancel       thrfn)
+{
+    auto bb = mesh.bounding_box();
+    float gnd = float(bb.min(Z));
+    std::vector<float> heights = {float(bb.min(Z))};
+    
+    for(float hi = gnd + layerh; hi <= gnd + h; hi += layerh)
+        heights.emplace_back(hi);
+    
+    base_plate(mesh, output, heights, thrfn);
+}
+
 Contour3D create_base_pool(const Polygons &ground_layer, 
                            const ExPolygons &obj_self_pad = {},
                            const PoolConfig& cfg = PoolConfig()) 
diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp
index 8aa4f5f41..67b9ccdcb 100644
--- a/src/libslic3r/SLA/SLABasePool.hpp
+++ b/src/libslic3r/SLA/SLABasePool.hpp
@@ -21,10 +21,15 @@ using ThrowOnCancel = std::function<void(void)>;
 /// Calculate the polygon representing the silhouette from the specified height
 void base_plate(const TriangleMesh& mesh,       // input mesh
                 ExPolygons& output,             // Output will be merged with
-                float zlevel = 0.1f,            // Plate creation level
+                float samplingheight = 0.1f,    // The height range to sample
                 float layerheight = 0.05f,      // The sampling height
                 ThrowOnCancel thrfn = [](){});  // Will be called frequently
 
+void base_plate(const TriangleMesh& mesh,       // input mesh
+                ExPolygons& output,             // Output will be merged with
+                const std::vector<float>&,      // Exact Z levels to sample
+                ThrowOnCancel thrfn = [](){});  // Will be called frequently
+
 // Function to cut tiny connector cavities for a given polygon. The input poly
 // will be offsetted by "padding" and small rectangle shaped cavities will be
 // inserted along the perimeter in every "stride" distance. The stick rectangles
diff --git a/src/libslic3r/SLA/SLASpatIndex.hpp b/src/libslic3r/SLA/SLASpatIndex.hpp
index e5fbfa7d4..90dcdc362 100644
--- a/src/libslic3r/SLA/SLASpatIndex.hpp
+++ b/src/libslic3r/SLA/SLASpatIndex.hpp
@@ -7,13 +7,15 @@
 
 #include <Eigen/Geometry>
 
+#include <libslic3r/BoundingBox.hpp>
+
 namespace Slic3r {
 namespace sla {
 
 typedef Eigen::Matrix<double,   3, 1, Eigen::DontAlign> Vec3d;
-using SpatElement = std::pair<Vec3d, unsigned>;
+using PointIndexEl = std::pair<Vec3d, unsigned>;
 
-class SpatIndex {
+class PointIndex {
     class Impl;
 
     // We use Pimpl because it takes a long time to compile boost headers which
@@ -21,30 +23,67 @@ class SpatIndex {
     std::unique_ptr<Impl> m_impl;
 public:
 
-    SpatIndex();
-    ~SpatIndex();
+    PointIndex();
+    ~PointIndex();
 
-    SpatIndex(const SpatIndex&);
-    SpatIndex(SpatIndex&&);
-    SpatIndex& operator=(const SpatIndex&);
-    SpatIndex& operator=(SpatIndex&&);
+    PointIndex(const PointIndex&);
+    PointIndex(PointIndex&&);
+    PointIndex& operator=(const PointIndex&);
+    PointIndex& operator=(PointIndex&&);
 
-    void insert(const SpatElement&);
-    bool remove(const SpatElement&);
+    void insert(const PointIndexEl&);
+    bool remove(const PointIndexEl&);
 
     inline void insert(const Vec3d& v, unsigned idx)
     {
         insert(std::make_pair(v, unsigned(idx)));
     }
 
-    std::vector<SpatElement> query(std::function<bool(const SpatElement&)>);
-    std::vector<SpatElement> nearest(const Vec3d&, unsigned k);
+    std::vector<PointIndexEl> query(std::function<bool(const PointIndexEl&)>);
+    std::vector<PointIndexEl> nearest(const Vec3d&, unsigned k);
 
     // For testing
     size_t size() const;
     bool empty() const { return size() == 0; }
 
-    void foreach(std::function<void(const SpatElement& el)> fn);
+    void foreach(std::function<void(const PointIndexEl& el)> fn);
+};
+
+using BoxIndexEl = std::pair<Slic3r::BoundingBox, unsigned>;
+
+class BoxIndex {
+    class Impl;
+    
+    // We use Pimpl because it takes a long time to compile boost headers which
+    // is the engine of this class. We include it only in the cpp file.
+    std::unique_ptr<Impl> m_impl;
+public:
+    
+    BoxIndex();
+    ~BoxIndex();
+    
+    BoxIndex(const BoxIndex&);
+    BoxIndex(BoxIndex&&);
+    BoxIndex& operator=(const BoxIndex&);
+    BoxIndex& operator=(BoxIndex&&);
+    
+    void insert(const BoxIndexEl&);
+    inline void insert(const BoundingBox& bb, unsigned idx)
+    {
+        insert(std::make_pair(bb, unsigned(idx)));
+    }
+    
+    bool remove(const BoxIndexEl&);
+
+    enum QueryType { qtIntersects, qtWithin };
+
+    std::vector<BoxIndexEl> query(const BoundingBox&, QueryType qt);
+    
+    // For testing
+    size_t size() const;
+    bool empty() const { return size() == 0; }
+    
+    void foreach(std::function<void(const BoxIndexEl& el)> fn);
 };
 
 }
diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index 41040e89e..c38ff34e1 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -569,37 +569,74 @@ struct Pad {
                sla::get_pad_elevation(pcfg))
     {
         Polygons basep;
-        cfg.throw_on_cancel();
+        auto &thr = cfg.throw_on_cancel;
         
-        // The 0.1f is the layer height with which the mesh is sampled and then
-        // the layers are unified into one vector of polygons.
-        ExPolygons platetmp;
-        base_plate(object_support_mesh, platetmp,
-                   float(cfg.min_wall_height_mm + cfg.min_wall_thickness_mm),
-                   0.1f, pcfg.throw_on_cancel);
+        thr();
         
-        for (const ExPolygon &bp : platetmp)  basep.emplace_back(bp.contour);
+        // Get a sample for the pad from the support mesh
+        {
+            ExPolygons platetmp;
+            float      plateZ = float(get_pad_fullheight(pcfg) + EPSILON);
+
+            base_plate(object_support_mesh, platetmp, plateZ, 0.1f, thr);
+
+            // We don't need no... holes control...
+            for (const ExPolygon &bp : platetmp)
+                basep.emplace_back(std::move(bp.contour));
+        }
         
         if(pcfg.embed_object) {
-            ExPolygons modelbase_sticks = modelbase;
             
+            // If the zero elevation mode is ON, we need to process the model
+            // base silhouette. Create the offsetted version and punch the
+            // breaksticks across its perimeter.
+            
+            ExPolygons modelbase_sticks = modelbase;
+
             if (pcfg.embed_object.object_gap_mm > 0.0)
                 modelbase_sticks
                     = offset_ex(modelbase_sticks,
-                                coord_t(pcfg.embed_object.object_gap_mm
-                                        / SCALING_FACTOR));
+                                float(scaled(pcfg.embed_object.object_gap_mm)));
             
+            BoxIndex bindex;
+            {
+                unsigned idx = 0;
+                for(auto &bp : basep) {
+                    auto bb = bp.bounding_box();
+                    bb.offset(float(scaled(pcfg.min_wall_thickness_mm)));
+                    bindex.insert(bb, idx++);
+                }
+            }
+            
+            ExPolygons pad_stickholes; pad_stickholes.reserve(modelbase.size());
             for(auto& poly : modelbase_sticks) {
-                basep.emplace_back(poly.contour);
-                sla::breakstick_holes(
-                    poly,
-                    pcfg.embed_object.object_gap_mm,   // padding
-                    pcfg.embed_object.stick_stride_mm,
-                    pcfg.embed_object.stick_width_mm,
-                    pcfg.embed_object.stick_penetration_mm);
+                
+                if (!bindex.query(poly.contour.bounding_box(),
+                                  BoxIndex::qtIntersects).empty()) {
+                    
+                    basep.emplace_back(poly.contour);
+                    
+                    auto it = poly.holes.begin();
+                    while(it != poly.holes.end()) {
+                        if (bindex.query(it->bounding_box(),
+                                         BoxIndex::qtIntersects).empty())
+                            it = poly.holes.erase(it);
+                        else
+                            ++it;
+                    }
+
+                    sla::breakstick_holes(
+                        poly,
+                        pcfg.embed_object.object_gap_mm,   // padding
+                        pcfg.embed_object.stick_stride_mm,
+                        pcfg.embed_object.stick_width_mm,
+                        pcfg.embed_object.stick_penetration_mm);
+                    
+                    pad_stickholes.emplace_back(poly);
+                }
             }
 
-            create_base_pool(basep, tmesh, modelbase_sticks, cfg);
+            create_base_pool(basep, tmesh, pad_stickholes, cfg);
         } else {
             for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour);
             create_base_pool(basep, tmesh, {}, cfg);
@@ -630,7 +667,7 @@ inline Vec2d to_vec2(const Vec3d& v3) {
     return {v3(X), v3(Y)};
 }
 
-bool operator==(const SpatElement& e1, const SpatElement& e2) {
+bool operator==(const PointIndexEl& e1, const PointIndexEl& e2) {
     return e1.second == e2.second;
 }
 
@@ -647,7 +684,7 @@ ClusteredPoints cluster(const PointSet& points,
 ClusteredPoints cluster(
         const std::vector<unsigned>& indices,
         std::function<Vec3d(unsigned)> pointfn,
-        std::function<bool(const SpatElement&, const SpatElement&)> predicate,
+        std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
         unsigned max_points);
 
 // This class will hold the support tree meshes with some additional bookkeeping
@@ -974,7 +1011,7 @@ class SLASupportTree::Algorithm {
     ThrowOnCancel m_thr;
 
     // A spatial index to easily find strong pillars to connect to.
-    SpatIndex m_pillar_index;
+    PointIndex m_pillar_index;
 
     inline double ray_mesh_intersect(const Vec3d& s,
                                      const Vec3d& dir)
@@ -1367,7 +1404,7 @@ class SLASupportTree::Algorithm {
     }
 
     bool search_pillar_and_connect(const Head& head) {
-        SpatIndex spindex = m_pillar_index;
+        PointIndex spindex = m_pillar_index;
 
         long nearest_id = -1;
 
@@ -1747,8 +1784,8 @@ public:
             return m_result.head(i).junction_point();
         };
 
-        auto predicate = [this](const SpatElement &e1,
-                                const SpatElement &e2) {
+        auto predicate = [this](const PointIndexEl &e1,
+                                const PointIndexEl &e2) {
             double d2d = distance(to_2d(e1.first), to_2d(e2.first));
             double d3d = distance(e1.first, e2.first);
             return d2d < 2 * m_cfg.base_radius_mm
@@ -2070,7 +2107,7 @@ public:
         // be connected multiple times this is ensured by the 'pairs' set which
         // remembers the processed pillar pairs
         auto cascadefn =
-                [this, d, &pairs, min_height_ratio, H1] (const SpatElement& el)
+                [this, d, &pairs, min_height_ratio, H1] (const PointIndexEl& el)
         {
             Vec3d qp = el.first;    // endpoint of the pillar
 
@@ -2083,13 +2120,13 @@ public:
             if(pillar.links >= neighbors) return;
 
             // Query all remaining points within reach
-            auto qres = m_pillar_index.query([qp, d](const SpatElement& e){
+            auto qres = m_pillar_index.query([qp, d](const PointIndexEl& e){
                 return distance(e.first, qp) < d;
             });
 
             // sort the result by distance (have to check if this is needed)
             std::sort(qres.begin(), qres.end(),
-                      [qp](const SpatElement& e1, const SpatElement& e2){
+                      [qp](const PointIndexEl& e1, const PointIndexEl& e2){
                 return distance(e1.first, qp) < distance(e2.first, qp);
             });
 
diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp
index c368b8604..04e6f79c7 100644
--- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp
+++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp
@@ -29,69 +29,137 @@ namespace sla {
 using igl::PI;
 
 /* **************************************************************************
- * SpatIndex implementation
+ * PointIndex implementation
  * ************************************************************************** */
 
-class SpatIndex::Impl {
+class PointIndex::Impl {
 public:
-    using BoostIndex = boost::geometry::index::rtree< SpatElement,
+    using BoostIndex = boost::geometry::index::rtree< PointIndexEl,
                        boost::geometry::index::rstar<16, 4> /* ? */ >;
 
     BoostIndex m_store;
 };
 
-SpatIndex::SpatIndex(): m_impl(new Impl()) {}
-SpatIndex::~SpatIndex() {}
+PointIndex::PointIndex(): m_impl(new Impl()) {}
+PointIndex::~PointIndex() {}
 
-SpatIndex::SpatIndex(const SpatIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
-SpatIndex::SpatIndex(SpatIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
+PointIndex::PointIndex(const PointIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
+PointIndex::PointIndex(PointIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
 
-SpatIndex& SpatIndex::operator=(const SpatIndex &cpy)
+PointIndex& PointIndex::operator=(const PointIndex &cpy)
 {
     m_impl.reset(new Impl(*cpy.m_impl));
     return *this;
 }
 
-SpatIndex& SpatIndex::operator=(SpatIndex &&cpy)
+PointIndex& PointIndex::operator=(PointIndex &&cpy)
 {
     m_impl.swap(cpy.m_impl);
     return *this;
 }
 
-void SpatIndex::insert(const SpatElement &el)
+void PointIndex::insert(const PointIndexEl &el)
 {
     m_impl->m_store.insert(el);
 }
 
-bool SpatIndex::remove(const SpatElement& el)
+bool PointIndex::remove(const PointIndexEl& el)
 {
     return m_impl->m_store.remove(el) == 1;
 }
 
-std::vector<SpatElement>
-SpatIndex::query(std::function<bool(const SpatElement &)> fn)
+std::vector<PointIndexEl>
+PointIndex::query(std::function<bool(const PointIndexEl &)> fn)
 {
     namespace bgi = boost::geometry::index;
 
-    std::vector<SpatElement> ret;
+    std::vector<PointIndexEl> ret;
     m_impl->m_store.query(bgi::satisfies(fn), std::back_inserter(ret));
     return ret;
 }
 
-std::vector<SpatElement> SpatIndex::nearest(const Vec3d &el, unsigned k = 1)
+std::vector<PointIndexEl> PointIndex::nearest(const Vec3d &el, unsigned k = 1)
 {
     namespace bgi = boost::geometry::index;
-    std::vector<SpatElement> ret; ret.reserve(k);
+    std::vector<PointIndexEl> ret; ret.reserve(k);
     m_impl->m_store.query(bgi::nearest(el, k), std::back_inserter(ret));
     return ret;
 }
 
-size_t SpatIndex::size() const
+size_t PointIndex::size() const
 {
     return m_impl->m_store.size();
 }
 
-void SpatIndex::foreach(std::function<void (const SpatElement &)> fn)
+void PointIndex::foreach(std::function<void (const PointIndexEl &)> fn)
+{
+    for(auto& el : m_impl->m_store) fn(el);
+}
+
+/* **************************************************************************
+ * BoxIndex implementation
+ * ************************************************************************** */
+
+class BoxIndex::Impl {
+public:
+    using BoostIndex = boost::geometry::index::
+        rtree<BoxIndexEl, boost::geometry::index::rstar<16, 4> /* ? */>;
+
+    BoostIndex m_store;
+};
+
+BoxIndex::BoxIndex(): m_impl(new Impl()) {}
+BoxIndex::~BoxIndex() {}
+
+BoxIndex::BoxIndex(const BoxIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
+BoxIndex::BoxIndex(BoxIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
+
+BoxIndex& BoxIndex::operator=(const BoxIndex &cpy)
+{
+    m_impl.reset(new Impl(*cpy.m_impl));
+    return *this;
+}
+
+BoxIndex& BoxIndex::operator=(BoxIndex &&cpy)
+{
+    m_impl.swap(cpy.m_impl);
+    return *this;
+}
+
+void BoxIndex::insert(const BoxIndexEl &el)
+{
+    m_impl->m_store.insert(el);
+}
+
+bool BoxIndex::remove(const BoxIndexEl& el)
+{
+    return m_impl->m_store.remove(el) == 1;
+}
+
+std::vector<BoxIndexEl> BoxIndex::query(const BoundingBox &qrbb,
+                                        BoxIndex::QueryType qt)
+{
+    namespace bgi = boost::geometry::index;
+    
+    std::vector<BoxIndexEl> ret; ret.reserve(m_impl->m_store.size());
+    
+    switch (qt) {
+    case qtIntersects:
+        m_impl->m_store.query(bgi::intersects(qrbb), std::back_inserter(ret));
+        break;
+    case qtWithin:
+        m_impl->m_store.query(bgi::within(qrbb), std::back_inserter(ret));
+    }
+    
+    return ret;
+}
+
+size_t BoxIndex::size() const
+{
+    return m_impl->m_store.size();
+}
+
+void BoxIndex::foreach(std::function<void (const BoxIndexEl &)> fn)
 {
     for(auto& el : m_impl->m_store) fn(el);
 }
@@ -352,12 +420,14 @@ PointSet normals(const PointSet& points,
     return ret;
 }
 namespace bgi = boost::geometry::index;
-using Index3D = bgi::rtree< SpatElement, bgi::rstar<16, 4> /* ? */ >;
+using Index3D = bgi::rtree< PointIndexEl, bgi::rstar<16, 4> /* ? */ >;
 
-ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
-                        std::function<std::vector<SpatElement>(const Index3D&, const SpatElement&)> qfn)
+ClusteredPoints cluster(Index3D &sindex,
+                        unsigned max_points,
+                        std::function<std::vector<PointIndexEl>(
+                            const Index3D &, const PointIndexEl &)> qfn)
 {
-    using Elems = std::vector<SpatElement>;
+    using Elems = std::vector<PointIndexEl>;
 
     // Recursive function for visiting all the points in a given distance to
     // each other
@@ -365,8 +435,8 @@ ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
     [&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster)
     {
         for(auto& p : pts) {
-            std::vector<SpatElement> tmp = qfn(sindex, p);
-            auto cmp = [](const SpatElement& e1, const SpatElement& e2){
+            std::vector<PointIndexEl> tmp = qfn(sindex, p);
+            auto cmp = [](const PointIndexEl& e1, const PointIndexEl& e2){
                 return e1.second < e2.second;
             };
 
@@ -410,12 +480,12 @@ ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
 }
 
 namespace {
-std::vector<SpatElement> distance_queryfn(const Index3D& sindex,
-                                          const SpatElement& p,
+std::vector<PointIndexEl> distance_queryfn(const Index3D& sindex,
+                                          const PointIndexEl& p,
                                           double dist,
                                           unsigned max_points)
 {
-    std::vector<SpatElement> tmp; tmp.reserve(max_points);
+    std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
     sindex.query(
         bgi::nearest(p.first, max_points),
         std::back_inserter(tmp)
@@ -442,7 +512,7 @@ ClusteredPoints cluster(
     for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
 
     return cluster(sindex, max_points,
-                   [dist, max_points](const Index3D& sidx, const SpatElement& p)
+                   [dist, max_points](const Index3D& sidx, const PointIndexEl& p)
     {
         return distance_queryfn(sidx, p, dist, max_points);
     });
@@ -452,7 +522,7 @@ ClusteredPoints cluster(
 ClusteredPoints cluster(
         const std::vector<unsigned>& indices,
         std::function<Vec3d(unsigned)> pointfn,
-        std::function<bool(const SpatElement&, const SpatElement&)> predicate,
+        std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
         unsigned max_points)
 {
     // A spatial index for querying the nearest points
@@ -462,10 +532,10 @@ ClusteredPoints cluster(
     for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
 
     return cluster(sindex, max_points,
-        [max_points, predicate](const Index3D& sidx, const SpatElement& p)
+        [max_points, predicate](const Index3D& sidx, const PointIndexEl& p)
     {
-        std::vector<SpatElement> tmp; tmp.reserve(max_points);
-        sidx.query(bgi::satisfies([p, predicate](const SpatElement& e){
+        std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
+        sidx.query(bgi::satisfies([p, predicate](const PointIndexEl& e){
             return predicate(p, e);
         }), std::back_inserter(tmp));
         return tmp;
@@ -482,7 +552,7 @@ ClusteredPoints cluster(const PointSet& pts, double dist, unsigned max_points)
         sindex.insert(std::make_pair(Vec3d(pts.row(i)), unsigned(i)));
 
     return cluster(sindex, max_points,
-                   [dist, max_points](const Index3D& sidx, const SpatElement& p)
+                   [dist, max_points](const Index3D& sidx, const PointIndexEl& p)
     {
         return distance_queryfn(sidx, p, dist, max_points);
     });

From d7684188f90c8eb2ae1f7bc881aa918d3177ecd6 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 18 Jun 2019 11:24:50 +0200
Subject: [PATCH 12/14] Removing unused pad parts working

---
 src/libslic3r/MTUtils.hpp            | 54 ++++++++++++++++++++++++++++
 src/libslic3r/SLA/SLASupportTree.cpp |  9 +++--
 2 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp
index 7e91ace32..ee70f535d 100644
--- a/src/libslic3r/MTUtils.hpp
+++ b/src/libslic3r/MTUtils.hpp
@@ -5,6 +5,9 @@
 #include <mutex>        // for std::lock_guard
 #include <functional>   // for std::function
 #include <utility>      // for std::forward
+#include <vector>
+#include <algorithm>
+#include <cmath>
 
 namespace Slic3r {
 
@@ -182,6 +185,57 @@ public:
     inline bool empty() const { return size() == 0; }
 };
 
+template<class T>
+struct remove_cvref
+{
+    using type =
+        typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+};
+
+template<class T>
+using remove_cvref_t = typename remove_cvref<T>::type;
+
+template<template<class> class C, class T>
+class Container: public C<remove_cvref_t<T>> {
+public:
+    explicit Container(size_t count, T&& initval):
+        C<remove_cvref_t<T>>(count, initval) {}
+};
+
+template<class T> using DefaultContainer = std::vector<T>;
+
+/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
+template<class T, class I, template<class> class C = DefaultContainer>
+inline C<remove_cvref_t<T>> linspace(const T &start, const T &stop, const I &n)
+{
+    Container<C, T> vals(n, T());
+    T stride = (stop - start) / n;
+    
+    size_t i = 0;
+    std::generate(vals.begin(), vals.end(), [&i, start, stride] {
+        return start + i++ * stride; 
+    });
+    
+    return vals;
+}
+
+/// A set of equidistant values starting from 'start' (inclusive), ending
+/// in the closest multiple of 'stride' less than or equal to 'end' and
+/// leaving 'stride' space between each value. 
+/// Very similar to Matlab [start:stride:end] notation.
+template<class T, template<class> class C = DefaultContainer>
+inline C<remove_cvref_t<T>> grid(const T &start, const T &stop, const T &stride)
+{
+    Container<C, T> vals(size_t(std::ceil((stop - start) / stride)), T());
+    
+    int i = 0;
+    std::generate(vals.begin(), vals.end(), [&i, start, stride] {
+        return start + i++ * stride; 
+    });
+     
+    return vals;
+}
+
 }
 
 #endif // MTUTILS_HPP
diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index c38ff34e1..cfb5c2e74 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -9,6 +9,7 @@
 #include "SLASpatIndex.hpp"
 #include "SLABasePool.hpp"
 
+#include <libslic3r/MTUtils.hpp>
 #include <libslic3r/ClipperUtils.hpp>
 #include <libslic3r/Model.hpp>
 
@@ -559,7 +560,7 @@ struct Pad {
 
     Pad() = default;
 
-    Pad(const TriangleMesh& object_support_mesh,
+    Pad(const TriangleMesh& support_mesh,
         const ExPolygons& modelbase,
         double ground_level,
         const PoolConfig& pcfg) :
@@ -576,9 +577,11 @@ struct Pad {
         // Get a sample for the pad from the support mesh
         {
             ExPolygons platetmp;
-            float      plateZ = float(get_pad_fullheight(pcfg) + EPSILON);
 
-            base_plate(object_support_mesh, platetmp, plateZ, 0.1f, thr);
+            float zstart = float(zlevel);
+            float zend   = zstart + float(get_pad_fullheight(pcfg) + EPSILON);
+
+            base_plate(support_mesh, platetmp, grid(zstart, zend, 0.1f), thr);
 
             // We don't need no... holes control...
             for (const ExPolygon &bp : platetmp)

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 13/14] 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));
                 }
             }

From 548f19462a9b55fd573a1e529a6be2fe0343b169 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Fri, 28 Jun 2019 15:42:59 +0200
Subject: [PATCH 14/14] Fix formatting

---
 src/libslic3r/MTUtils.hpp | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp
index f83d38a42..42992223f 100644
--- a/src/libslic3r/MTUtils.hpp
+++ b/src/libslic3r/MTUtils.hpp
@@ -244,21 +244,21 @@ template<class C> bool all_of(const C &container)
                        });
 }
 
-template<class T>
-struct remove_cvref
+template<class T> struct remove_cvref
 {
     using type =
         typename std::remove_cv<typename std::remove_reference<T>::type>::type;
 };
 
-template<class T>
-using remove_cvref_t = typename remove_cvref<T>::type;
+template<class T> using remove_cvref_t = typename remove_cvref<T>::type;
 
 template<template<class> class C, class T>
-class Container: public C<remove_cvref_t<T>> {
+class Container : public C<remove_cvref_t<T>>
+{
 public:
-    explicit Container(size_t count, T&& initval):
-        C<remove_cvref_t<T>>(count, initval) {}
+    explicit Container(size_t count, T &&initval)
+        : C<remove_cvref_t<T>>(count, initval)
+    {}
 };
 
 template<class T> using DefaultContainer = std::vector<T>;
@@ -268,13 +268,13 @@ template<class T, class I, template<class> class C = DefaultContainer>
 inline C<remove_cvref_t<T>> linspace(const T &start, const T &stop, const I &n)
 {
     Container<C, T> vals(n, T());
-    T stride = (stop - start) / n;
-    
-    size_t i = 0;
+
+    T      stride = (stop - start) / n;
+    size_t i      = 0;
     std::generate(vals.begin(), vals.end(), [&i, start, stride] {
-        return start + i++ * stride; 
+        return start + i++ * stride;
     });
-    
+
     return vals;
 }