diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 1d88b21c7..a35d99184 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -206,9 +206,10 @@ sub make_perimeters { $hole->reverse; } - # create other offsets + # generate perimeters inwards + my $loop_number = $Slic3r::perimeters + ($surface->additional_inner_perimeters || 0); push @perimeters, []; - for (my $loop = 0; $loop < $Slic3r::perimeters; $loop++) { + for (my $loop = 0; $loop < $loop_number; $loop++) { # offsetting a polygon can result in one or many offset polygons @last_offsets = map $_->offset_ex(-$distance), @last_offsets if $distance; last if !@last_offsets; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 75bf7fb94..2a1aad22f 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -166,7 +166,7 @@ sub export_gcode { # this will add a set of extrusion loops to each layer # as well as generate infill boundaries $status_cb->(20, "Generating perimeters"); - $_->make_perimeters for map @{$_->layers}, @{$self->objects}; + $_->make_perimeters for @{$self->objects}; # this will clip $layer->surfaces to the infill boundaries # and split them in top/bottom/internal surfaces; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index fea9ec586..5e403f8fd 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -157,6 +157,67 @@ sub cleanup { @{$self->layers} = (); } +sub make_perimeters { + my $self = shift; + + # compare each layer to the one below, and mark those slices needing + # one additional inner perimeter, like the top of domed objects- + + # this algorithm makes sure that almost one perimeter is overlapping: + my $overlap = $Slic3r::flow_spacing; + + for my $layer_id (0 .. $self->layer_count-2) { + my $layer = $self->layers->[$layer_id]; + my $upper_layer = $self->layers->[$layer_id+1]; + + # compute polygons representing the thickness of the first external perimeter of + # the upper layer slices + my $upper = diff_ex( + [ map @$_, map $_->expolygon->offset_ex(+ 0.5 * scale $Slic3r::flow_spacing), @{$upper_layer->slices} ], + [ map @$_, map $_->expolygon->offset_ex(- scale($overlap) + (0.5 * scale $Slic3r::flow_spacing)), @{$upper_layer->slices} ], + ); + next if !@$upper; + + # we need to limit our detection to the areas which would actually benefit from + # more perimeters. so, let's compute the area we want to ignore + my $ignore = []; + { + my $diff = diff_ex( + [ map @$_, map $_->expolygon->offset_ex(- ($Slic3r::perimeters-0.5) * scale $Slic3r::flow_spacing), @{$layer->slices} ], + [ map @{$_->expolygon}, @{$upper_layer->slices} ], + ); + $ignore = [ map @$_, map $_->offset_ex(scale $Slic3r::flow_spacing), @$diff ]; + } + + foreach my $slice (@{$layer->slices}) { + my $hypothetical_perimeter_num = $Slic3r::perimeters + 1; + CYCLE: while (1) { + # compute polygons representing the thickness of the hypotetical new internal perimeter + # of our slice + my $hypothetical_perimeter; + { + my $outer = [ map @$_, $slice->expolygon->offset_ex(- ($hypothetical_perimeter_num-1.5) * scale $Slic3r::flow_spacing) ]; + last CYCLE if !@$outer; + my $inner = [ map @$_, $slice->expolygon->offset_ex(- ($hypothetical_perimeter_num-0.5) * scale $Slic3r::flow_spacing) ]; + last CYCLE if !@$inner; + $hypothetical_perimeter = diff_ex($outer, $inner); + } + last CYCLE if !@$hypothetical_perimeter; + + + my $intersection = intersection_ex([ map @$_, @$upper ], [ map @$_, @$hypothetical_perimeter ]); + $intersection = diff_ex([ map @$_, @$intersection ], $ignore) if @$ignore; + last CYCLE if !@{ $intersection }; + Slic3r::debugf " adding one more perimeter at layer %d\n", $layer_id; + $slice->additional_inner_perimeters(($slice->additional_inner_perimeters || 0) + 1); + $hypothetical_perimeter_num++; + } + } + } + + $_->make_perimeters for @{$self->layers}; +} + sub detect_surfaces_type { my $self = shift; Slic3r::debugf "Detecting solid surfaces...\n"; diff --git a/lib/Slic3r/Surface.pm b/lib/Slic3r/Surface.pm index 783501dfc..95f3da490 100644 --- a/lib/Slic3r/Surface.pm +++ b/lib/Slic3r/Surface.pm @@ -6,13 +6,14 @@ use constant S_EXPOLYGON => 0; use constant S_SURFACE_TYPE => 1; use constant S_DEPTH_LAYERS => 2; use constant S_BRIDGE_ANGLE => 3; +use constant S_ADDITIONAL_INNER_PERIMETERS => 4; sub new { my $class = shift; my %args = @_; my $self = [ - map delete $args{$_}, qw(expolygon surface_type depth_layers bridge_angle), + map delete $args{$_}, qw(expolygon surface_type depth_layers bridge_angle additional_inner_perimeters), ]; $self->[S_DEPTH_LAYERS] //= 1; #/ @@ -24,6 +25,7 @@ sub expolygon { $_[0][S_EXPOLYGON] } sub surface_type { $_[0][S_SURFACE_TYPE] = $_[1] if $_[1]; $_[0][S_SURFACE_TYPE] } sub depth_layers { $_[0][S_DEPTH_LAYERS] } # this integer represents the thickness of the surface expressed in layers sub bridge_angle { $_[0][S_BRIDGE_ANGLE] } +sub additional_inner_perimeters { $_[0][S_ADDITIONAL_INNER_PERIMETERS] = $_[1] if $_[1]; $_[0][S_ADDITIONAL_INNER_PERIMETERS] } # delegate handles sub encloses_point { $_[0]->expolygon->encloses_point }