Permalink
Browse files

Ported CoolingBuffer to C++/XS

1 parent 475f9d4 commit 117fb306363d40ac67f6bc51f8481010dc9b9419 @alexrj committed Dec 21, 2016
View
@@ -56,7 +56,6 @@ use Slic3r::Format::AMF;
use Slic3r::Format::OBJ;
use Slic3r::Format::STL;
use Slic3r::GCode::ArcFitting;
-use Slic3r::GCode::CoolingBuffer;
use Slic3r::GCode::MotionPlanner;
use Slic3r::GCode::PressureRegulator;
use Slic3r::GCode::Reader;
@@ -1,94 +0,0 @@
-# A standalone (no C++ implementation) G-code filter, to control cooling of the print.
-# The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited
-# and the print is modified to stretch over a minimum layer time.
-
-package Slic3r::GCode::CoolingBuffer;
-use Moo;
-
-has 'config' => (is => 'ro', required => 1); # Slic3r::Config::Print
-has 'gcodegen' => (is => 'ro', required => 1); # Slic3r::GCode C++ instance
-has 'gcode' => (is => 'rw', default => sub {""}); # Cache of a G-code snippet emitted for a single layer.
-has 'elapsed_time' => (is => 'rw', default => sub {0});
-has 'layer_id' => (is => 'rw');
-has 'last_z' => (is => 'rw', default => sub { {} }); # obj_id => z (basically a 'last seen' table)
-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, $obj_id, $layer_id, $print_z) = @_;
-
- my $return = "";
- if (exists $self->last_z->{$obj_id} && $self->last_z->{$obj_id} != $print_z) {
- # A layer was finished, Z of the object's layer changed. Process the layer.
- $return = $self->flush;
- }
-
- $self->layer_id($layer_id);
- $self->last_z->{$obj_id} = $print_z;
- $self->gcode($self->gcode . $gcode);
- #FIXME Vojtech: This is a very rough estimate of the print time,
- # not taking into account the acceleration profiles generated by the printer firmware.
- $self->elapsed_time($self->elapsed_time + $self->gcodegen->elapsed_time);
- $self->gcodegen->set_elapsed_time(0);
-
- return $return;
-}
-
-sub flush {
- my $self = shift;
-
- my $gcode = $self->gcode;
- my $elapsed = $self->elapsed_time;
- $self->gcode("");
- $self->elapsed_time(0);
- $self->last_z({}); # reset the whole table otherwise we would compute overlapping times
-
- my $fan_speed = $self->config->fan_always_on ? $self->config->min_fan_speed : 0;
- my $speed_factor = 1;
- if ($self->config->cooling) {
- Slic3r::debugf "Layer %d estimated printing time: %d seconds\n", $self->layer_id, $elapsed;
- if ($elapsed < $self->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 = $self->config->max_fan_speed;
- $speed_factor = $elapsed / $self->config->slowdown_below_layer_time;
- } elsif ($elapsed < $self->config->fan_below_layer_time) {
- # Layer time quite short. Enable the fan proportionally according to the current layer time.
- $fan_speed = $self->config->max_fan_speed - ($self->config->max_fan_speed - $self->config->min_fan_speed)
- * ($elapsed - $self->config->slowdown_below_layer_time)
- / ($self->config->fan_below_layer_time - $self->config->slowdown_below_layer_time); #/
- }
- Slic3r::debugf " fan = %d%%, speed = %f%%\n", $fan_speed, $speed_factor * 100;
-
- if ($speed_factor < 1) {
- # Adjust feed rate of G1 commands marked with an _EXTRUDE_SET_SPEED
- # as long as they are not _WIPE moves (they cannot if they are _EXTRUDE_SET_SPEED)
- # and they are not preceded directly by _BRIDGE_FAN_START (do not adjust bridging speed).
- $gcode =~ s/^(?=.*?;_EXTRUDE_SET_SPEED)(?!.*?;_WIPE)(?<!;_BRIDGE_FAN_START\n)(G1\sF)(\d+(?:\.\d+)?)/
- my $new_speed = $2 * $speed_factor;
- $1 . sprintf("%.3f", $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->writer->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->writer->set_fan($self->config->bridge_fan_speed, 1) /gmex;
- $gcode =~ s/^;_BRIDGE_FAN_END\n/ $self->gcodegen->writer->set_fan($fan_speed, 1) /gmex;
- }
- $gcode =~ s/;_WIPE//g;
- $gcode =~ s/;_EXTRUDE_SET_SPEED//g;
-
- return $gcode;
-}
-
-1;
@@ -98,10 +98,7 @@ sub BUILD {
}
}
- $self->_cooling_buffer(Slic3r::GCode::CoolingBuffer->new(
- config => $self->config,
- gcodegen => $self->_gcodegen,
- ));
+ $self->_cooling_buffer(Slic3r::GCode::CoolingBuffer->new($self->_gcodegen));
$self->_spiral_vase(Slic3r::GCode::SpiralVase->new(config => $self->config))
if $self->config->spiral_vase;
View
@@ -52,6 +52,7 @@ add_library(libslic3r STATIC
${LIBDIR}/libslic3r/Fill/FillRectilinear.cpp
${LIBDIR}/libslic3r/Flow.cpp
${LIBDIR}/libslic3r/GCode.cpp
+ ${LIBDIR}/libslic3r/GCode/CoolingBuffer.cpp
${LIBDIR}/libslic3r/GCodeSender.cpp
${LIBDIR}/libslic3r/GCodeWriter.cpp
${LIBDIR}/libslic3r/Geometry.cpp
View
@@ -14,35 +14,32 @@ use List::Util qw(first);
use Slic3r;
use Slic3r::Test;
+my $gcodegen;
sub buffer {
my $config = shift || Slic3r::Config->new;
my $print_config = Slic3r::Config::Print->new;
$print_config->apply_dynamic($config);
- my $gcodegen = Slic3r::GCode->new;
+ $gcodegen = Slic3r::GCode->new;
$gcodegen->apply_print_config($print_config);
$gcodegen->set_layer_count(10);
- my $buffer = Slic3r::GCode::CoolingBuffer->new(
- config => $print_config,
- gcodegen => $gcodegen,
- );
- return $buffer;
+ return Slic3r::GCode::CoolingBuffer->new($gcodegen);
}
my $config = Slic3r::Config->new_from_defaults;
$config->set('disable_fan_first_layers', 0);
{
my $buffer = buffer($config);
- $buffer->gcodegen->set_elapsed_time($buffer->config->slowdown_below_layer_time + 1);
+ $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time + 1);
my $gcode = $buffer->append('G1 F3000;_EXTRUDE_SET_SPEED\nG1 X100 E1', 0, 0, 0.4) . $buffer->flush;
like $gcode, qr/F3000/, 'speed is not altered when elapsed time is greater than slowdown threshold';
}
{
my $buffer = buffer($config);
- $buffer->gcodegen->set_elapsed_time($buffer->config->slowdown_below_layer_time - 1);
+ $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time - 1);
my $gcode = $buffer->append(
"G1 X50 F2500\n" .
"G1 F3000;_EXTRUDE_SET_SPEED\n" .
@@ -57,7 +54,7 @@ $config->set('disable_fan_first_layers', 0);
{
my $buffer = buffer($config);
- $buffer->gcodegen->set_elapsed_time($buffer->config->fan_below_layer_time + 1);
+ $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time + 1);
my $gcode = $buffer->append('G1 X100 E1 F3000', 0, 0, 0.4) . $buffer->flush;
unlike $gcode, qr/M106/, 'fan is not activated when elapsed time is greater than fan threshold';
}
@@ -67,7 +64,7 @@ $config->set('disable_fan_first_layers', 0);
my $gcode = "";
for my $obj_id (0 .. 1) {
# use an elapsed time which is < the slowdown threshold but greater than it when summed twice
- $buffer->gcodegen->set_elapsed_time($buffer->config->slowdown_below_layer_time - 1);
+ $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time - 1);
$gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, 0, 0.4);
}
$gcode .= $buffer->flush;
@@ -80,7 +77,7 @@ $config->set('disable_fan_first_layers', 0);
for my $layer_id (0 .. 1) {
for my $obj_id (0 .. 1) {
# use an elapsed time which is < the threshold but greater than it when summed twice
- $buffer->gcodegen->set_elapsed_time($buffer->config->fan_below_layer_time - 1);
+ $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time - 1);
$gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0.4 + 0.4*$layer_id + 0.1*$obj_id); # print same layer at distinct heights
}
}
@@ -94,7 +91,7 @@ $config->set('disable_fan_first_layers', 0);
for my $layer_id (0 .. 1) {
for my $obj_id (0 .. 1) {
# use an elapsed time which is < the threshold even when summed twice
- $buffer->gcodegen->set_elapsed_time($buffer->config->fan_below_layer_time/2 - 1);
+ $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time/2 - 1);
$gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0.4 + 0.4*$layer_id + 0.1*$obj_id); # print same layer at distinct heights
}
}
@@ -147,7 +144,7 @@ $config->set('disable_fan_first_layers', 0);
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
my @layer_times = (0); # in seconds
- Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
+ Slic3r::GCode::Reader->new->parse(my $gcode = Slic3r::Test::gcode($print), sub {
my ($self, $cmd, $args, $info) = @_;
if ($cmd eq 'G1') {
View
@@ -44,6 +44,8 @@ src/libslic3r/Flow.cpp
src/libslic3r/Flow.hpp
src/libslic3r/GCode.cpp
src/libslic3r/GCode.hpp
+src/libslic3r/GCode/CoolingBuffer.cpp
+src/libslic3r/GCode/CoolingBuffer.hpp
src/libslic3r/GCodeSender.cpp
src/libslic3r/GCodeSender.hpp
src/libslic3r/GCodeWriter.cpp
@@ -0,0 +1,132 @@
+#include "CoolingBuffer.hpp"
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <iostream>
+
+namespace Slic3r {
+
+std::string
+CoolingBuffer::append(const std::string &gcode, std::string obj_id, size_t layer_id, float print_z)
+{
+ std::string out;
+ if (this->_last_z.find(obj_id) != this->_last_z.end()) {
+ // A layer was finished, Z of the object's layer changed. Process the layer.
+ out = this->flush();
+ }
+
+ this->_layer_id = layer_id;
+ this->_last_z[obj_id] = print_z;
+ 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;
+
+ return out;
+}
+
+void
+apply_speed_factor(std::string &line, float speed_factor, float min_print_speed)
+{
+ // find pos of F
+ size_t pos = line.find_first_of('F');
+ size_t last_pos = line.find_first_of(' ', pos+1);
+
+ // extract current speed
+ float speed;
+ {
+ std::istringstream iss(line.substr(pos+1, last_pos));
+ iss >> speed;
+ }
+
+ // change speed
+ speed *= speed_factor;
+ speed = std::max(speed, min_print_speed);
+
+ // replace speed in string
+ {
+ std::ostringstream oss;
+ oss << speed;
+ line.replace(pos+1, (last_pos-pos), oss.str());
+ }
+}
+
+std::string
+CoolingBuffer::flush()
+{
+ GCode &gg = *this->_gcodegen;
+
+ 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;
+
+ if (gg.config.cooling) {
+ #ifdef SLIC3R_DEBUG
+ printf("Layer %zu estimated printing time: %f seconds\n", layer_id, elapsed);
+ #endif
+
+ if (elapsed < (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) {
+ // 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)
+ / (gg.config.fan_below_layer_time - gg.config.slowdown_below_layer_time);
+ }
+
+ #ifdef SLIC3R_DEBUG
+ printf(" fan = %d%%, speed = %f%%\n", fan_speed, speed_factor * 100);
+ #endif
+
+ if (speed_factor < 1.0) {
+ // Adjust feed rate of G1 commands marked with an _EXTRUDE_SET_SPEED
+ // as long as they are not _WIPE moves (they cannot if they are _EXTRUDE_SET_SPEED)
+ // and they are not preceded directly by _BRIDGE_FAN_START (do not adjust bridging speed).
+ std::string new_gcode;
+ std::istringstream ss(gcode);
+ std::string line;
+ bool bridge_fan_start = false;
+ while (std::getline(ss, line)) {
+ if (boost::starts_with(line, "G1")
+ && boost::contains(line, ";_EXTRUDE_SET_SPEED")
+ && !boost::contains(line, ";_WIPE")
+ && !bridge_fan_start) {
+ 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");
+ new_gcode += line + '\n';
+ }
+ gcode = new_gcode;
+ }
+ }
+ if (this->_layer_id < gg.config.disable_fan_first_layers)
+ fan_speed = 0;
+
+ gcode = gg.writer.set_fan(fan_speed) + gcode;
+
+ // bridge fan speed
+ if (!gg.config.cooling || gg.config.bridge_fan_speed == 0 || this->_layer_id < gg.config.disable_fan_first_layers) {
+ boost::replace_all(gcode, ";_BRIDGE_FAN_START", "");
+ boost::replace_all(gcode, ";_BRIDGE_FAN_END", "");
+ } else {
+ boost::replace_all(gcode, ";_BRIDGE_FAN_START", gg.writer.set_fan(gg.config.bridge_fan_speed, true));
+ boost::replace_all(gcode, ";_BRIDGE_FAN_END", gg.writer.set_fan(fan_speed, true));
+ }
+ boost::replace_all(gcode, ";_WIPE", "");
+ boost::replace_all(gcode, ";_EXTRUDE_SET_SPEED", "");
+
+ return gcode;
+}
+
+}
@@ -0,0 +1,39 @@
+#ifndef slic3r_CoolingBuffer_hpp_
+#define slic3r_CoolingBuffer_hpp_
+
+#include "libslic3r.h"
+#include "GCode.hpp"
+#include <map>
+#include <string>
+
+namespace Slic3r {
+
+/*
+A standalone G-code filter, to control cooling of the print.
+The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited
+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)
+ {
+ this->_min_print_speed = this->_gcodegen->config.min_print_speed * 60;
+ };
+ std::string append(const std::string &gcode, std::string obj_id, size_t layer_id, float print_z);
+ std::string flush();
+ GCode* gcodegen() { return this->_gcodegen; };
+
+ private:
+ GCode* _gcodegen;
+ std::string _gcode;
+ float _elapsed_time;
+ size_t _layer_id;
+ std::map<std::string,float> _last_z;
+ float _min_print_speed;
+};
+
+}
+
+#endif
View
@@ -13,6 +13,7 @@ REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection");
REGISTER_CLASS(Flow, "Flow");
REGISTER_CLASS(Filler, "Filler");
REGISTER_CLASS(AvoidCrossingPerimeters, "GCode::AvoidCrossingPerimeters");
+REGISTER_CLASS(CoolingBuffer, "GCode::CoolingBuffer");
REGISTER_CLASS(OozePrevention, "GCode::OozePrevention");
REGISTER_CLASS(Wipe, "GCode::Wipe");
REGISTER_CLASS(GCode, "GCode");
Oops, something went wrong.

0 comments on commit 117fb30

Please sign in to comment.