diff --git a/README.md b/README.md index 46c19db72..db1295993 100644 --- a/README.md +++ b/README.md @@ -333,6 +333,8 @@ The author of the Silk icon set is Mark James. Set a different extrusion width for first layer --perimeter-extrusion-width Set a different extrusion width for perimeters + --external-perimeter-extrusion-width + Set a different extrusion width for external perimeters --infill-extrusion-width Set a different extrusion width for infill --solid-infill-extrusion-width diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index a7c8312c5..fed894e97 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -4,7 +4,8 @@ use warnings; use parent qw(Exporter); -our @EXPORT_OK = qw(FLOW_ROLE_PERIMETER FLOW_ROLE_INFILL FLOW_ROLE_SOLID_INFILL +our @EXPORT_OK = qw(FLOW_ROLE_EXTERNAL_PERIMETER FLOW_ROLE_PERIMETER FLOW_ROLE_INFILL + FLOW_ROLE_SOLID_INFILL FLOW_ROLE_TOP_SOLID_INFILL FLOW_ROLE_SUPPORT_MATERIAL FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE); our %EXPORT_TAGS = (roles => \@EXPORT_OK); diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 6cc637d71..15c1321fd 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -544,7 +544,7 @@ sub build { { title => 'Extrusion width', label_width => 180, - options => [qw(extrusion_width first_layer_extrusion_width perimeter_extrusion_width infill_extrusion_width solid_infill_extrusion_width top_infill_extrusion_width support_material_extrusion_width)], + options => [qw(extrusion_width first_layer_extrusion_width perimeter_extrusion_width external_perimeter_extrusion_width infill_extrusion_width solid_infill_extrusion_width top_infill_extrusion_width support_material_extrusion_width)], }, { title => 'Flow', diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 2a3fe12f8..42efb5bd4 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -62,12 +62,23 @@ sub flow { sub make_perimeters { my $self = shift; + # external perimeters + my $ext_perimeter_flow = $self->flow(FLOW_ROLE_EXTERNAL_PERIMETER); + my $ext_mm3_per_mm = $ext_perimeter_flow->mm3_per_mm($self->height); + my $ext_pwidth = $ext_perimeter_flow->scaled_width; + my $ext_pspacing = $ext_perimeter_flow->scaled_spacing; + + # other perimeters my $perimeter_flow = $self->flow(FLOW_ROLE_PERIMETER); my $mm3_per_mm = $perimeter_flow->mm3_per_mm($self->height); - my $overhang_flow = $self->region->flow(FLOW_ROLE_PERIMETER, -1, 1, 0, undef, $self->layer->object); - my $mm3_per_mm_overhang = $overhang_flow->mm3_per_mm(-1); my $pwidth = $perimeter_flow->scaled_width; my $pspacing = $perimeter_flow->scaled_spacing; + + # overhang perimeters + my $overhang_flow = $self->region->flow(FLOW_ROLE_PERIMETER, -1, 1, 0, undef, $self->layer->object); + my $mm3_per_mm_overhang = $overhang_flow->mm3_per_mm(-1); + + # solid infill my $solid_infill_flow = $self->flow(FLOW_ROLE_SOLID_INFILL); my $ispacing = $solid_infill_flow->scaled_spacing; my $gap_area_threshold = $pwidth ** 2; @@ -77,7 +88,8 @@ sub make_perimeters { # with some tolerance in order to avoid triggering medial axis when # some squishing might work. Loops are still spaced by the entire # flow spacing; this only applies to collapsing parts. - my $min_spacing = $pspacing * (1 - &Slic3r::INSET_OVERLAP_TOLERANCE); + my $min_spacing = $pspacing * (1 - &Slic3r::INSET_OVERLAP_TOLERANCE); + my $ext_min_spacing = $ext_pspacing * (1 - &Slic3r::INSET_OVERLAP_TOLERANCE); $self->perimeters->clear; $self->fill_surfaces->clear; @@ -101,26 +113,30 @@ sub make_perimeters { my @offsets = (); if ($i == 1) { # the minimum thickness of a single loop is: - # width/2 + spacing/2 + spacing/2 + width/2 + # ext_width/2 + ext_spacing/2 + spacing/2 + width/2 @offsets = @{offset2( \@last, - -(0.5*$pwidth + 0.5*$min_spacing - 1), - +(0.5*$min_spacing - 1), + -(0.5*$ext_pwidth + 0.5*$ext_min_spacing - 1), + +(0.5*$ext_min_spacing - 1), )}; # look for thin walls if ($self->config->thin_walls) { my $diff = diff_ex( \@last, - offset(\@offsets, +0.5*$pwidth), + offset(\@offsets, +0.5*$ext_pwidth), 1, # medial axis requires non-overlapping geometry ); push @thin_walls, @$diff; } } else { + my $distance = ($i == 2) + ? (0.5*$ext_pspacing + 0.5*$pspacing) + : (1.0*$pspacing); + @offsets = @{offset2( \@last, - -(1.0*$pspacing + 0.5*$min_spacing - 1), + -($distance + 0.5*$min_spacing - 1), +(0.5*$min_spacing - 1), )}; @@ -283,8 +299,8 @@ sub make_perimeters { push @paths, Slic3r::ExtrusionPath->new( polyline => $polyline, role => $role, - mm3_per_mm => $mm3_per_mm, - width => $perimeter_flow->width, + mm3_per_mm => ($is_external ? $ext_mm3_per_mm : $mm3_per_mm), + width => ($is_external ? $ext_perimeter_flow->width : $perimeter_flow->width), height => $self->height, ); } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index e36ce607d..ab75a5c8c 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -835,6 +835,8 @@ sub write_gcode { my $first_object = $self->objects->[0]; my $layer_height = $first_object->config->layer_height; for my $region_id (0..$#{$self->regions}) { + printf $fh "; external perimeters extrusion width = %.2fmm\n", + $self->regions->[$region_id]->flow(FLOW_ROLE_EXTERNAL_PERIMETER, $layer_height, 0, 0, undef, $first_object)->width; printf $fh "; perimeters extrusion width = %.2fmm\n", $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 0, undef, $first_object)->width; printf $fh "; infill extrusion width = %.2fmm\n", diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 127bf5a82..7918871d4 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -372,11 +372,12 @@ sub make_perimeters { my $layerm = $self->layers->[$i]->regions->[$region_id]; my $upper_layerm = $self->layers->[$i+1]->regions->[$region_id]; my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing; + my $ext_perimeter_spacing = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_spacing; my $overlap = $perimeter_spacing; # one perimeter my $diff = diff( - offset([ map @{$_->expolygon}, @{$layerm->slices} ], -($region_perimeters * $perimeter_spacing)), + offset([ map @{$_->expolygon}, @{$layerm->slices} ], -($ext_perimeter_spacing + ($region_perimeters-1) * $perimeter_spacing)), offset([ map @{$_->expolygon}, @{$upper_layerm->slices} ], -$overlap), ); next if !@$diff; @@ -453,7 +454,7 @@ sub detect_surfaces_type { ); # collapse very narrow parts (using the safety offset in the diff is not enough) - my $offset = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_width / 10; + my $offset = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width / 10; return map Slic3r::Surface->new(expolygon => $_, surface_type => $result_type), @{ offset2_ex($diff, -$offset, +$offset) }; }; @@ -768,7 +769,7 @@ sub discover_horizontal_shells { # than a perimeter width, since it's probably just crossing a sloping wall # and it's not wanted in a hollow print even if it would make sense when # obeying the solid shell count option strictly (DWIM!) - my $margin = $neighbor_layerm->flow(FLOW_ROLE_PERIMETER)->scaled_width; + my $margin = $neighbor_layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width; my $too_narrow = diff( $new_internal_solid, offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5), diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index b54775976..58152c341 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -23,6 +23,8 @@ sub flow { # (might be an absolute value, or a percent value, or zero for auto) if ($first_layer && $self->print->config->first_layer_extrusion_width) { $config_width = $self->print->config->first_layer_extrusion_width; + } elsif ($role == FLOW_ROLE_EXTERNAL_PERIMETER) { + $config_width = $self->config->external_perimeter_extrusion_width; } elsif ($role == FLOW_ROLE_PERIMETER) { $config_width = $self->config->perimeter_extrusion_width; } elsif ($role == FLOW_ROLE_INFILL) { @@ -42,7 +44,7 @@ sub flow { # get the configured nozzle_diameter for the extruder associated # to the flow role requested my $extruder; # 1-based - if ($role == FLOW_ROLE_PERIMETER) { + if ($role == FLOW_ROLE_PERIMETER || $role == FLOW_ROLE_EXTERNAL_PERIMETER) { $extruder = $self->config->perimeter_extruder; } elsif ($role == FLOW_ROLE_INFILL || $role == FLOW_ROLE_SOLID_INFILL || $role == FLOW_ROLE_TOP_SOLID_INFILL) { $extruder = $self->config->infill_extruder; diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index fa941ad92..a4216c09f 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -127,7 +127,7 @@ sub contact_area { } else { my $lower_layer = $object->layers->[$layer_id-1]; foreach my $layerm (@{$layer->regions}) { - my $fw = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_width; + my $fw = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width; my $diff; # If a threshold angle was specified, use a different logic for detecting overhangs. diff --git a/slic3r.pl b/slic3r.pl index a5332504e..578c6a4ae 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -456,6 +456,8 @@ $j Set a different extrusion width for first layer --perimeter-extrusion-width Set a different extrusion width for perimeters + --external-perimeter-extrusion-width + Set a different extrusion width for external perimeters --infill-extrusion-width Set a different extrusion width for infill --solid-infill-extrusion-width diff --git a/t/fill.t b/t/fill.t index dc1606a20..7da926d43 100644 --- a/t/fill.t +++ b/t/fill.t @@ -275,6 +275,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) { $config->set('nozzle_diameter', [0.35]); $config->set('infill_extruder', 2); $config->set('infill_extrusion_width', 0.52); + $config->set('first_layer_extrusion_width', 0); my $print = Slic3r::Test::init_print('A', config => $config); my %infill = (); # Z => [ Line, Line ... ] @@ -298,7 +299,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) { my $grow_d = scale($config->infill_extrusion_width)/2; my $layer0_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.2} } ]); my $layer1_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.4} } ]); - my $diff = [ grep $_->area >= 2*$grow_d**2, @{diff_ex($layer0_infill, $layer1_infill)} ]; + my $diff = [ grep $_->area >= 4*($grow_d**2), @{diff_ex($layer0_infill, $layer1_infill)} ]; is scalar(@$diff), 0, 'no missing parts in solid shell when fill_density is 0'; } diff --git a/xs/src/Flow.cpp b/xs/src/Flow.cpp index 6f66f70d3..e03cfdea7 100644 --- a/xs/src/Flow.cpp +++ b/xs/src/Flow.cpp @@ -65,7 +65,7 @@ Flow::_width(FlowRole role, float nozzle_diameter, float height, float bridge_fl float min = nozzle_diameter * 1.05; float max = -1; - if (role == frPerimeter || role == frSupportMaterial) { + if (role == frExternalPerimeter || role == frSupportMaterial) { min = max = nozzle_diameter; } else if (role != frInfill) { // do not limit width for sparse infill so that we use full native flow for it diff --git a/xs/src/Flow.hpp b/xs/src/Flow.hpp index e98173dc9..47cea8b19 100644 --- a/xs/src/Flow.hpp +++ b/xs/src/Flow.hpp @@ -11,6 +11,7 @@ namespace Slic3r { #define OVERLAP_FACTOR 1.0 enum FlowRole { + frExternalPerimeter, frPerimeter, frInfill, frSolidInfill, diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index 0aef25c09..3f43ebfce 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -172,6 +172,13 @@ class PrintConfigDef Options["end_gcode"].full_width = true; Options["end_gcode"].height = 120; + Options["external_perimeter_extrusion_width"].type = coFloatOrPercent; + Options["external_perimeter_extrusion_width"].label = "External perimeters"; + Options["external_perimeter_extrusion_width"].category = "Extrusion Width"; + Options["external_perimeter_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for external perimeters. If left zero, an automatic value will be used that maximizes accuracy of the external visible surfaces. If expressed as percentage (for example 200%) it will be computed over layer height."; + Options["external_perimeter_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; + Options["external_perimeter_extrusion_width"].cli = "external-perimeter-extrusion-width=s"; + Options["external_perimeter_speed"].type = coFloatOrPercent; Options["external_perimeter_speed"].label = "External perimeters"; Options["external_perimeter_speed"].category = "Speed"; @@ -555,7 +562,7 @@ class PrintConfigDef Options["perimeter_extrusion_width"].type = coFloatOrPercent; Options["perimeter_extrusion_width"].label = "Perimeters"; Options["perimeter_extrusion_width"].category = "Extrusion Width"; - Options["perimeter_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for perimeters. You may want to use thinner extrudates to get more accurate surfaces. If expressed as percentage (for example 90%) it will be computed over layer height."; + Options["perimeter_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for perimeters. You may want to use thinner extrudates to get more accurate surfaces. If expressed as percentage (for example 200%) it will be computed over layer height."; Options["perimeter_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; Options["perimeter_extrusion_width"].cli = "perimeter-extrusion-width=s"; Options["perimeter_extrusion_width"].aliases.push_back("perimeters_extrusion_width"); @@ -1088,6 +1095,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig public: ConfigOptionInt bottom_solid_layers; ConfigOptionFloat bridge_speed; + ConfigOptionFloatOrPercent external_perimeter_extrusion_width; ConfigOptionFloatOrPercent external_perimeter_speed; ConfigOptionBool extra_perimeters; ConfigOptionInt fill_angle; @@ -1117,6 +1125,8 @@ class PrintRegionConfig : public virtual StaticPrintConfig PrintRegionConfig() : StaticPrintConfig() { this->bottom_solid_layers.value = 3; this->bridge_speed.value = 60; + this->external_perimeter_extrusion_width.value = 0; + this->external_perimeter_extrusion_width.percent = false; this->external_perimeter_speed.value = 70; this->external_perimeter_speed.percent = true; this->extra_perimeters.value = true; @@ -1155,6 +1165,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig ConfigOption* option(const t_config_option_key opt_key, bool create = false) { if (opt_key == "bottom_solid_layers") return &this->bottom_solid_layers; if (opt_key == "bridge_speed") return &this->bridge_speed; + if (opt_key == "external_perimeter_extrusion_width") return &this->external_perimeter_extrusion_width; if (opt_key == "external_perimeter_speed") return &this->external_perimeter_speed; if (opt_key == "extra_perimeters") return &this->extra_perimeters; if (opt_key == "fill_angle") return &this->fill_angle; diff --git a/xs/xsp/Flow.xsp b/xs/xsp/Flow.xsp index 0fe645450..3663a29d5 100644 --- a/xs/xsp/Flow.xsp +++ b/xs/xsp/Flow.xsp @@ -65,6 +65,7 @@ _new_from_spacing(CLASS, spacing, nozzle_diameter, height, bridge) IV _constant() ALIAS: + FLOW_ROLE_EXTERNAL_PERIMETER = frExternalPerimeter FLOW_ROLE_PERIMETER = frPerimeter FLOW_ROLE_INFILL = frInfill FLOW_ROLE_SOLID_INFILL = frSolidInfill