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");