diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 56279223a3..47a81394a5 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -991,39 +991,47 @@ sub discover_horizontal_shells { sub combine_infill { my $self = shift; - return unless defined first { $_->config->infill_every_layers > 1 && $_->config->fill_density > 0 } @{$self->print->regions}; - - my @layer_heights = map $_->height, @{$self->layers}; - + # work on each region separately for my $region_id (0 .. ($self->print->region_count-1)) { my $region = $self->print->regions->[$region_id]; my $every = $region->config->infill_every_layers; + next unless $every > 1 && $region->config->fill_density > 0; # limit the number of combined layers to the maximum height allowed by this regions' nozzle my $nozzle_diameter = $self->print->config->get_at('nozzle_diameter', $region->config->infill_extruder-1); # define the combinations - my @combine = (); # layer_id => thickness in layers + my %combine = (); # layer_idx => number of additional combined lower layers { my $current_height = my $layers = 0; - for my $layer_id (1 .. $#layer_heights) { - my $height = $self->get_layer($layer_id)->height; + for my $layer_idx (0 .. ($self->layer_count-1)) { + my $layer = $self->get_layer($layer_idx); + next if $layer->id == 0; # skip first print layer (which may not be first layer in array because of raft) + my $height = $layer->height; + # check whether the combination of this layer with the lower layers' buffer + # would exceed max layer height or max combined layer count if ($current_height + $height >= $nozzle_diameter || $layers >= $every) { - $combine[$layer_id-1] = $layers; + # append combination to lower layer + $combine{$layer_idx-1} = $layers; $current_height = $layers = 0; } $current_height += $height; $layers++; } + + # append lower layers (if any) to uppermost layer + $combine{$self->layer_count-1} = $layers; } - # skip bottom layer - for my $layer_id (1 .. $#combine) { - next unless ($combine[$layer_id] // 1) > 1; + # loop through layers to which we have assigned layers to combine + for my $layer_idx (sort keys %combine) { + next unless $combine{$layer_idx} > 1; + + # get all the LayerRegion objects to be combined my @layerms = map $self->get_layer($_)->regions->[$region_id], - ($layer_id - ($combine[$layer_id]-1) .. $layer_id); + ($layer_idx - ($combine{$layer_idx}-1) .. $layer_idx); # only combine internal infill for my $type (S_TYPE_INTERNAL) { @@ -1046,7 +1054,7 @@ sub combine_infill { Slic3r::debugf " combining %d %s regions from layers %d-%d\n", scalar(@$intersection), ($type == S_TYPE_INTERNAL ? 'internal' : 'internal-solid'), - $layer_id-($every-1), $layer_id; + $layer_idx-($every-1), $layer_idx; # $intersection now contains the regions that can be combined across the full amount of layers # so let's remove those areas from all layers @@ -1073,7 +1081,7 @@ sub combine_infill { )}; # apply surfaces back with adjusted depth to the uppermost layer - if ($layerm->id == $layer_id) { + if ($layerm->id == $self->get_layer($layer_idx)->id) { push @new_this_type, map Slic3r::Surface->new( expolygon => $_, diff --git a/t/combineinfill.t b/t/combineinfill.t index 62e22e6b28..5b51899eeb 100644 --- a/t/combineinfill.t +++ b/t/combineinfill.t @@ -11,37 +11,64 @@ use List::Util qw(first); use Slic3r; use Slic3r::Test; -plan tests => 2; +plan tests => 6; { + my $test = sub { + my ($config) = @_; + + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + ok my $gcode = Slic3r::Test::gcode($print), "infill_every_layers does not crash"; + + my $tool = undef; + my %layers = (); # layer_z => 1 + my %layer_infill = (); # layer_z => has_infill + Slic3r::GCode::Reader->new->parse($gcode, sub { + my ($self, $cmd, $args, $info) = @_; + + if ($cmd =~ /^T(\d+)/) { + $tool = $1; + } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0 && $tool != $config->support_material_extruder-1) { + $layer_infill{$self->Z} //= 0; + if ($tool == $config->infill_extruder-1) { + $layer_infill{$self->Z} = 1; + } + } + $layers{$args->{Z}} = 1 if $cmd eq 'G1' && $info->{dist_Z} > 0; + }); + + my $layers_with_perimeters = scalar(keys %layer_infill); + my $layers_with_infill = grep $_ > 0, values %layer_infill; + is scalar(keys %layers), $layers_with_perimeters+$config->raft_layers, 'expected number of layers'; + + # first infill layer is never combined, so we don't consider it + $layers_with_infill--; + $layers_with_perimeters--; + + # we expect that infill is generated for half the number of combined layers + # plus for each single layer that was not combined (remainder) + is $layers_with_infill, + int($layers_with_perimeters/$config->infill_every_layers) + ($layers_with_perimeters % $config->infill_every_layers), + 'infill is only present in correct number of layers'; + }; + my $config = Slic3r::Config->new_from_defaults; + $config->set('gcode_comments', 1); $config->set('layer_height', 0.2); $config->set('first_layer_height', 0.2); $config->set('nozzle_diameter', [0.5]); $config->set('infill_every_layers', 2); + $config->set('perimeter_extruder', 1); $config->set('infill_extruder', 2); + $config->set('support_material_extruder', 3); + $config->set('support_material_interface_extruder', 3); $config->set('top_solid_layers', 0); $config->set('bottom_solid_layers', 0); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - ok my $gcode = Slic3r::Test::gcode($print), "infill_every_layers does not crash"; + $test->($config); - my $tool = undef; - my %layer_infill = (); # layer_z => has_infill - Slic3r::GCode::Reader->new->parse($gcode, sub { - my ($self, $cmd, $args, $info) = @_; - - if ($cmd =~ /^T(\d+)/) { - $tool = $1; - } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) { - $layer_infill{$self->Z} //= 0; - if ($tool == $config->infill_extruder-1) { - $layer_infill{$self->Z} = 1; - } - } - }); - my $layers_with_infill = grep $_, values %layer_infill; - $layers_with_infill--; # first layer is never combined - is $layers_with_infill, scalar(keys %layer_infill)/2, 'infill is only present in correct number of layers'; + $config->set('skirts', 0); # prevent usage of perimeter_extruder in raft layers + $config->set('raft_layers', 5); + $test->($config); } # the following needs to be adapted to the new API