diff --git a/t/cooling.t b/t/cooling.t index a413a2b332..80819bdcb1 100644 --- a/t/cooling.t +++ b/t/cooling.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 12; +plan tests => 13; BEGIN { use FindBin; @@ -10,7 +10,7 @@ BEGIN { use local::lib "$FindBin::Bin/../local-lib"; } -use List::Util qw(first); +use List::Util qw(none all); use Slic3r; use Slic3r::Test; @@ -141,21 +141,33 @@ $config->set('disable_fan_first_layers', 0); $config->set('slowdown_below_layer_time', 10); $config->set('min_print_speed', 0); $config->set('start_gcode', ''); + $config->set('first_layer_speed', '100%'); + $config->set('external_perimeter_speed', 99); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); my @layer_times = (0); # in seconds + my %layer_external = (); # z => 1 Slic3r::GCode::Reader->new->parse(my $gcode = Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; if ($cmd eq 'G1') { if ($info->{dist_Z}) { push @layer_times, 0; + $layer_external{ $args->{Z} } = 0; } $layer_times[-1] += abs($info->{dist_XY} || $info->{dist_E} || $info->{dist_Z} || 0) / ($args->{F} // $self->F) * 60; + if ($args->{F} && $args->{F} == $config->external_perimeter_speed*60) { + $layer_external{ $self->Z }++; + } } }); - my $all_below = !defined first { $_ > 0 && $_ < $config->slowdown_below_layer_time } @layer_times; + @layer_times = grep $_, @layer_times; + my $all_below = none { $_ < $config->slowdown_below_layer_time } @layer_times; ok $all_below, 'slowdown_below_layer_time is honored'; + + # check that all layers have at least one unaltered external perimeter speed + my $external = all { $_ > 0 } values %layer_external; + ok $external, 'slowdown_below_layer_time does not alter external perimeters'; } __END__ diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 2f17e91c39..01906526cf 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -201,7 +201,8 @@ Wipe::wipe(GCode &gcodegen, bool toolchange) GCode::GCode() : placeholder_parser(NULL), enable_loop_clipping(true), enable_cooling_markers(false), layer_count(0), - layer_index(-1), layer(NULL), first_layer(false), elapsed_time(0.0), volumetric_speed(0), + layer_index(-1), layer(NULL), first_layer(false), elapsed_time(0.0), + elapsed_time_bridges(0.0), elapsed_time_external(0.0), volumetric_speed(0), _last_pos_defined(false) { } @@ -575,7 +576,9 @@ GCode::_extrude(ExtrusionPath path, std::string description, double speed) // extrude arc or line if (path.is_bridge() && this->enable_cooling_markers) gcode += ";_BRIDGE_FAN_START\n"; - gcode += this->writer.set_speed(F, "", this->enable_cooling_markers ? ";_EXTRUDE_SET_SPEED" : ""); + std::string comment = ";_EXTRUDE_SET_SPEED"; + if (path.role == erExternalPerimeter) comment += ";_EXTERNAL_PERIMETER"; + gcode += this->writer.set_speed(F, "", this->enable_cooling_markers ? comment : ""); double path_length = 0; { std::string comment = this->config.gcode_comments ? description : ""; @@ -600,8 +603,12 @@ GCode::_extrude(ExtrusionPath path, std::string description, double speed) this->set_last_pos(path.last_point()); - if (this->config.cooling) - this->elapsed_time += path_length / F * 60; + if (this->config.cooling) { + float t = path_length / F * 60; + this->elapsed_time += t; + if (path.is_bridge()) this->elapsed_time_bridges += t; + if (path.role == erExternalPerimeter) this->elapsed_time_external += t; + } return gcode; } diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 8698eb1a6e..715af4d4c5 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -89,7 +89,7 @@ class GCode { // This value is not quite precise. First it only accouts for extrusion moves and travel moves, // it does not account for wipe, retract / unretract moves. // second it does not account for the velocity profiles of the printer. - float elapsed_time; // seconds + float elapsed_time, elapsed_time_bridges, elapsed_time_external; // seconds double volumetric_speed; GCode(); diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.cpp b/xs/src/libslic3r/GCode/CoolingBuffer.cpp index e976c9f465..786a9a8e40 100644 --- a/xs/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/xs/src/libslic3r/GCode/CoolingBuffer.cpp @@ -19,8 +19,12 @@ CoolingBuffer::append(const std::string &gcode, std::string obj_id, size_t layer this->_gcode += gcode; // This is a very rough estimate of the print time, // not taking into account the acceleration curves generated by the printer firmware. - this->_elapsed_time += this->_gcodegen->elapsed_time; - this->_gcodegen->elapsed_time = 0; + this->_elapsed_time += this->_gcodegen->elapsed_time; + this->_elapsed_time_bridges += this->_gcodegen->elapsed_time_bridges; + this->_elapsed_time_external += this->_gcodegen->elapsed_time_external; + this->_gcodegen->elapsed_time = 0; + this->_gcodegen->elapsed_time_bridges = 0; + this->_gcodegen->elapsed_time_external = 0; return out; } @@ -55,32 +59,40 @@ std::string CoolingBuffer::flush() { GCode &gg = *this->_gcodegen; + std::string gcode = this->_gcode; - std::string gcode = this->_gcode; - float elapsed = this->_elapsed_time; - this->_gcode = ""; - this->_elapsed_time = 0; - this->_last_z.clear(); // reset the whole table otherwise we would compute overlapping times - - int fan_speed = gg.config.fan_always_on ? gg.config.min_fan_speed.value : 0; - - float speed_factor = 1.0; + int fan_speed = gg.config.fan_always_on ? gg.config.min_fan_speed.value : 0; + float speed_factor = 1.0; + bool slowdown_external = true; if (gg.config.cooling) { #ifdef SLIC3R_DEBUG - printf("Layer %zu estimated printing time: %f seconds\n", this->_layer_id, elapsed); + printf("Layer %zu estimated printing time: %f seconds\n", this->_layer_id, this->_elapsed_time); #endif - if (elapsed < (float)gg.config.slowdown_below_layer_time) { + if (this->_elapsed_time < (float)gg.config.slowdown_below_layer_time) { // Layer time very short. Enable the fan to a full throttle and slow down the print // (stretch the layer print time to slowdown_below_layer_time). fan_speed = gg.config.max_fan_speed; - speed_factor = elapsed / (float)gg.config.slowdown_below_layer_time; - } else if (elapsed < (float)gg.config.fan_below_layer_time) { + + // We are not altering speed of bridges. + float time_to_stretch = this->_elapsed_time - this->_elapsed_time_bridges; + float target_time = (float)gg.config.slowdown_below_layer_time - this->_elapsed_time_bridges; + + // If we spend most of our time on external perimeters include them in the slowdown, + // otherwise only alter other extrusions. + if (this->_elapsed_time_external < time_to_stretch/2.) { + time_to_stretch -= this->_elapsed_time_external; + target_time -= this->_elapsed_time_external; + slowdown_external = false; + } + + speed_factor = time_to_stretch / target_time; + } else if (this->_elapsed_time < (float)gg.config.fan_below_layer_time) { // Layer time quite short. Enable the fan proportionally according to the current layer time. fan_speed = gg.config.max_fan_speed - (gg.config.max_fan_speed - gg.config.min_fan_speed) - * (elapsed - (float)gg.config.slowdown_below_layer_time) + * (this->_elapsed_time - (float)gg.config.slowdown_below_layer_time) / (gg.config.fan_below_layer_time - gg.config.slowdown_below_layer_time); } @@ -100,11 +112,12 @@ CoolingBuffer::flush() if (boost::starts_with(line, "G1") && boost::contains(line, ";_EXTRUDE_SET_SPEED") && !boost::contains(line, ";_WIPE") - && !bridge_fan_start) { + && !bridge_fan_start + && (slowdown_external || !boost::contains(line, ";_EXTERNAL_PERIMETER"))) { apply_speed_factor(line, speed_factor, this->_min_print_speed); boost::replace_first(line, ";_EXTRUDE_SET_SPEED", ""); } - bridge_fan_start = boost::contains(line, ";_BRIDGE_FAN_START"); + bridge_fan_start = boost::starts_with(line, ";_BRIDGE_FAN_START"); new_gcode += line + '\n'; } gcode = new_gcode; @@ -125,6 +138,14 @@ CoolingBuffer::flush() } boost::replace_all(gcode, ";_WIPE", ""); boost::replace_all(gcode, ";_EXTRUDE_SET_SPEED", ""); + boost::replace_all(gcode, ";_EXTERNAL_PERIMETER", ""); + + // Reset the buffer. + this->_elapsed_time = 0; + this->_elapsed_time_bridges = 0; + this->_elapsed_time_external = 0; + this->_gcode = ""; + this->_last_z.clear(); // reset the whole table otherwise we would compute overlapping times return gcode; } diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.hpp b/xs/src/libslic3r/GCode/CoolingBuffer.hpp index 01770a58d5..c290612ba4 100644 --- a/xs/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/xs/src/libslic3r/GCode/CoolingBuffer.hpp @@ -17,7 +17,8 @@ and the print is modified to stretch over a minimum layer time. class CoolingBuffer { public: CoolingBuffer(GCode &gcodegen) - : _gcodegen(&gcodegen), _elapsed_time(0.), _layer_id(0) + : _gcodegen(&gcodegen), _elapsed_time(0.), _elapsed_time_bridges(0.), + _elapsed_time_external(0.), _layer_id(0) { this->_min_print_speed = this->_gcodegen->config.min_print_speed * 60; }; @@ -29,6 +30,8 @@ class CoolingBuffer { GCode* _gcodegen; std::string _gcode; float _elapsed_time; + float _elapsed_time_bridges; + float _elapsed_time_external; size_t _layer_id; std::map _last_z; float _min_print_speed;