Implemented top_solid_min_thickness / bottom_solid_min_thickness.

The two new config keys define a minimum vertical shell thickness.
The top shell thickness is calculated as a maximum of sum over
top_solid_layers * layer heights and top_solid_min_thickness,
the bottom shell thickness is calculated as a maximum of sum over
bottom_solid_layers * layer heights and bottom_solid_min_thickness.

The results of the formula above are shown at the Print parameter page
below the two new values to hint the user about the interaction
of the old versus new config values.

top_solid_min_thickness has no meaning if top_solid_layers is zero,
bottom_solid_min_thickness has no meaning if bottom_solid_layers is zero.
This commit is contained in:
bubnikv 2020-02-05 16:53:26 +01:00
parent 6f777264a1
commit 495a71ed00
14 changed files with 246 additions and 52 deletions

View file

@ -42,7 +42,7 @@ enum Mode
SingleExtruder, // Single extruder printer preset is selected
MultiAsSingle, // Multiple extruder printer preset is selected, but
// this mode works just for Single extruder print
// (For all print from objects settings is used just one extruder)
// (The same extruder is assigned to all ModelObjects and ModelVolumes).
MultiExtruder // Multiple extruder printer preset is selected
};

View file

@ -105,6 +105,7 @@ public:
coordf_t slice_z; // Z used for slicing in unscaled coordinates
coordf_t print_z; // Z used for printing in unscaled coordinates
coordf_t height; // layer height in unscaled coordinates
coordf_t bottom_z() const { return this->print_z - this->height; }
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
// (with possibly differing extruder ID and slicing parameters) and merged.

View file

@ -168,6 +168,17 @@ void PrintConfigDef::init_fff_params()
def->min = 0;
def->set_default_value(new ConfigOptionInt(3));
def = this->add("bottom_solid_min_thickness", coFloat);
//TRN To be shown in Print Settings "Top solid layers"
def->label = L("Bottom");
def->category = L("Layers and Perimeters");
def->tooltip = L("The number of bottom solid layers is increased above bottom_solid_layers if necessary to satisfy "
"minimum thickness of bottom shell.");
def->full_label = L("Minimum bottom shell thickness");
def->sidetext = L("mm");
def->min = 0;
def->set_default_value(new ConfigOptionFloat(0.));
def = this->add("bridge_acceleration", coFloat);
def->label = L("Bridge");
def->tooltip = L("This is the acceleration your printer will use for bridges. "
@ -1782,6 +1793,13 @@ void PrintConfigDef::init_fff_params()
def->shortcut.push_back("bottom_solid_layers");
def->min = 0;
def = this->add("solid_min_thickness", coFloat);
def->label = L("Minimum thickness of a top / bottom shell");
def->tooltip = L("Minimum thickness of a top / bottom shell");
def->shortcut.push_back("top_solid_min_thickness");
def->shortcut.push_back("bottom_solid_min_thickness");
def->min = 0;
def = this->add("spiral_vase", coBool);
def->label = L("Spiral vase");
def->tooltip = L("This feature will raise Z gradually while printing a single-walled object "
@ -2128,6 +2146,18 @@ void PrintConfigDef::init_fff_params()
def->min = 0;
def->set_default_value(new ConfigOptionInt(3));
def = this->add("top_solid_min_thickness", coFloat);
//TRN To be shown in Print Settings "Top solid layers"
def->label = L("Top");
def->category = L("Layers and Perimeters");
def->tooltip = L("The number of top solid layers is increased above top_solid_layers if necessary to satisfy "
"minimum thickness of top shell."
" This is useful to prevent pillowing effect when printing with variable layer height.");
def->full_label = L("Minimum top shell thickness");
def->sidetext = L("mm");
def->min = 0;
def->set_default_value(new ConfigOptionFloat(0.));
def = this->add("travel_speed", coFloat);
def->label = L("Travel");
def->tooltip = L("Speed for travel moves (jumps between distant extrusion points).");

View file

@ -466,6 +466,7 @@ class PrintRegionConfig : public StaticPrintConfig
public:
ConfigOptionFloat bridge_angle;
ConfigOptionInt bottom_solid_layers;
ConfigOptionFloat bottom_solid_min_thickness;
ConfigOptionFloat bridge_flow_ratio;
ConfigOptionFloat bridge_speed;
ConfigOptionBool ensure_vertical_shell_thickness;
@ -501,6 +502,7 @@ public:
ConfigOptionBool thin_walls;
ConfigOptionFloatOrPercent top_infill_extrusion_width;
ConfigOptionInt top_solid_layers;
ConfigOptionFloat top_solid_min_thickness;
ConfigOptionFloatOrPercent top_solid_infill_speed;
ConfigOptionBool wipe_into_infill;
@ -509,6 +511,7 @@ protected:
{
OPT_PTR(bridge_angle);
OPT_PTR(bottom_solid_layers);
OPT_PTR(bottom_solid_min_thickness);
OPT_PTR(bridge_flow_ratio);
OPT_PTR(bridge_speed);
OPT_PTR(ensure_vertical_shell_thickness);
@ -542,6 +545,7 @@ protected:
OPT_PTR(top_infill_extrusion_width);
OPT_PTR(top_solid_infill_speed);
OPT_PTR(top_solid_layers);
OPT_PTR(top_solid_min_thickness);
OPT_PTR(wipe_into_infill);
}
};

View file

@ -507,7 +507,9 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|| opt_key == "infill_every_layers"
|| opt_key == "solid_infill_every_layers"
|| opt_key == "bottom_solid_layers"
|| opt_key == "bottom_solid_min_thickness"
|| opt_key == "top_solid_layers"
|| opt_key == "top_solid_min_thickness"
|| opt_key == "solid_infill_below_area"
|| opt_key == "infill_extruder"
|| opt_key == "solid_infill_extruder"
@ -914,6 +916,19 @@ void PrintObject::discover_vertical_shells()
Polygons bottom_surfaces;
Polygons holes;
};
coordf_t min_layer_height = this->slicing_parameters().min_layer_height;
// Does this region possibly produce more than 1 top or bottom layer?
auto has_extra_layers_fn = [min_layer_height](const PrintRegionConfig &config) {
auto num_extra_layers = [min_layer_height](int num_solid_layers, coordf_t min_shell_thickness) {
if (num_solid_layers == 0)
return 0;
int n = num_solid_layers - 1;
int n2 = int(ceil(min_shell_thickness / min_layer_height));
return std::max(n, n2 - 1);
};
return num_extra_layers(config.top_solid_layers, config.top_solid_min_thickness) +
num_extra_layers(config.bottom_solid_layers, config.bottom_solid_min_thickness) > 0;
};
std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(m_layers.size(), DiscoverVerticalShellsCacheEntry());
bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value;
if (top_bottom_surfaces_all_regions) {
@ -921,11 +936,11 @@ void PrintObject::discover_vertical_shells()
// is calculated over all materials.
// Is the "ensure vertical wall thickness" applicable to any region?
bool has_extra_layers = false;
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
const PrintRegion &region = *m_print->get_region(idx_region);
if (region.config().ensure_vertical_shell_thickness.value &&
(region.config().top_solid_layers.value > 1 || region.config().bottom_solid_layers.value > 1)) {
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++idx_region) {
const PrintRegionConfig &config = m_print->get_region(idx_region)->config();
if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) {
has_extra_layers = true;
break;
}
}
if (! has_extra_layers)
@ -1006,9 +1021,7 @@ void PrintObject::discover_vertical_shells()
if (! region.config().ensure_vertical_shell_thickness.value)
// This region will be handled by discover_horizontal_shells().
continue;
int n_extra_top_layers = std::max(0, region.config().top_solid_layers.value - 1);
int n_extra_bottom_layers = std::max(0, region.config().bottom_solid_layers.value - 1);
if (n_extra_top_layers + n_extra_bottom_layers == 0)
if (! has_extra_layers_fn(region.config()))
// Zero or 1 layer, there is no additional vertical wall thickness enforced.
continue;
@ -1049,7 +1062,7 @@ void PrintObject::discover_vertical_shells()
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : ensure vertical wall thickness";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size(), grain_size),
[this, idx_region, n_extra_top_layers, n_extra_bottom_layers, &cache_top_botom_regions]
[this, idx_region, &cache_top_botom_regions]
(const tbb::blocked_range<size_t>& range) {
// printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
@ -1060,8 +1073,9 @@ void PrintObject::discover_vertical_shells()
++ debug_idx;
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
Layer *layer = m_layers[idx_layer];
LayerRegion *layerm = layer->m_regions[idx_region];
Layer *layer = m_layers[idx_layer];
LayerRegion *layerm = layer->m_regions[idx_region];
const PrintRegionConfig &region_config = layerm->region()->config();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial");
@ -1101,30 +1115,47 @@ void PrintObject::discover_vertical_shells()
}
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Reset the top / bottom inflated regions caches of entries, which are out of the moving window.
bool hole_first = true;
for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n)
if (n >= 0 && n < (int)m_layers.size()) {
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[n];
if (hole_first) {
hole_first = false;
polygons_append(holes, cache.holes);
}
else if (! holes.empty()) {
holes = intersection(holes, cache.holes);
}
size_t n_shell_old = shell.size();
if (n > int(idx_layer))
// Collect top surfaces.
polygons_append(shell, cache.top_surfaces);
else if (n < int(idx_layer))
// Collect bottom and bottom bridge surfaces.
polygons_append(shell, cache.bottom_surfaces);
// Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once.
if (n_shell_old < shell.size())
shell = union_(shell, false);
}
polygons_append(holes, cache_top_botom_regions[idx_layer].holes);
{
// Gather top regions projected to this layer.
coordf_t print_z = layer->print_z;
int n_top_layers = region_config.top_solid_layers.value;
for (int i = int(idx_layer) + 1;
i < int(m_layers.size()) &&
(i < int(idx_layer) + n_top_layers ||
m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON);
++ i) {
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
if (! holes.empty())
holes = intersection(holes, cache.holes);
if (! cache.top_surfaces.empty()) {
polygons_append(shell, cache.top_surfaces);
// Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once.
shell = union_(shell, false);
}
}
}
{
// Gather bottom regions projected to this layer.
coordf_t bottom_z = layer->bottom_z();
int n_bottom_layers = region_config.bottom_solid_layers.value;
for (int i = int(idx_layer) - 1;
i >= 0 &&
(i > int(idx_layer) - n_bottom_layers ||
bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON);
-- i) {
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
if (! holes.empty())
holes = intersection(holes, cache.holes);
if (! cache.bottom_surfaces.empty()) {
polygons_append(shell, cache.bottom_surfaces);
// Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once.
shell = union_(shell, false);
}
}
}
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell));
@ -2280,7 +2311,8 @@ void PrintObject::discover_horizontal_shells()
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (size_t i = 0; i < m_layers.size(); ++ i) {
m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[i]->regions()[region_id];
Layer *layer = m_layers[i];
LayerRegion *layerm = layer->regions()[region_id];
const PrintRegionConfig &region_config = layerm->region()->config();
if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 &&
(i % region_config.solid_infill_every_layers) == 0) {
@ -2295,6 +2327,8 @@ void PrintObject::discover_horizontal_shells()
if (region_config.ensure_vertical_shell_thickness.value)
continue;
coordf_t print_z = layer->print_z;
coordf_t bottom_z = layer->bottom_z();
for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) {
m_print->throw_if_canceled();
SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge;
@ -2323,10 +2357,15 @@ void PrintObject::discover_horizontal_shells()
continue;
// Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom';
size_t solid_layers = (type == stTop) ? region_config.top_solid_layers.value : region_config.bottom_solid_layers.value;
for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - (int)i) < solid_layers; (type == stTop) ? -- n : ++ n) {
if (n < 0 || n >= int(m_layers.size()))
continue;
// Scatter top / bottom regions to other layers. Scattering process is inherently serial, it is difficult to parallelize without locking.
for (int n = (type == stTop) ? int(i) - 1 : int(i) + 1;
(type == stTop) ?
(n >= 0 && (int(i) - n < region_config.top_solid_layers.value ||
print_z - m_layers[n]->print_z < region_config.top_solid_min_thickness.value - EPSILON)) :
(n < int(m_layers.size()) && (n - int(i) < region_config.bottom_solid_layers.value ||
m_layers[n]->bottom_z() - bottom_z < region_config.bottom_solid_min_thickness.value - EPSILON));
(type == stTop) ? -- n : ++ n)
{
// Slic3r::debugf " looking for neighbors on layer %d...\n", $n;
// Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface.
LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id];

View file

@ -41,6 +41,23 @@ inline coordf_t max_layer_height_from_nozzle(const PrintConfig &print_config, in
return std::max(min_layer_height, (max_layer_height == 0.) ? (0.75 * nozzle_dmr) : max_layer_height);
}
// Minimum layer height for the variable layer height algorithm.
coordf_t Slicing::min_layer_height_from_nozzle(const DynamicPrintConfig &print_config, int idx_nozzle)
{
coordf_t min_layer_height = print_config.opt_float("min_layer_height", idx_nozzle - 1);
return (min_layer_height == 0.) ? MIN_LAYER_HEIGHT_DEFAULT : std::max(MIN_LAYER_HEIGHT, min_layer_height);
}
// Maximum layer height for the variable layer height algorithm, 3/4 of a nozzle dimaeter by default,
// it should not be smaller than the minimum layer height.
coordf_t Slicing::max_layer_height_from_nozzle(const DynamicPrintConfig &print_config, int idx_nozzle)
{
coordf_t min_layer_height = min_layer_height_from_nozzle(print_config, idx_nozzle);
coordf_t max_layer_height = print_config.opt_float("max_layer_height", idx_nozzle - 1);
coordf_t nozzle_dmr = print_config.opt_float("nozzle_diameter", idx_nozzle - 1);
return std::max(min_layer_height, (max_layer_height == 0.) ? (0.75 * nozzle_dmr) : max_layer_height);
}
SlicingParameters SlicingParameters::create_from_config(
const PrintConfig &print_config,
const PrintObjectConfig &object_config,

View file

@ -99,7 +99,6 @@ struct SlicingParameters
};
static_assert(IsTriviallyCopyable<SlicingParameters>::value, "SlicingParameters class is not POD (and it should be - see constructor).");
// The two slicing parameters lead to the same layering as long as the variable layer thickness is not in action.
inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters &sp2)
{
@ -183,7 +182,17 @@ extern int generate_layer_height_texture(
const std::vector<coordf_t> &layers,
void *data, int rows, int cols, bool level_of_detail_2nd_level);
}; // namespace Slic3r
namespace Slicing {
// Minimum layer height for the variable layer height algorithm. Nozzle index is 1 based.
coordf_t min_layer_height_from_nozzle(const DynamicPrintConfig &print_config, int idx_nozzle);
// Maximum layer height for the variable layer height algorithm, 3/4 of a nozzle dimaeter by default,
// it should not be smaller than the minimum layer height.
// Nozzle index is 1 based.
coordf_t max_layer_height_from_nozzle(const DynamicPrintConfig &print_config, int idx_nozzle);
} // namespace Slicing
} // namespace Slic3r
namespace cereal
{

View file

@ -233,22 +233,27 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
"solid_infill_every_layers", "solid_infill_below_area", "infill_extruder" })
toggle_field(el, have_infill);
bool have_solid_infill = config->opt_int("top_solid_layers") > 0 || config->opt_int("bottom_solid_layers") > 0;
bool has_spiral_vase = config->opt_bool("spiral_vase");
bool has_top_solid_infill = config->opt_int("top_solid_layers") > 0;
bool has_bottom_solid_infill = config->opt_int("bottom_solid_layers") > 0;
bool has_solid_infill = has_top_solid_infill || has_bottom_solid_infill;
// solid_infill_extruder uses the same logic as in Print::extruders()
for (auto el : { "top_fill_pattern", "bottom_fill_pattern", "infill_first", "solid_infill_extruder",
"solid_infill_extrusion_width", "solid_infill_speed" })
toggle_field(el, have_solid_infill);
toggle_field(el, has_solid_infill);
for (auto el : { "fill_angle", "bridge_angle", "infill_extrusion_width",
"infill_speed", "bridge_speed" })
toggle_field(el, have_infill || have_solid_infill);
toggle_field(el, have_infill || has_solid_infill);
toggle_field("top_solid_min_thickness", ! has_spiral_vase && has_top_solid_infill);
toggle_field("bottom_solid_min_thickness", ! has_spiral_vase && has_bottom_solid_infill);
// Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476).
toggle_field("gap_fill_speed", have_perimeters);
bool have_top_solid_infill = config->opt_int("top_solid_layers") > 0;
for (auto el : { "top_infill_extrusion_width", "top_solid_infill_speed" })
toggle_field(el, have_top_solid_infill);
toggle_field(el, has_top_solid_infill);
bool have_default_acceleration = config->opt_float("default_acceleration") > 0;
for (auto el : { "perimeter_acceleration", "infill_acceleration",

View file

@ -1980,7 +1980,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology",
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
"layer_height", "first_layer_height", "min_layer_height", "max_layer_height",
"brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", "bottom_solid_layers", "solid_infill_extruder",
"brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers",
"support_material", "support_material_extruder", "support_material_interface_extruder", "support_material_contact_distance", "raft_layers"
}))
, sidebar(new Sidebar(q))

View file

@ -386,7 +386,8 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
const std::vector<std::string>& Preset::print_options()
{
static std::vector<std::string> s_opts {
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "top_solid_layers", "bottom_solid_layers",
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius",
"top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
"extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",
"seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
"infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",

View file

@ -1,6 +1,7 @@
#include <cassert>
#include "libslic3r/Flow.hpp"
#include "libslic3r/Slicing.hpp"
#include "libslic3r/libslic3r.h"
#include "PresetBundle.hpp"
@ -242,7 +243,7 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre
float nozzle_diameter = float(printer_config.opt_float("nozzle_diameter", 0));
std::string out;
if (layer_height <= 0.f){
if (layer_height <= 0.f) {
out += _utf8(L("Recommended object thin wall thickness: Not available due to invalid layer height."));
return out;
}
@ -272,4 +273,70 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre
return out;
}
// Produce a textual explanation of the combined effects of the top/bottom_solid_layers
// versus top/bottom_min_shell_thickness. Which of the two values wins depends
// on the active layer height.
std::string PresetHints::top_bottom_shell_thickness_explanation(const PresetBundle &preset_bundle)
{
const DynamicPrintConfig &print_config = preset_bundle.prints .get_edited_preset().config;
const DynamicPrintConfig &printer_config = preset_bundle.printers .get_edited_preset().config;
std::string out;
int top_solid_layers = print_config.opt_int("top_solid_layers");
int bottom_solid_layers = print_config.opt_int("bottom_solid_layers");
bool has_top_layers = top_solid_layers > 0;
bool has_bottom_layers = bottom_solid_layers > 0;
bool has_shell = has_top_layers && has_bottom_layers;
double top_solid_min_thickness = print_config.opt_float("top_solid_min_thickness");
double bottom_solid_min_thickness = print_config.opt_float("bottom_solid_min_thickness");
double layer_height = print_config.opt_float("layer_height");
bool variable_layer_height = printer_config.opt_bool("variable_layer_height");
//FIXME the following lines take into account the 1st extruder only.
double min_layer_height = (has_shell && variable_layer_height) ? Slicing::min_layer_height_from_nozzle(printer_config, 1) : layer_height;
double max_layer_height = (has_shell && variable_layer_height) ? Slicing::max_layer_height_from_nozzle(printer_config, 1) : layer_height;
if (layer_height <= 0.f) {
out += _utf8(L("Top / bottom shell thickness hint: Not available due to invalid layer height."));
return out;
}
if (has_top_layers) {
double top_shell_thickness = top_solid_layers * layer_height;
if (top_shell_thickness < top_solid_min_thickness) {
// top_solid_min_shell_thickness triggers even in case of normal layer height. Round the top_shell_thickness up
// to an integer multiply of layer_height.
double n = ceil(top_solid_min_thickness / layer_height);
top_shell_thickness = n * layer_height;
}
double top_shell_thickness_minimum = std::max(top_solid_min_thickness, top_solid_layers * min_layer_height);
out += (boost::format(_utf8(L("Top shell is %1% mm thick for layer height %2% mm."))) % top_shell_thickness % layer_height).str();
if (variable_layer_height && top_shell_thickness_minimum < top_shell_thickness) {
out += " ";
out += (boost::format(_utf8(L("Minimum top shell thickness is %1% mm."))) % top_shell_thickness_minimum).str();
}
}
if (has_bottom_layers) {
double bottom_shell_thickness = bottom_solid_layers * layer_height;
if (bottom_shell_thickness < bottom_solid_min_thickness) {
// bottom_solid_min_shell_thickness triggers even in case of normal layer height. Round the bottom_shell_thickness up
// to an integer multiply of layer_height.
double n = ceil(bottom_solid_min_thickness / layer_height);
bottom_shell_thickness = n * layer_height;
}
double bottom_shell_thickness_minimum = std::max(bottom_solid_min_thickness, bottom_solid_layers * min_layer_height);
if (! out.empty())
out += "\n";
out += (boost::format(_utf8(L("Bottom shell is %1% mm thick for layer height %2% mm."))) % bottom_shell_thickness % layer_height).str();
if (variable_layer_height && bottom_shell_thickness_minimum < bottom_shell_thickness) {
out += " ";
out += (boost::format(_utf8(L("Minimum bottom shell thickness is %1% mm."))) % bottom_shell_thickness_minimum).str();
}
}
return out;
}
}; // namespace Slic3r

View file

@ -23,6 +23,11 @@ public:
// Produce a textual description of a recommended thin wall thickness
// from the provided number of perimeters and the external / internal perimeter width.
static std::string recommended_thin_wall_thickness(const PresetBundle &preset_bundle);
// Produce a textual explanation of the combined effects of the top/bottom_solid_layers
// versus top/bottom_min_shell_thickness. Which of the two values wins depends
// on the active layer height.
static std::string top_bottom_shell_thickness_explanation(const PresetBundle &preset_bundle);
};
} // namespace Slic3r

View file

@ -1056,6 +1056,16 @@ void TabPrint::build()
line.append_option(optgroup->get_option("top_solid_layers"));
line.append_option(optgroup->get_option("bottom_solid_layers"));
optgroup->append_line(line);
line = { _(L("Minimum shell thickness")), "" };
line.append_option(optgroup->get_option("top_solid_min_thickness"));
line.append_option(optgroup->get_option("bottom_solid_min_thickness"));
optgroup->append_line(line);
line = { "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
return description_line_widget(parent, &m_top_bottom_shell_thickness_explanation);
};
optgroup->append_line(line);
optgroup = page->new_optgroup(_(L("Quality (slower slicing)")));
optgroup->append_single_option_line("extra_perimeters");
@ -1277,6 +1287,8 @@ void TabPrint::update()
m_recommended_thin_wall_thickness_description_line->SetText(
from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle)));
m_top_bottom_shell_thickness_explanation->SetText(
from_u8(PresetHints::top_bottom_shell_thickness_explanation(*m_preset_bundle)));
Layout();
// Thaw();
@ -1295,6 +1307,8 @@ void TabPrint::OnActivate()
{
m_recommended_thin_wall_thickness_description_line->SetText(
from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle)));
m_top_bottom_shell_thickness_explanation->SetText(
from_u8(PresetHints::top_bottom_shell_thickness_explanation(*m_preset_bundle)));
Tab::OnActivate();
}

View file

@ -327,8 +327,9 @@ public:
Tab(parent, _(L("Print Settings")), Slic3r::Preset::TYPE_PRINT) {}
~TabPrint() {}
ogStaticText* m_recommended_thin_wall_thickness_description_line;
bool m_support_material_overhangs_queried = false;
ogStaticText* m_recommended_thin_wall_thickness_description_line = nullptr;
ogStaticText* m_top_bottom_shell_thickness_explanation = nullptr;
bool m_support_material_overhangs_queried = false;
void build() override;
void reload_config() override;
@ -336,6 +337,7 @@ public:
void OnActivate() override;
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; }
};
class TabFilament : public Tab
{
ogStaticText* m_volumetric_speed_description_line;