Browse Source

Bugfix: infill was not correctly generated when infill_every_layers was used along with raft_layers. Includes regression test. #2396

Alessandro Ranellucci 10 years ago
parent
commit
9a9ba02d85
2 changed files with 69 additions and 34 deletions
  1. 22 14
      lib/Slic3r/Print/Object.pm
  2. 47 20
      t/combineinfill.t

+ 22 - 14
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        => $_,

+ 47 - 20
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