From 691c45d57fd1e8e0f898e0afde4528ea2760c64b Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Sun, 28 Jul 2013 10:56:41 +0200
Subject: [PATCH] Smarter handling of hollow prints. Optimization included

---
 lib/Slic3r/Print/Object.pm | 39 +++++++++++++++++++++-----------------
 t/shells.t                 |  8 +++++++-
 2 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index 9c639bce7..446a7c029 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -581,7 +581,7 @@ sub discover_horizontal_shells {
                     for grep $_->surface_type == S_TYPE_INTERNAL, @{$layerm->fill_surfaces};
             }
             
-            foreach my $type (S_TYPE_TOP, S_TYPE_BOTTOM) {
+            EXTERNAL: foreach my $type (S_TYPE_TOP, S_TYPE_BOTTOM) {
                 # find slices of current type for current layer
                 # get both slices and fill_surfaces before the former contains the perimeters area
                 # and the latter contains the enlarged external surfaces
@@ -592,7 +592,7 @@ sub discover_horizontal_shells {
                 my $solid_layers = ($type == S_TYPE_TOP)
                     ? $Slic3r::Config->top_solid_layers
                     : $Slic3r::Config->bottom_solid_layers;
-                for (my $n = $type == S_TYPE_TOP ? $i-1 : $i+1; 
+                NEIGHBOR: for (my $n = $type == S_TYPE_TOP ? $i-1 : $i+1; 
                         abs($n - $i) <= $solid_layers-1; 
                         $type == S_TYPE_TOP ? $n-- : $n++) {
                     
@@ -615,7 +615,7 @@ sub discover_horizontal_shells {
                         [ map $_->p, grep { $_->surface_type == S_TYPE_INTERNAL || $_->surface_type == S_TYPE_INTERNALSOLID } @neighbor_fill_surfaces ],
                         undef, 1,
                     );
-                    next if !@$new_internal_solid;
+                    next EXTERNAL if !@$new_internal_solid;
                     
                     # make sure the new internal solid is wide enough, as it might get collapsed when
                     # spacing is added in Fill.pm
@@ -627,21 +627,26 @@ sub discover_horizontal_shells {
                             1,
                         );
                         
-                        # if some parts are going to collapse, let's grow them and add the extra area to the neighbor layer
-                        # as well as to our original surfaces so that we support this additional area in the next shell too
+                        # if some parts are going to collapse, use a different strategy according to fill density
                         if (@$too_narrow) {
-                            # consider the actual fill area
-                            my @fill_boundaries = $Slic3r::Config->fill_density > 0
-                                ? @neighbor_fill_surfaces
-                                : grep $_->surface_type != S_TYPE_INTERNAL, @neighbor_fill_surfaces;
-                            
-                            # make sure our grown surfaces don't exceed the fill area
-                            my @grown = map @$_, @{intersection_ex(
-                                [ offset([ map @$_, @$too_narrow ], +$margin) ],
-                                [ map $_->p, @fill_boundaries ],
-                            )};
-                            $new_internal_solid = union_ex([ @grown, (map @$_, @$new_internal_solid) ]);
-                            $solid = union_ex([ @grown, (map @$_, @$solid) ]);
+                            if ($Slic3r::Config->fill_density > 0) {
+                                # if we have internal infill, grow the collapsing parts and add the extra area to 
+                                # the neighbor layer as well as to our original surfaces so that we support this 
+                                # additional area in the next shell too
+
+                                # make sure our grown surfaces don't exceed the fill area
+                                my @grown = map @$_, @{intersection_ex(
+                                    [ offset([ map @$_, @$too_narrow ], +$margin) ],
+                                    [ map $_->p, @neighbor_fill_surfaces ],
+                                )};
+                                $new_internal_solid = $solid = union_ex([ @grown, (map @$_, @$new_internal_solid) ]);
+                            } else {
+                                # if we're printing a hollow object, we discard such small parts
+                                $new_internal_solid = $solid = diff_ex(
+                                    [ map @$_, @$new_internal_solid ],
+                                    [ map @$_, @$too_narrow ],
+                                );
+                            }
                         }
                     }
                     
diff --git a/t/shells.t b/t/shells.t
index adb177c56..d84adb3e1 100644
--- a/t/shells.t
+++ b/t/shells.t
@@ -74,13 +74,19 @@ use Slic3r::Test;
     # (shape, extrusion_width) so that perimeters cover the bottom surfaces of
     # their lower layer - the test checks that shells are not generated on the
     # above layers (thus 'across' the shadow perimeter)
+    # the test is actually calibrated to leave a narrow bottom region for each
+    # layer - we test that in case of fill_density = 0 such narrow shells are 
+    # discarded instead of grown
     $config->set('perimeters', 1);
     $config->set('fill_density', 0);
     $config->set('cooling', 0);                 # prevent speed alteration
     $config->set('first_layer_speed', '100%');  # prevent speed alteration
-    $config->set('extrusion_width', 0.6);
+    $config->set('layer_height', 0.4);
+    $config->set('first_layer_height', '100%');
+    $config->set('extrusion_width', 0.5);
     $config->set('bottom_solid_layers', 3);
     $config->set('top_solid_layers', 0);
+    $config->set('solid_infill_speed', 99);
     
     my $print = Slic3r::Test::init_print('V', config => $config);
     my %layers = ();  # Z => 1