From b4be61b703a33c1b3f579c143a520680caaaed38 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Thu, 4 Apr 2013 01:17:44 +0200
Subject: [PATCH] Bugfix: time estimates for cooling were computed for each
 object separately instead of the whole actual layer. #1071

---
 MANIFEST                          |  1 +
 lib/Slic3r.pm                     |  1 +
 lib/Slic3r/GCode/CoolingBuffer.pm | 77 +++++++++++++++++++++++++++++++
 lib/Slic3r/Print.pm               | 57 ++++++-----------------
 4 files changed, 93 insertions(+), 43 deletions(-)
 create mode 100644 lib/Slic3r/GCode/CoolingBuffer.pm

diff --git a/MANIFEST b/MANIFEST
index 6e59ce2e5..e7498665e 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -24,6 +24,7 @@ lib/Slic3r/Format/AMF/Parser.pm
 lib/Slic3r/Format/OBJ.pm
 lib/Slic3r/Format/STL.pm
 lib/Slic3r/GCode.pm
+lib/Slic3r/GCode/CoolingBuffer.pm
 lib/Slic3r/GCode/MotionPlanner.pm
 lib/Slic3r/Geometry.pm
 lib/Slic3r/Geometry/Clipper.pm
diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm
index 478165bc3..eddc2939b 100644
--- a/lib/Slic3r.pm
+++ b/lib/Slic3r.pm
@@ -45,6 +45,7 @@ use Slic3r::Format::AMF;
 use Slic3r::Format::OBJ;
 use Slic3r::Format::STL;
 use Slic3r::GCode;
+use Slic3r::GCode::CoolingBuffer;
 use Slic3r::GCode::MotionPlanner;
 use Slic3r::Geometry qw(PI);
 use Slic3r::Layer;
diff --git a/lib/Slic3r/GCode/CoolingBuffer.pm b/lib/Slic3r/GCode/CoolingBuffer.pm
new file mode 100644
index 000000000..d88e6c2e0
--- /dev/null
+++ b/lib/Slic3r/GCode/CoolingBuffer.pm
@@ -0,0 +1,77 @@
+package Slic3r::GCode::CoolingBuffer;
+use Moo;
+
+has 'config'    => (is => 'ro', required => 1);
+has 'gcodegen'  => (is => 'ro', required => 1);
+has 'gcode'     => (is => 'rw', default => sub {""});
+has 'layer_id'  => (is => 'rw');
+has 'last_z'    => (is => 'rw');
+has 'min_print_speed' => (is => 'lazy');
+
+sub _build_min_print_speed {
+    my $self = shift;
+    return 60 * $self->config->min_print_speed;
+}
+
+sub append {
+    my $self = shift;
+    my ($gcode, $layer) = @_;
+    
+    my $return = "";
+    if (defined $self->last_z && $self->last_z != $layer->print_z) {
+        $return = $self->flush;
+        $self->gcodegen->elapsed_time(0);
+    }
+    
+    $self->layer_id($layer->id);
+    $self->last_z($layer->print_z);
+    $self->gcode($self->gcode . $gcode);
+    
+    return $return;
+}
+
+sub flush {
+    my $self = shift;
+    
+    my $gcode = $self->gcode;
+    $self->gcode("");
+    
+    my $fan_speed = $self->config->fan_always_on ? $self->config->min_fan_speed : 0;
+    my $speed_factor = 1;
+    if ($self->config->cooling) {
+        my $layer_time = $self->gcodegen->elapsed_time;
+        Slic3r::debugf "Layer %d estimated printing time: %d seconds\n", $self->layer_id, $layer_time;
+        if ($layer_time < $self->config->slowdown_below_layer_time) {
+            $fan_speed = $self->config->max_fan_speed;
+            $speed_factor = $layer_time / $self->config->slowdown_below_layer_time;
+        } elsif ($layer_time < $self->config->fan_below_layer_time) {
+            $fan_speed = $self->config->max_fan_speed - ($self->config->max_fan_speed - $self->config->min_fan_speed)
+                * ($layer_time - $self->config->slowdown_below_layer_time)
+                / ($self->config->fan_below_layer_time - $self->config->slowdown_below_layer_time); #/
+        }
+        Slic3r::debugf "  fan = %d%%, speed = %d%%\n", $fan_speed, $speed_factor * 100;
+        
+        if ($speed_factor < 1) {
+            my $dec = $self->gcodegen->dec;
+            $gcode =~ s/^(?=.*? [XY])(?=.*? E)(?!;_WIPE)(?<!;_BRIDGE_FAN_START\n)(G1 .*?F)(\d+(?:\.\d+)?)/
+                my $new_speed = $2 * $speed_factor;
+                $1 . sprintf("%.${dec}f", $new_speed < $self->min_print_speed ? $self->min_print_speed : $new_speed)
+                /gexm;
+        }
+        $fan_speed = 0 if $self->layer_id < $self->config->disable_fan_first_layers;
+    }
+    $gcode = $self->gcodegen->set_fan($fan_speed) . $gcode;
+    
+    # bridge fan speed
+    if (!$self->config->cooling || $self->config->bridge_fan_speed == 0 || $self->layer_id < $self->config->disable_fan_first_layers) {
+        $gcode =~ s/^;_BRIDGE_FAN_(?:START|END)\n//gm;
+    } else {
+        $gcode =~ s/^;_BRIDGE_FAN_START\n/ $self->gcodegen->set_fan($self->config->bridge_fan_speed, 1) /gmex;
+        $gcode =~ s/^;_BRIDGE_FAN_END\n/ $self->gcodegen->set_fan($fan_speed, 1) /gmex;
+    }
+    $gcode =~ s/;_WIPE//g;
+    
+    return $gcode;
+}
+
+1;
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index 64747ca6f..189b665bd 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -716,8 +716,6 @@ sub write_gcode {
         multiple_extruders  => (@{$self->extruders} > 1),
         layer_count         => $self->layer_count,
     );
-    my $min_print_speed = 60 * $Slic3r::Config->min_print_speed;
-    my $dec = $gcodegen->dec;
     print $fh "G21 ; set units to millimeters\n";
     print $fh $gcodegen->set_fan(0, 1) if $Slic3r::Config->cooling && $Slic3r::Config->disable_fan_first_layers;
     
@@ -801,7 +799,6 @@ sub write_gcode {
         
         # set new layer, but don't move Z as support material contact areas may need an intermediate one
         $gcode .= $gcodegen->change_layer($layer);
-        $gcodegen->elapsed_time(0);
         
         # prepare callback to call as soon as a Z command is generated
         $gcodegen->move_z_callback(sub {
@@ -943,42 +940,6 @@ sub write_gcode {
                 }
             }
         }
-        return if !$gcode;
-        
-        my $fan_speed = $Slic3r::Config->fan_always_on ? $Slic3r::Config->min_fan_speed : 0;
-        my $speed_factor = 1;
-        if ($Slic3r::Config->cooling) {
-            my $layer_time = $gcodegen->elapsed_time;
-            Slic3r::debugf "Layer %d estimated printing time: %d seconds\n", $layer->id, $layer_time;
-            if ($layer_time < $Slic3r::Config->slowdown_below_layer_time) {
-                $fan_speed = $Slic3r::Config->max_fan_speed;
-                $speed_factor = $layer_time / $Slic3r::Config->slowdown_below_layer_time;
-            } elsif ($layer_time < $Slic3r::Config->fan_below_layer_time) {
-                $fan_speed = $Slic3r::Config->max_fan_speed - ($Slic3r::Config->max_fan_speed - $Slic3r::Config->min_fan_speed)
-                    * ($layer_time - $Slic3r::Config->slowdown_below_layer_time)
-                    / ($Slic3r::Config->fan_below_layer_time - $Slic3r::Config->slowdown_below_layer_time); #/
-            }
-            Slic3r::debugf "  fan = %d%%, speed = %d%%\n", $fan_speed, $speed_factor * 100;
-            
-            if ($speed_factor < 1) {
-                $gcode =~ s/^(?=.*? [XY])(?=.*? E)(?!;_WIPE)(?<!;_BRIDGE_FAN_START\n)(G1 .*?F)(\d+(?:\.\d+)?)/
-                    my $new_speed = $2 * $speed_factor;
-                    $1 . sprintf("%.${dec}f", $new_speed < $min_print_speed ? $min_print_speed : $new_speed)
-                    /gexm;
-            }
-            $fan_speed = 0 if $layer->id < $Slic3r::Config->disable_fan_first_layers;
-        }
-        $gcode = $gcodegen->set_fan($fan_speed) . $gcode;
-        
-        # bridge fan speed
-        if (!$Slic3r::Config->cooling || $Slic3r::Config->bridge_fan_speed == 0 || $layer->id < $Slic3r::Config->disable_fan_first_layers) {
-            $gcode =~ s/^;_BRIDGE_FAN_(?:START|END)\n//gm;
-        } else {
-            $gcode =~ s/^;_BRIDGE_FAN_START\n/ $gcodegen->set_fan($Slic3r::Config->bridge_fan_speed, 1) /gmex;
-            $gcode =~ s/^;_BRIDGE_FAN_END\n/ $gcodegen->set_fan($fan_speed, 1) /gmex;
-        }
-        $gcode =~ s/;_WIPE//g;
-        
         return $gcode;
     };
     
@@ -1001,6 +962,11 @@ sub write_gcode {
                     print $fh $gcodegen->G0(Slic3r::Point->new(0,0), undef, 0, 'move to origin position for next object');
                 }
                 
+                my $buffer = Slic3r::GCode::CoolingBuffer->new(
+                    config      => $Slic3r::Config,
+                    gcodegen    => $gcodegen,
+                );
+                
                 for my $layer (@{$self->objects->[$obj_idx]->layers}) {
                     # if we are printing the bottom layer of an object, and we have already finished
                     # another one, set first layer temperatures. this happens before the Z move
@@ -1010,15 +976,20 @@ sub write_gcode {
                             if $Slic3r::Config->first_layer_bed_temperature;
                         $print_first_layer_temperature->();
                     }
-                    print $fh $extrude_layer->($layer, [$copy]);
+                    print $fh $buffer->append($extrude_layer->($layer, [$copy]), $layer);
                 }
+                print $fh $buffer->flush;
                 $finished_objects++;
             }
         }
     } else {
-        print $fh $extrude_layer->($_, $_->object->copies)
-            for sort { $a->print_z <=> $b->print_z }
-                map @{$_->layers}, @{$self->objects};
+        my $buffer = Slic3r::GCode::CoolingBuffer->new(
+            config      => $Slic3r::Config,
+            gcodegen    => $gcodegen,
+        );
+        print $fh $buffer->append($extrude_layer->($_, $_->object->copies), $_)
+            for sort { $a->print_z <=> $b->print_z } map @{$_->layers}, @{$self->objects};
+        print $fh $buffer->flush;
     }
     
     # save statistic data