Ported CoolingBuffer to C++/XS
This commit is contained in:
parent
c517b0d8f1
commit
a65c9ba083
10 changed files with 227 additions and 17 deletions
|
@ -65,7 +65,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;
|
||||
|
|
|
@ -100,10 +100,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;
|
||||
|
|
45
t/cooling.t
45
t/cooling.t
|
@ -12,20 +12,17 @@ BEGIN {
|
|||
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;
|
||||
|
@ -33,14 +30,14 @@ $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" .
|
||||
|
@ -55,7 +52,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';
|
||||
}
|
||||
|
@ -65,7 +62,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;
|
||||
|
@ -78,7 +75,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
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +89,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
|
||||
}
|
||||
}
|
||||
|
@ -134,4 +131,28 @@ $config->set('disable_fan_first_layers', 0);
|
|||
ok !$bridge_with_no_fan, 'bridge fan is turned on for all bridges';
|
||||
}
|
||||
|
||||
{
|
||||
my $config = Slic3r::Config->new_from_defaults;
|
||||
$config->set('cooling', 1);
|
||||
$config->set('fan_below_layer_time', 0);
|
||||
$config->set('slowdown_below_layer_time', 10);
|
||||
$config->set('min_print_speed', 0);
|
||||
$config->set('start_gcode', '');
|
||||
|
||||
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
||||
my @layer_times = (0); # in seconds
|
||||
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_times[-1] += abs($info->{dist_XY} || $info->{dist_E} || $info->{dist_Z} || 0) / ($args->{F} // $self->F) * 60;
|
||||
}
|
||||
});
|
||||
my $all_below = !defined first { $_ > 0 && $_ < $config->slowdown_below_layer_time } @layer_times;
|
||||
ok $all_below, 'slowdown_below_layer_time is honored';
|
||||
}
|
||||
|
||||
__END__
|
||||
|
|
|
@ -52,6 +52,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
|
||||
|
|
132
xs/src/libslic3r/GCode/CoolingBuffer.cpp
Normal file
132
xs/src/libslic3r/GCode/CoolingBuffer.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
39
xs/src/libslic3r/GCode/CoolingBuffer.hpp
Normal file
39
xs/src/libslic3r/GCode/CoolingBuffer.hpp
Normal file
|
@ -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
|
|
@ -15,6 +15,7 @@ REGISTER_CLASS(ExtrusionSimulator, "ExtrusionSimulator");
|
|||
REGISTER_CLASS(Filler, "Filler");
|
||||
REGISTER_CLASS(Flow, "Flow");
|
||||
REGISTER_CLASS(AvoidCrossingPerimeters, "GCode::AvoidCrossingPerimeters");
|
||||
REGISTER_CLASS(CoolingBuffer, "GCode::CoolingBuffer");
|
||||
REGISTER_CLASS(OozePrevention, "GCode::OozePrevention");
|
||||
REGISTER_CLASS(Wipe, "GCode::Wipe");
|
||||
REGISTER_CLASS(GCode, "GCode");
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/GCode.hpp"
|
||||
#include "libslic3r/GCode/CoolingBuffer.hpp"
|
||||
%}
|
||||
|
||||
%name{Slic3r::GCode::AvoidCrossingPerimeters} class AvoidCrossingPerimeters {
|
||||
|
@ -70,6 +71,16 @@
|
|||
%code{% THIS->path = *value; %};
|
||||
};
|
||||
|
||||
%name{Slic3r::GCode::CoolingBuffer} class CoolingBuffer {
|
||||
CoolingBuffer(GCode* gcode)
|
||||
%code{% RETVAL = new CoolingBuffer(*gcode); %};
|
||||
~CoolingBuffer();
|
||||
Ref<GCode> gcodegen();
|
||||
|
||||
std::string append(std::string gcode, std::string obj_id, size_t layer_id, float print_z);
|
||||
std::string flush();
|
||||
};
|
||||
|
||||
%name{Slic3r::GCode} class GCode {
|
||||
GCode();
|
||||
~GCode();
|
||||
|
|
|
@ -197,6 +197,10 @@ OozePrevention* O_OBJECT_SLIC3R
|
|||
Ref<OozePrevention> O_OBJECT_SLIC3R_T
|
||||
Clone<OozePrevention> O_OBJECT_SLIC3R_T
|
||||
|
||||
CoolingBuffer* O_OBJECT_SLIC3R
|
||||
Ref<CoolingBuffer> O_OBJECT_SLIC3R_T
|
||||
Clone<CoolingBuffer> O_OBJECT_SLIC3R_T
|
||||
|
||||
GCode* O_OBJECT_SLIC3R
|
||||
Ref<GCode> O_OBJECT_SLIC3R_T
|
||||
Clone<GCode> O_OBJECT_SLIC3R_T
|
||||
|
|
|
@ -164,6 +164,10 @@
|
|||
%typemap{Ref<OozePrevention>}{simple};
|
||||
%typemap{Clone<OozePrevention>}{simple};
|
||||
|
||||
%typemap{CoolingBuffer*};
|
||||
%typemap{Ref<CoolingBuffer>}{simple};
|
||||
%typemap{Clone<CoolingBuffer>}{simple};
|
||||
|
||||
%typemap{GCode*};
|
||||
%typemap{Ref<GCode>}{simple};
|
||||
%typemap{Clone<GCode>}{simple};
|
||||
|
|
Loading…
Reference in a new issue