Partial implementation #1405: minimum shell thickness option (#3885)

* Added Minimum shell thickness option

-Added the option to libslic3r
-Created some of the necessary checks for illegal values

* Enforce min_shell_thickness as 0 when spiral vase is enabled

-This could be switched to match the extrusion width, but we already know there will only be 1 perimeter so this is probably more logical

* Feature is implemented

-Added a method in PerimeterGenerator to determine the amount of perimeters needed to meet min_shell_thickness

* Added a test in perimeters.t

* Fixed styling as requested

-Removed the magic number in PerimeterGenerator::num_loops()
-Added more test cases in perimeters.t
-Added documentation of the new feature in slic3r.pl and README.md

* Implemented requested changes

-Removed the num_loops function from PerimeterGenerator
-Added a scalled min_shell_thickness variable to PerimeterGenerator
-Changed the loop_number logic to use a previously defined variable loops

* Resolved more implementation issues

-Added min shell to invalidate state method
-Created additional checks in PresetEditor and LayerRegion for min shell
-Fixed the tooltip to be more descriptive
This commit is contained in:
Caroline 2018-05-22 18:25:02 -05:00 committed by Joseph Lenox
parent 61ace41351
commit f3b590911d
10 changed files with 122 additions and 31 deletions

View File

@ -216,6 +216,7 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
--perimeters Number of perimeters/horizontal skins (range: 0+, default: 3) --perimeters Number of perimeters/horizontal skins (range: 0+, default: 3)
--top-solid-layers Number of solid layers to do for top surfaces (range: 0+, default: 3) --top-solid-layers Number of solid layers to do for top surfaces (range: 0+, default: 3)
--bottom-solid-layers Number of solid layers to do for bottom surfaces (range: 0+, default: 3) --bottom-solid-layers Number of solid layers to do for bottom surfaces (range: 0+, default: 3)
--min-shell-thickness Minimum thickness of all solid shells (range: 0+, default: 0)
--solid-layers Shortcut for setting the two options above at once --solid-layers Shortcut for setting the two options above at once
--fill-density Infill density (range: 0%-100%, default: 40%) --fill-density Infill density (range: 0%-100%, default: 40%)
--fill-angle Infill angle in degrees (range: 0-90, default: 45) --fill-angle Infill angle in degrees (range: 0-90, default: 45)

View File

@ -176,6 +176,9 @@ sub validate {
die "Invalid value for --filament-diameter\n" die "Invalid value for --filament-diameter\n"
if grep $_ < 1, @{$self->filament_diameter}; if grep $_ < 1, @{$self->filament_diameter};
die "Invalid value for --min-shell-thickness\n"
if $self->min_shell_thickness < 0;
# --nozzle-diameter # --nozzle-diameter
die "Invalid value for --nozzle-diameter\n" die "Invalid value for --nozzle-diameter\n"
if grep $_ < 0, @{$self->nozzle_diameter}; if grep $_ < 0, @{$self->nozzle_diameter};
@ -254,6 +257,9 @@ sub validate {
die "Can't make less than one perimeter when spiral vase mode is enabled\n" die "Can't make less than one perimeter when spiral vase mode is enabled\n"
if $self->perimeters < 1; if $self->perimeters < 1;
die "Minimum shell thickness should be 0 when spiral vase mode is enabled\n"
if $self->min_shell_thickness > 0;
die "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0\n" die "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0\n"
if $self->fill_density > 0; if $self->fill_density > 0;

View File

@ -438,7 +438,7 @@ sub options {
layer_height first_layer_height layer_height first_layer_height
adaptive_slicing adaptive_slicing_quality match_horizontal_surfaces adaptive_slicing adaptive_slicing_quality match_horizontal_surfaces
perimeters spiral_vase perimeters spiral_vase
top_solid_layers bottom_solid_layers top_solid_layers min_shell_thickness bottom_solid_layers
extra_perimeters avoid_crossing_perimeters thin_walls overhangs extra_perimeters avoid_crossing_perimeters thin_walls overhangs
seam_position external_perimeters_first seam_position external_perimeters_first
fill_density fill_pattern top_infill_pattern bottom_infill_pattern fill_gaps fill_density fill_pattern top_infill_pattern bottom_infill_pattern fill_gaps
@ -521,6 +521,7 @@ sub build {
{ {
my $optgroup = $page->new_optgroup('Vertical shells'); my $optgroup = $page->new_optgroup('Vertical shells');
$optgroup->append_single_option_line('perimeters'); $optgroup->append_single_option_line('perimeters');
$optgroup->append_single_option_line('min_shell_thickness');
$optgroup->append_single_option_line('spiral_vase'); $optgroup->append_single_option_line('spiral_vase');
} }
{ {
@ -800,8 +801,9 @@ sub _update {
my $opt_key = $key; my $opt_key = $key;
$opt_key = "all_keys" if (length($key // '') == 0); $opt_key = "all_keys" if (length($key // '') == 0);
my $config = $self->{config}; my $config = $self->{config};
if (any { /$opt_key/ } qw(all_keys spiral_vase perimeters top_solid_layers fill_density support_material)) {
if ($config->spiral_vase && !($config->perimeters == 1 && $config->top_solid_layers == 0 && $config->fill_density == 0 && $config->support_material == 0)) { if (any { /$opt_key/ } qw(all_keys spiral_vase perimeters top_solid_layers fill_density support_material min_shell_thickness)) {
if ($config->spiral_vase && !($config->perimeters == 1 && $config->min_shell_thickness == 0 & $config->top_solid_layers == 0 && $config->fill_density == 0 && $config->support_material == 0)) {
my $dialog = Wx::MessageDialog->new($self, my $dialog = Wx::MessageDialog->new($self,
"The Spiral Vase mode requires:\n" "The Spiral Vase mode requires:\n"
. "- one perimeter\n" . "- one perimeter\n"
@ -813,6 +815,7 @@ sub _update {
if ($dialog->ShowModal() == wxID_YES) { if ($dialog->ShowModal() == wxID_YES) {
my $new_conf = Slic3r::Config->new; my $new_conf = Slic3r::Config->new;
$new_conf->set("perimeters", 1); $new_conf->set("perimeters", 1);
$new_conf->set("min_shell_thickness", 0);
$new_conf->set("top_solid_layers", 0); $new_conf->set("top_solid_layers", 0);
$new_conf->set("fill_density", 0); $new_conf->set("fill_density", 0);
$new_conf->set("support_material", 0); $new_conf->set("support_material", 0);
@ -874,8 +877,7 @@ sub _update {
} }
} }
my $have_perimeters = ($config->perimeters > 0) || ($config->min_shell_thickness > 0);
my $have_perimeters = $config->perimeters > 0;
if (any { /$opt_key/ } qw(all_keys perimeters)) { if (any { /$opt_key/ } qw(all_keys perimeters)) {
$self->get_field($_)->toggle($have_perimeters) $self->get_field($_)->toggle($have_perimeters)
for qw(extra_perimeters thin_walls overhangs seam_position external_perimeters_first for qw(extra_perimeters thin_walls overhangs seam_position external_perimeters_first
@ -917,7 +919,8 @@ sub _update {
$self->get_field('gap_fill_speed')->toggle($have_perimeters && $have_infill && $config->fill_gaps); $self->get_field('gap_fill_speed')->toggle($have_perimeters && $have_infill && $config->fill_gaps);
} }
my $have_top_solid_infill = $config->top_solid_layers > 0; my $have_top_solid_infill = ($config->top_solid_layers > 0) || ($config->min_shell_thickness > 0);
$self->get_field($_)->toggle($have_top_solid_infill) $self->get_field($_)->toggle($have_top_solid_infill)
for qw(top_infill_extrusion_width top_solid_infill_speed); for qw(top_infill_extrusion_width top_solid_infill_speed);

View File

@ -439,6 +439,7 @@ $j
--perimeters Number of perimeters/horizontal skins (range: 0+, default: $config->{perimeters}) --perimeters Number of perimeters/horizontal skins (range: 0+, default: $config->{perimeters})
--top-solid-layers Number of solid layers to do for top surfaces (range: 0+, default: $config->{top_solid_layers}) --top-solid-layers Number of solid layers to do for top surfaces (range: 0+, default: $config->{top_solid_layers})
--bottom-solid-layers Number of solid layers to do for bottom surfaces (range: 0+, default: $config->{bottom_solid_layers}) --bottom-solid-layers Number of solid layers to do for bottom surfaces (range: 0+, default: $config->{bottom_solid_layers})
--min-shell-thickness Minimum thickness of all solid shells (range: 0+, default: 0)
--solid-layers Shortcut for setting the two options above at once --solid-layers Shortcut for setting the two options above at once
--fill-density Infill density (range: 0%-100%, default: $config->{fill_density}%) --fill-density Infill density (range: 0%-100%, default: $config->{fill_density}%)
--fill-angle Infill angle in degrees (range: 0-90, default: $config->{fill_angle}) --fill-angle Infill angle in degrees (range: 0-90, default: $config->{fill_angle})

View File

@ -1,4 +1,4 @@
use Test::More tests => 63; use Test::More tests => 66;
use strict; use strict;
use warnings; use warnings;
@ -313,6 +313,54 @@ use Slic3r::Test;
ok !(grep { $_ % $config->perimeters } values %perimeters), 'no superfluous extra perimeters'; ok !(grep { $_ % $config->perimeters } values %perimeters), 'no superfluous extra perimeters';
} }
{
my $config = Slic3r::Config->new_from_defaults;
$config->set('skirts', 0);
$config->set('perimeters', 3);
$config->set('min_shell_thickness', 3);
$config->set('layer_height', 0.4);
$config->set('first_layer_height', 0.35);
$config->set('extra_perimeters', 0);
$config->set('first_layer_extrusion_width', 0.5);
$config->set('perimeter_extrusion_width', 0.5);
$config->set('external_perimeter_extrusion_width', 0.5);
$config->set('cooling', 0); # to prevent speeds from being altered
$config->set('first_layer_speed', '100%'); # to prevent speeds from being altered
$config->set('perimeter_speed', 99);
$config->set('external_perimeter_speed', 99);
$config->set('small_perimeter_speed', 99);
$config->set('thin_walls', 0);
my $test = sub {
my $print = Slic3r::Test::init_print('ipadstand', config => $config);
my %perimeters = (); # z => number of loops
my $in_loop = 0;
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
my ($self, $cmd, $args, $info) = @_;
if ($info->{extruding} && $info->{dist_XY} > 0 && ($args->{F} // $self->F) == $config->perimeter_speed*60) {
$perimeters{$self->Z}++ if !$in_loop;
$in_loop = 1;
} else {
$in_loop = 0;
}
});
ok !(grep { $_ % $config->min_shell_thickness/$config->perimeter_extrusion_width } values %perimeters), 'should be 6 perimeters';
};
$test->();
$config->set('first_layer_extrusion_width', 0.54);
$config->set('perimeter_extrusion_width', 0.54);
$config->set('external_perimeter_extrusion_width', 0.54);
$test->();
$config->set('first_layer_extrusion_width', 0.59);
$config->set('perimeter_extrusion_width', 0.51);
$config->set('external_perimeter_extrusion_width', 0.51);
$test->();
}
{ {
my $config = Slic3r::Config->new_from_defaults; my $config = Slic3r::Config->new_from_defaults;
$config->set('nozzle_diameter', [0.4]); $config->set('nozzle_diameter', [0.4]);

View File

@ -237,6 +237,7 @@ LayerRegion::prepare_fill_surfaces()
the only meaningful information returned by psPerimeters. */ the only meaningful information returned by psPerimeters. */
// if no solid layers are requested, turn top/bottom surfaces to internal // if no solid layers are requested, turn top/bottom surfaces to internal
if (this->region()->config.min_shell_thickness == 0) {
if (this->region()->config.top_solid_layers == 0) { if (this->region()->config.top_solid_layers == 0) {
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
if (surface->surface_type == stTop) { if (surface->surface_type == stTop) {
@ -254,6 +255,7 @@ LayerRegion::prepare_fill_surfaces()
surface->surface_type = stInternal; surface->surface_type = stInternal;
} }
} }
}
// turn too small internal regions into solid regions according to the user setting // turn too small internal regions into solid regions according to the user setting
const float &fill_density = this->region()->config.fill_density; const float &fill_density = this->region()->config.fill_density;

View File

@ -39,6 +39,9 @@ PerimeterGenerator::process()
coord_t min_spacing = pspacing * (1 - INSET_OVERLAP_TOLERANCE); coord_t min_spacing = pspacing * (1 - INSET_OVERLAP_TOLERANCE);
coord_t ext_min_spacing = ext_pspacing * (1 - INSET_OVERLAP_TOLERANCE); coord_t ext_min_spacing = ext_pspacing * (1 - INSET_OVERLAP_TOLERANCE);
// minimum shell thickness
coord_t min_shell_thickness = scale_(this->config->min_shell_thickness);
// prepare grown lower layer slices for overhang detection // prepare grown lower layer slices for overhang detection
if (this->lower_slices != NULL && this->config->overhangs) { if (this->lower_slices != NULL && this->config->overhangs) {
// We consider overhang any part where the entire nozzle diameter is not supported by the // We consider overhang any part where the entire nozzle diameter is not supported by the
@ -54,7 +57,20 @@ PerimeterGenerator::process()
for (Surfaces::const_iterator surface = this->slices->surfaces.begin(); for (Surfaces::const_iterator surface = this->slices->surfaces.begin();
surface != this->slices->surfaces.end(); ++surface) { surface != this->slices->surfaces.end(); ++surface) {
// detect how many perimeters must be generated for this island // detect how many perimeters must be generated for this island
const int loop_number = this->config->perimeters + surface->extra_perimeters -1; // 0-indexed loops int loops = this->config->perimeters + surface->extra_perimeters;
// If the user has defined a minimum shell thickness compute the number of loops needed to satisfy
if (min_shell_thickness > 0) {
int min_loops = 1;
min_loops += ceil(((float)min_shell_thickness-ext_pwidth)/pwidth);
if (loops < min_loops)
loops = min_loops;
}
const int loop_number = loops-1; // 0-indexed loops
Polygons gaps; Polygons gaps;

View File

@ -827,6 +827,16 @@ PrintConfigDef::PrintConfigDef()
def->max = 100; def->max = 100;
def->default_value = new ConfigOptionInt(35); def->default_value = new ConfigOptionInt(35);
def = this->add("min_shell_thickness", coFloat);
def->label = "Minimum shell thickness";
def->category = "Layers and Perimeters";
def->sidetext = "mm";
def->tooltip = "Alternative method of configuring perimeters and top/bottom layers. If this is above 0 extra perimeters and solid layers will be generated when necessary";
def->cli = "min-shell-thickness=f";
def->min = 0;
def->default_value = new ConfigOptionFloat(0);
def = this->add("min_layer_height", coFloats); def = this->add("min_layer_height", coFloats);
def->label = "Min"; def->label = "Min";
def->tooltip = "This is the lowest printable layer height for this extruder and limits the resolution for adaptive slicing. Typical values are 0.1 or 0.05."; def->tooltip = "This is the lowest printable layer height for this extruder and limits the resolution for adaptive slicing. Typical values are 0.1 or 0.05.";

View File

@ -255,6 +255,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig
ConfigOptionInt infill_every_layers; ConfigOptionInt infill_every_layers;
ConfigOptionFloatOrPercent infill_overlap; ConfigOptionFloatOrPercent infill_overlap;
ConfigOptionFloat infill_speed; ConfigOptionFloat infill_speed;
ConfigOptionFloat min_shell_thickness;
ConfigOptionBool overhangs; ConfigOptionBool overhangs;
ConfigOptionInt perimeter_extruder; ConfigOptionInt perimeter_extruder;
ConfigOptionFloatOrPercent perimeter_extrusion_width; ConfigOptionFloatOrPercent perimeter_extrusion_width;
@ -296,6 +297,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig
OPT_PTR(infill_every_layers); OPT_PTR(infill_every_layers);
OPT_PTR(infill_overlap); OPT_PTR(infill_overlap);
OPT_PTR(infill_speed); OPT_PTR(infill_speed);
OPT_PTR(min_shell_thickness);
OPT_PTR(overhangs); OPT_PTR(overhangs);
OPT_PTR(perimeter_extruder); OPT_PTR(perimeter_extruder);
OPT_PTR(perimeter_extrusion_width); OPT_PTR(perimeter_extrusion_width);

View File

@ -76,6 +76,7 @@ PrintRegion::invalidate_state_by_config(const PrintConfigBase &config)
for (const t_config_option_key &opt_key : diff) { for (const t_config_option_key &opt_key : diff) {
if (opt_key == "perimeters" if (opt_key == "perimeters"
|| opt_key == "extra_perimeters" || opt_key == "extra_perimeters"
|| opt_key == "min_shell_thickness"
|| opt_key == "gap_fill_speed" || opt_key == "gap_fill_speed"
|| opt_key == "overhangs" || opt_key == "overhangs"
|| opt_key == "first_layer_extrusion_width" || opt_key == "first_layer_extrusion_width"
@ -98,6 +99,7 @@ PrintRegion::invalidate_state_by_config(const PrintConfigBase &config)
|| opt_key == "solid_infill_every_layers" || opt_key == "solid_infill_every_layers"
|| opt_key == "bottom_solid_layers" || opt_key == "bottom_solid_layers"
|| opt_key == "top_solid_layers" || opt_key == "top_solid_layers"
|| opt_key == "min_shell_thickness"
|| opt_key == "infill_extruder" || opt_key == "infill_extruder"
|| opt_key == "solid_infill_extruder" || opt_key == "solid_infill_extruder"
|| opt_key == "infill_extrusion_width") { || opt_key == "infill_extrusion_width") {