From d27e22c2c3eadbd1b173b970749c84a5945da948 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Tue, 20 Nov 2018 16:12:04 +0100
Subject: [PATCH] height level mismatches seems to be fixed.

---
 src/libslic3r/PrintConfig.cpp        |   7 ++
 src/libslic3r/PrintConfig.hpp        |   2 +
 src/libslic3r/SLA/SLABasePool.hpp    |  16 ++++
 src/libslic3r/SLA/SLASupportTree.cpp |  12 +--
 src/libslic3r/SLA/SLASupportTree.hpp |   3 -
 src/libslic3r/SLAPrint.cpp           | 122 +++++++++++++++++----------
 src/slic3r/GUI/Preset.cpp            |   1 +
 src/slic3r/GUI/Tab.cpp               |   1 +
 8 files changed, 106 insertions(+), 58 deletions(-)

diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 96c492292..055d7e46a 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -2525,6 +2525,13 @@ void PrintConfigDef::init_sla_params()
     def->min = 0;
     def->default_value = new ConfigOptionFloat(5.0);
 
+    def = this->add("pad_enable", coBool);
+    def->label = L("Use pad");
+    def->tooltip = L("Add a pad underneath the supported model");
+    def->sidetext = L("");
+    def->cli = "";
+    def->default_value = new ConfigOptionBool(true);
+
     def = this->add("pad_wall_thickness", coFloat);
     def->label = L("Pad wall thickness");
     def->tooltip = L("");
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 54d6274a9..f1d1c9784 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -942,6 +942,7 @@ public:
 
     // Now for the base pool (pad) /////////////////////////////////////////////
 
+    ConfigOptionBool  pad_enable;
     ConfigOptionFloat pad_wall_thickness /*= 2*/;
     ConfigOptionFloat pad_wall_height /*= 5*/;
     ConfigOptionFloat pad_max_merge_distance /*= 50*/;
@@ -961,6 +962,7 @@ protected:
         OPT_PTR(support_critical_angle);
         OPT_PTR(support_max_bridge_length);
         OPT_PTR(support_object_elevation);
+        OPT_PTR(pad_enable);
         OPT_PTR(pad_wall_thickness);
         OPT_PTR(pad_wall_height);
         OPT_PTR(pad_max_merge_distance);
diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp
index e773de29c..132a7aeac 100644
--- a/src/libslic3r/SLA/SLABasePool.hpp
+++ b/src/libslic3r/SLA/SLABasePool.hpp
@@ -23,6 +23,13 @@ struct PoolConfig {
     double min_wall_height_mm = 5;
     double max_merge_distance_mm = 50;
     double edge_radius_mm = 1;
+
+    inline PoolConfig() {}
+    inline PoolConfig(double wt, double wh, double md, double er):
+        min_wall_thickness_mm(wt),
+        min_wall_height_mm(wh),
+        max_merge_distance_mm(md),
+        edge_radius_mm(er) {}
 };
 
 /// Calculate the pool for the mesh for SLA printing
@@ -31,6 +38,15 @@ void create_base_pool(const ExPolygons& base_plate,
                       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.
+inline double get_pad_elevation(const PoolConfig& cfg) {
+    return cfg.min_wall_height_mm / 2.0;
+}
+
 }
 
 }
diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index 55e0c5010..f42f3b4ac 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -512,7 +512,7 @@ struct Pad {
         double ground_level,
         const PoolConfig& pcfg) :
         cfg(pcfg),
-        zlevel(ground_level + cfg.min_wall_height_mm/2)
+        zlevel(ground_level + sla::get_pad_elevation(pcfg))
     {
         ExPolygons basep;
         base_plate(object_support_mesh, basep,
@@ -1634,7 +1634,7 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
     const auto modelh = float(stree.full_height());
     auto gndlvl = float(this->m_impl->ground_level);
     const Pad& pad = m_impl->pad();
-    if(!pad.empty()) gndlvl -= float(pad.cfg.min_wall_height_mm/2);
+    if(!pad.empty()) gndlvl -= float(get_pad_elevation(pad.cfg));
 
     std::vector<float> heights = {gndlvl};
     heights.reserve(size_t(modelh/layerh) + 1);
@@ -1673,20 +1673,12 @@ const TriangleMesh &SLASupportTree::get_pad() const
     return m_impl->pad().tmesh;
 }
 
-double SLASupportTree::get_elevation() const
-{
-    double ph = m_impl->pad().empty()? 0 :
-                                       m_impl->pad().cfg.min_wall_height_mm/2.0;
-    return m_elevation + ph;
-}
-
 SLASupportTree::SLASupportTree(const PointSet &points,
                                const EigenMesh3D& emesh,
                                const SupportConfig &cfg,
                                const Controller &ctl):
     m_impl(new Impl()), m_ctl(ctl)
 {
-    m_elevation = cfg.object_elevation_mm;
     m_impl->ground_level = emesh.ground_level - cfg.object_elevation_mm;
     generate(points, emesh, cfg, ctl);
 }
diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp
index 6295a45b0..ce562946c 100644
--- a/src/libslic3r/SLA/SLASupportTree.hpp
+++ b/src/libslic3r/SLA/SLASupportTree.hpp
@@ -118,9 +118,6 @@ class SLASupportTree {
     std::unique_ptr<Impl> m_impl;
     Controller m_ctl;
 
-    // the only value from config that is also needed after construction
-    double m_elevation = 0;
-
     Impl& get() { return *m_impl; }
     const Impl& get() const { return *m_impl; }
 
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index e9e2688cf..568ff6566 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -3,6 +3,8 @@
 #include "SLA/SLABasePool.hpp"
 
 #include <tbb/parallel_for.h>
+#include <boost/log/trivial.hpp>
+
 //#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
 
 #include "I18N.hpp"
@@ -128,32 +130,36 @@ void SLAPrint::process()
     // the model objects we have to process and the instances are also filtered
 
     // shortcut to initial layer height
-    auto ilh = float(m_material_config.initial_layer_height.getFloat());
-
-    std::cout << "Initial layer height: " << m_material_config.initial_layer_height.getFloat() << std::endl;
+    double ilhd = m_material_config.initial_layer_height.getFloat();
+    auto   ilh  = float(ilhd);
 
     // Slicing the model object. This method is oversimplified and needs to
     // be compared with the fff slicing algorithm for verification
-    auto slice_model = [this, ilh](SLAPrintObject& po) {
-        auto lh = float(po.m_config.layer_height.getFloat());
+    auto slice_model = [this, ilh, ilhd](SLAPrintObject& po) {
+        double lh = po.m_config.layer_height.getFloat();
 
         TriangleMesh mesh = po.transformed_mesh();
         TriangleMeshSlicer slicer(&mesh);
         auto bb3d = mesh.bounding_box();
 
-        auto H = bb3d.max(Z) - bb3d.min(Z);
+        double elevation = po.get_elevation();
+
+        float minZ = float(bb3d.min(Z)) - float(elevation);
+        float maxZ = float(bb3d.max(Z)) ;
+        auto flh = float(lh);
         auto gnd = float(bb3d.min(Z));
 
-        double elevation = po.m_config.support_object_elevation.getFloat();
-        float ih = elevation > 0 ? lh : ilh;
+        std::vector<float> heights;
 
-        std::vector<float> heights = {gnd};
-        for(float h = gnd + ih; h < gnd + H; h += lh) heights.emplace_back(h);
+        // The first layer (the one before the initial height) is added only
+        // if the there is no pad and no elevation value
+        if(minZ >= gnd) heights.emplace_back(minZ);
+
+        for(float h = minZ + ilh; h < maxZ; h += flh)
+            if(h >= gnd) heights.emplace_back(h);
 
         auto& layers = po.m_model_slices;
-        slicer.slice(heights, &layers, [this](){
-            throw_if_canceled();
-        });
+        slicer.slice(heights, &layers, [this](){ throw_if_canceled(); });
     };
 
     auto support_points = [](SLAPrintObject& po) {
@@ -216,6 +222,7 @@ void SLAPrint::process()
         // repeated)
 
         if(po.is_step_done(slaposSupportTree) &&
+           po.m_config.pad_enable.getBool() &&
            po.m_supportdata &&
            po.m_supportdata->support_tree_ptr)
         {
@@ -225,10 +232,12 @@ void SLAPrint::process()
             double er = po.m_config.pad_edge_radius.getFloat();
             double lh = po.m_config.layer_height.getFloat();
             double elevation = po.m_config.support_object_elevation.getFloat();
+            sla::PoolConfig pcfg(wt, h, md, er);
 
             sla::ExPolygons bp;
-            if(elevation < h/2) sla::base_plate(po.transformed_mesh(), bp,
-                                                float(h/2), float(lh));
+            double pad_h = sla::get_pad_elevation(pcfg);
+            if(elevation < pad_h) sla::base_plate(po.transformed_mesh(), bp,
+                                                  float(pad_h), float(lh));
 
             po.m_supportdata->support_tree_ptr->add_pad(bp, wt, h, md, er);
         }
@@ -246,7 +255,7 @@ void SLAPrint::process()
     };
 
     // Rasterizing the model objects, and their supports
-    auto rasterize = [this, ilh]() {
+    auto rasterize = [this, ilh, ilhd]() {
         using Layer = sla::ExPolygons;
         using LayerCopies = std::vector<SLAPrintObject::Instance>;
         struct LayerRef {
@@ -262,51 +271,60 @@ void SLAPrint::process()
         // layers according to quantized height levels
         std::map<LevelID, LayerRefs> levels;
 
-        auto sinitlh = LevelID(scale_(ilh));
+        auto sih = LevelID(scale_(ilh));
 
         // For all print objects, go through its initial layers and place them
         // into the layers hash
         for(SLAPrintObject *o : m_objects) {
-
-            double gndlvl = o->transformed_mesh().bounding_box().min(Z);
-            double elevation = o->m_config.support_object_elevation.getFloat();
-
+            auto bb = o->transformed_mesh().bounding_box();
+            double modelgnd = bb.min(Z);
+            double elevation = o->get_elevation();
             double lh = o->m_config.layer_height.getFloat();
+            double minZ = modelgnd - elevation;
 
-            // TODO: this juust misses the support layers with a slight offset...
-            double ih = elevation > 0 ? lh : ilh;
+            // scaled values:
+            auto sminZ = LevelID(scale_(minZ));
+            auto smaxZ = LevelID(scale_(bb.max(Z)));
+            auto smodelgnd = LevelID(scale_(modelgnd));
+            auto slh = LevelID(scale_(lh));
 
-            auto sgl = LevelID(scale_(gndlvl));
-            auto slh = LevelID(scale_(lh));    // scaled layer height
-            auto sih = LevelID(scale_(ih));
+            // It is important that the next levels math the levels in
+            // model_slice method. Only difference is that here it works with
+            // scaled coordinates
+            std::vector<LevelID> levelids;
+            if(sminZ >= smodelgnd) levelids.emplace_back(sminZ);
+            for(LevelID h = sminZ + sih; h < smaxZ; h += slh)
+                if(h >= smodelgnd) levelids.emplace_back(h);
 
             SlicedModel & oslices = o->m_model_slices;
+
+            // If everything went well this code should not run at all, but
+            // let's be robust...
+            assert(levelids.size() == oslices.size());
+            if(levelids.size() < oslices.size()) { // extend the levels until...
+
+                BOOST_LOG_TRIVIAL(warning)
+                        << "Height level mismatch at rasterization!\n";
+
+                LevelID lastlvl = levelids.back();
+                while(levelids.size() < oslices.size()) {
+                    lastlvl += slh;
+                    levelids.emplace_back(lastlvl);
+                }
+            }
+
             for(int i = 0; i < oslices.size(); ++i) {
-                int a = i == 0 ? 0 : 1;
-                int b = i == 0 ? 0 : i - 1;
-
-                LevelID h = sgl + sih * a + b * slh;
-
-                std::cout << "Model layer level: " << h << std::endl;
-
+                LevelID h = levelids[i];
                 auto& lyrs = levels[h]; // this initializes a new record
                 lyrs.emplace_back(oslices[i], o->m_instances);
             }
 
             if(o->m_supportdata) { // deal with the support slices if present
                 auto& sslices = o->m_supportdata->support_slices;
-
-                // Supports start below the ground level.
-                // Counting the pad height as well
-                double el = o->get_elevation();
-                auto sel = LevelID(scale_(el));
-
                 for(int i = 0; i < sslices.size(); ++i) {
                     int a = i == 0 ? 0 : 1;
                     int b = i == 0 ? 0 : i - 1;
-
-                    LevelID h = sgl - sel + sinitlh * a + b * slh;
-                    std::cout << "Support layer level: " << h << std::endl;
+                    LevelID h = sminZ + a * sih + b * slh;
 
                     auto& lyrs = levels[h];
                     lyrs.emplace_back(sslices[i], o->m_instances);
@@ -473,9 +491,23 @@ SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
 SLAPrintObject::~SLAPrintObject() {}
 
 double SLAPrintObject::get_elevation() const {
-    return m_supportdata && m_supportdata->support_tree_ptr?
-                m_supportdata->support_tree_ptr->get_elevation() :
-                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
+        // 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;
+        pcfg.min_wall_height_mm = m_config.pad_wall_height.getFloat();
+        pcfg.min_wall_thickness_mm = m_config.pad_wall_thickness.getFloat();
+        pcfg.edge_radius_mm = m_config.pad_edge_radius.getFloat();
+        pcfg.max_merge_distance_mm = m_config.pad_max_merge_distance.getFloat();
+        ret += sla::get_pad_elevation(pcfg);
+    }
+
+    return ret;
 }
 
 //const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index 541e5c79b..4ed00e834 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -412,6 +412,7 @@ const std::vector<std::string>& Preset::sla_print_options()
             "support_critical_angle",
             "support_max_bridge_length",
             "support_object_elevation",
+            "pad_enable",
             "pad_wall_thickness",
             "pad_wall_height",
             "pad_max_merge_distance",
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index dcd5f33fc..85220b8ad 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -3012,6 +3012,7 @@ void TabSLAPrint::build()
     optgroup->append_single_option_line("support_max_bridge_length");
 
     optgroup = page->new_optgroup(_(L("Pad")));
+    optgroup->append_single_option_line("pad_enable");
     optgroup->append_single_option_line("pad_wall_thickness");
     optgroup->append_single_option_line("pad_wall_height");
     optgroup->append_single_option_line("pad_max_merge_distance");