Further refactoring of the cooling logic to collect per extruder data.
This commit is contained in:
bubnikv 2017-06-23 10:13:09 +02:00
parent 39b9341359
commit 32fa84c5a5
9 changed files with 123 additions and 132 deletions

View file

@ -32,20 +32,19 @@ $config->set('disable_fan_first_layers', [ 0 ]);
{ {
my $buffer = buffer($config); my $buffer = buffer($config);
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time->[0] + 1); $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time->[0] + 1);
my $gcode = $buffer->append('G1 F3000;_EXTRUDE_SET_SPEED\nG1 X100 E1', 0, 0, 0) . $buffer->flush; my $gcode = $buffer->process_layer('G1 F3000;_EXTRUDE_SET_SPEED\nG1 X100 E1', 0);
like $gcode, qr/F3000/, 'speed is not altered when elapsed time is greater than slowdown threshold'; like $gcode, qr/F3000/, 'speed is not altered when elapsed time is greater than slowdown threshold';
} }
{ {
my $buffer = buffer($config); my $buffer = buffer($config);
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time->[0] - 1); $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time->[0] - 1);
my $gcode = $buffer->append( my $gcode = $buffer->process_layer(
"G1 X50 F2500\n" . "G1 X50 F2500\n" .
"G1 F3000;_EXTRUDE_SET_SPEED\n" . "G1 F3000;_EXTRUDE_SET_SPEED\n" .
"G1 X100 E1\n" . "G1 X100 E1\n" .
"G1 E4 F400", "G1 E4 F400",
0, 0, 0 0);
) . $buffer->flush;
unlike $gcode, qr/F3000/, 'speed is altered when elapsed time is lower than slowdown threshold'; unlike $gcode, qr/F3000/, 'speed is altered when elapsed time is lower than slowdown threshold';
like $gcode, qr/F2500/, 'speed is not altered for travel moves'; like $gcode, qr/F2500/, 'speed is not altered for travel moves';
like $gcode, qr/F400/, 'speed is not altered for extruder-only moves'; like $gcode, qr/F400/, 'speed is not altered for extruder-only moves';
@ -54,7 +53,7 @@ $config->set('disable_fan_first_layers', [ 0 ]);
{ {
my $buffer = buffer($config); my $buffer = buffer($config);
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time->[0] + 1); $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time->[0] + 1);
my $gcode = $buffer->append('G1 X100 E1 F3000', 0, 0, 0) . $buffer->flush; my $gcode = $buffer->process_layer('G1 X100 E1 F3000', 0);
unlike $gcode, qr/M106/, 'fan is not activated when elapsed time is greater than fan threshold'; unlike $gcode, qr/M106/, 'fan is not activated when elapsed time is greater than fan threshold';
} }
@ -62,25 +61,26 @@ $config->set('disable_fan_first_layers', [ 0 ]);
my $buffer = buffer($config); my $buffer = buffer($config);
my $gcode = ""; my $gcode = "";
for my $obj_id (0 .. 1) { for my $obj_id (0 .. 1) {
# use an elapsed time which is < the slowdown threshold but greater than it when summed twice $gcode .= "G1 X100 E1 F3000\n";
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time->[0] - 1);
$gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, 0, 0);
} }
$gcode .= $buffer->flush; # use an elapsed time which is < the slowdown threshold but greater than it when summed twice
like $gcode, qr/F3000/, 'slowdown is computed on all objects printing at same Z'; $buffer->gcodegen->set_elapsed_time(2 * ($buffer->gcodegen->config->slowdown_below_layer_time->[0] - 1));
$gcode .= $buffer->process_layer($gcode, 0);
like $gcode, qr/F3000/, 'slowdown is computed on all objects printing at the same Z';
} }
{ {
my $buffer = buffer($config); my $buffer = buffer($config);
my $gcode = ""; my $gcode = "";
for my $layer_id (0 .. 1) { for my $layer_id (0 .. 1) {
my $layer_gcode = "";
for my $obj_id (0 .. 1) { for my $obj_id (0 .. 1) {
# use an elapsed time which is < the threshold but greater than it when summed twice $layer_gcode .= "G1 X100 E1 F3000\n";
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time->[0] - 1);
$gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0);
} }
# use an elapsed time which is < the threshold but greater than it when summed twice
$buffer->gcodegen->set_elapsed_time(2 * ($buffer->gcodegen->config->fan_below_layer_time->[0] - 1));
$gcode .= $buffer->process_layer($layer_gcode, $layer_id);
} }
$gcode .= $buffer->flush;
unlike $gcode, qr/M106/, 'fan activation is computed on all objects printing at different Z'; unlike $gcode, qr/M106/, 'fan activation is computed on all objects printing at different Z';
} }
@ -88,13 +88,14 @@ $config->set('disable_fan_first_layers', [ 0 ]);
my $buffer = buffer($config); my $buffer = buffer($config);
my $gcode = ""; my $gcode = "";
for my $layer_id (0 .. 1) { for my $layer_id (0 .. 1) {
my $layer_gcode = "";
for my $obj_id (0 .. 1) { for my $obj_id (0 .. 1) {
# use an elapsed time which is < the threshold even when summed twice $layer_gcode .= "G1 X100 E1 F3000\n";
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time->[0]/2 - 1);
$gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0);
} }
# use an elapsed time which is < the threshold even when summed twice
$buffer->gcodegen->set_elapsed_time(2 * ($buffer->gcodegen->config->fan_below_layer_time->[0]/2 - 1));
$gcode .= $buffer->process_layer($layer_gcode, $layer_id);
} }
$gcode .= $buffer->flush;
like $gcode, qr/M106/, 'fan activation is computed on all objects printing at different Z'; like $gcode, qr/M106/, 'fan activation is computed on all objects printing at different Z';
} }

View file

@ -165,13 +165,13 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
// Let the tool change be executed by the wipe tower class. // Let the tool change be executed by the wipe tower class.
// Inform the G-code writer about the changes done behind its back. // Inform the G-code writer about the changes done behind its back.
gcode += tcr.gcode; gcode += tcr.gcode;
// Accumulate the elapsed time for the correct calculation of layer cooling.
//FIXME currently disabled as Slic3r PE needs to be updated to differentiate the moves it could slow down
// from the moves it could not.
gcodegen.m_elapsed_time.other += tcr.elapsed_time;
// Let the m_writer know the current extruder_id, but ignore the generated G-code. // Let the m_writer know the current extruder_id, but ignore the generated G-code.
if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)) if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))
gcodegen.writer().toolchange(new_extruder_id); gcodegen.writer().toolchange(new_extruder_id);
// Accumulate the elapsed time for the correct calculation of layer cooling.
//FIXME currently disabled as Slic3r PE needs to be updated to differentiate the moves it could slow down
// from the moves it could not.
gcodegen.writer().elapsed_time()->other += tcr.elapsed_time;
// A phony move to the end position at the wipe tower. // A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(Pointf(tcr.end_pos.x, tcr.end_pos.y)); gcodegen.writer().travel_to_xy(Pointf(tcr.end_pos.x, tcr.end_pos.y));
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
@ -470,6 +470,8 @@ bool GCode::do_export(FILE *file, Print &print)
assert(final_extruder_id != (unsigned int)-1); assert(final_extruder_id != (unsigned int)-1);
} }
m_cooling_buffer->set_current_extruder(initial_extruder_id);
// Disable fan. // Disable fan.
if (print.config.cooling.get_at(initial_extruder_id) && print.config.disable_fan_first_layers.get_at(initial_extruder_id)) if (print.config.cooling.get_at(initial_extruder_id) && print.config.disable_fan_first_layers.get_at(initial_extruder_id))
write(file, m_writer.set_fan(0, true)); write(file, m_writer.set_fan(0, true));
@ -568,6 +570,7 @@ bool GCode::do_export(FILE *file, Print &print)
initial_extruder_id = new_extruder_id; initial_extruder_id = new_extruder_id;
final_extruder_id = tool_ordering.last_extruder(); final_extruder_id = tool_ordering.last_extruder();
assert(final_extruder_id != (unsigned int)-1); assert(final_extruder_id != (unsigned int)-1);
m_cooling_buffer->set_current_extruder(initial_extruder_id);
} }
this->set_origin(unscale(copy.x), unscale(copy.y)); this->set_origin(unscale(copy.x), unscale(copy.y));
if (finished_objects > 0) { if (finished_objects > 0) {
@ -594,7 +597,6 @@ bool GCode::do_export(FILE *file, Print &print)
lrs.emplace_back(std::move(ltp)); lrs.emplace_back(std::move(ltp));
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), &copy - object._shifted_copies.data()); this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), &copy - object._shifted_copies.data());
} }
write(file, this->filter(m_cooling_buffer->flush(), true));
++ finished_objects; ++ finished_objects;
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
// Reset it when starting another object from 1st layer. // Reset it when starting another object from 1st layer.
@ -622,7 +624,6 @@ bool GCode::do_export(FILE *file, Print &print)
m_wipe_tower->next_layer(); m_wipe_tower->next_layer();
this->process_layer(file, print, layer.second, layer_tools, size_t(-1)); this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
} }
write(file, this->filter(m_cooling_buffer->flush(), true));
if (m_wipe_tower) if (m_wipe_tower)
// Purge the extruder, pull out the active filament. // Purge the extruder, pull out the active filament.
write(file, m_wipe_tower->finalize(*this)); write(file, m_wipe_tower->finalize(*this));
@ -1107,7 +1108,7 @@ void GCode::process_layer(
//FIXME Update the CoolingBuffer class to ignore the object ID, which does not make sense anymore //FIXME Update the CoolingBuffer class to ignore the object ID, which does not make sense anymore
// once all extrusions of a layer are processed at once. // once all extrusions of a layer are processed at once.
// Update the test cases. // Update the test cases.
gcode = m_cooling_buffer->append(gcode, 0, layer.id(), false); gcode = m_cooling_buffer->process_layer(gcode, layer.id());
write(file, this->filter(std::move(gcode), false)); write(file, this->filter(std::move(gcode), false));
} }
@ -1865,11 +1866,11 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (m_config.cooling.values.front()) { if (m_config.cooling.values.front()) {
float t = path_length / F * 60.f; float t = path_length / F * 60.f;
m_elapsed_time.total += t; m_writer.elapsed_time()->total += t;
if (is_bridge(path.role())) if (is_bridge(path.role()))
m_elapsed_time.bridges += t; m_writer.elapsed_time()->bridges += t;
if (path.role() == erExternalPerimeter) if (path.role() == erExternalPerimeter)
m_elapsed_time.external_perimeters += t; m_writer.elapsed_time()->external_perimeters += t;
} }
return gcode; return gcode;
@ -1918,7 +1919,7 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
gcode += m_writer.travel_to_xy(this->point_to_gcode(line->b), comment); gcode += m_writer.travel_to_xy(this->point_to_gcode(line->b), comment);
if (m_config.cooling.values.front()) if (m_config.cooling.values.front())
m_elapsed_time.travel += unscale(travel.length()) / m_config.get_abs_value("travel_speed"); m_writer.elapsed_time()->travel += unscale(travel.length()) / m_config.get_abs_value("travel_speed");
return gcode; return gcode;
} }

View file

@ -107,32 +107,6 @@ private:
bool m_brim_done; bool m_brim_done;
}; };
struct ElapsedTime
{
ElapsedTime() { this->reset(); }
void reset() { total = bridges = external_perimeters = travel = other = 0.f; }
ElapsedTime& operator+=(const ElapsedTime &rhs) {
this->total += rhs.total;
this->bridges += rhs.bridges;
this->external_perimeters += rhs.external_perimeters;
this->travel += rhs.travel;
this->other += rhs.other;
return *this;
}
// Potion of the total time, which cannot be stretched to heed the minimum layer print time.
float non_stretchable() const { return this->bridges + this->travel + this->other; }
// Potion of the total time, which could be stretched to heed the minimum layer print time.
float stretchable() const { return this->total - this->non_stretchable(); }
float total;
float bridges;
float external_perimeters;
float travel;
float other;
};
class GCode { class GCode {
public: public:
GCode() : GCode() :
@ -165,13 +139,12 @@ public:
const Layer* layer() const { return m_layer; } const Layer* layer() const { return m_layer; }
GCodeWriter& writer() { return m_writer; } GCodeWriter& writer() { return m_writer; }
bool enable_cooling_markers() const { return m_enable_cooling_markers; } bool enable_cooling_markers() const { return m_enable_cooling_markers; }
ElapsedTime get_reset_elapsed_time() { ElapsedTime et = this->m_elapsed_time; this->m_elapsed_time.reset(); return et; }
// For Perl bindings, to be used exclusively by unit tests. // For Perl bindings, to be used exclusively by unit tests.
unsigned int layer_count() const { return m_layer_count; } unsigned int layer_count() const { return m_layer_count; }
void set_layer_count(unsigned int value) { m_layer_count = value; } void set_layer_count(unsigned int value) { m_layer_count = value; }
float elapsed_time() const { return m_elapsed_time.total; } float elapsed_time() const { return m_writer.elapsed_time()->total; }
void set_elapsed_time(float value) { m_elapsed_time.total = value; } void set_elapsed_time(float value) { std::vector<unsigned int> extruders; extruders.push_back(0); m_writer.set_extruders(extruders); m_writer.set_extruder(0); m_writer.elapsed_time()->total = value; }
void apply_print_config(const PrintConfig &print_config); void apply_print_config(const PrintConfig &print_config);
protected: protected:
@ -268,11 +241,6 @@ protected:
// In non-sequential mode, all its copies will be printed. // In non-sequential mode, all its copies will be printed.
const Layer* m_layer; const Layer* m_layer;
std::map<const PrintObject*,Point> m_seam_position; std::map<const PrintObject*,Point> m_seam_position;
// Used by the CoolingBuffer G-code filter to calculate time spent per layer change.
// 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.
ElapsedTime m_elapsed_time;
double m_volumetric_speed; double m_volumetric_speed;
// Support for the extrusion role markers. Which marker is active? // Support for the extrusion role markers. Which marker is active?
ExtrusionRole m_last_extrusion_role; ExtrusionRole m_last_extrusion_role;

View file

@ -6,36 +6,6 @@
namespace Slic3r { namespace Slic3r {
CoolingBuffer::CoolingBuffer(GCode &gcodegen) :
m_gcodegen(gcodegen), m_layer_id(0),
m_elapsed_time(new ElapsedTime)
{
}
CoolingBuffer::~CoolingBuffer()
{
delete m_elapsed_time;
}
std::string CoolingBuffer::append(const std::string &gcode, size_t object_id, size_t layer_id, bool is_support)
{
std::string out;
size_t signature = object_id * 2 + (is_support ? 1 : 0);
if (m_object_ids_visited.find(signature) != m_object_ids_visited.end())
// For a single print_z, a combination of (object_id, is_support) could repeat once only.
// If the combination of (object_id, is_support) reappears, this must be for another print_z,
// therefore a layer has to be finalized.
out = this->flush();
m_object_ids_visited.insert(signature);
m_layer_id = layer_id;
m_gcode += gcode;
// This is a very rough estimate of the print time,
// not taking into account the acceleration curves generated by the printer firmware.
*m_elapsed_time += m_gcodegen.get_reset_elapsed_time();
return out;
}
void apply_speed_factor(std::string &line, float speed_factor, float min_print_speed) void apply_speed_factor(std::string &line, float speed_factor, float min_print_speed)
{ {
// find pos of F // find pos of F
@ -61,45 +31,47 @@ void apply_speed_factor(std::string &line, float speed_factor, float min_print_s
} }
} }
std::string CoolingBuffer::flush() std::string CoolingBuffer::process_layer(const std::string &gcode_src, size_t layer_id)
{ {
const FullPrintConfig &config = m_gcodegen.config(); const FullPrintConfig &config = m_gcodegen.config();
std::string gcode = std::move(m_gcode); std::string gcode = gcode_src;
m_gcode.clear(); int fan_speed = config.fan_always_on.values.front() ? config.min_fan_speed.values.front() : 0;
float speed_factor = 1.0;
bool slowdown_external = true;
int fan_speed = config.fan_always_on.values.front() ? config.min_fan_speed.values.front() : 0; const std::vector<ElapsedTime> &elapsed_times = m_gcodegen.writer().elapsed_times();
ElapsedTime elapsed_time;
float speed_factor = 1.0; for (const ElapsedTime &et : elapsed_times)
bool slowdown_external = true; elapsed_time += et;
if (config.cooling.values.front()) { if (config.cooling.values.front()) {
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
printf("Layer %zu estimated printing time: %f seconds\n", m_layer_id, m_elapsed_time->total); printf("Layer %zu estimated printing time: %f seconds\n", layer_id, elapsed_time.total);
#endif #endif
if (m_elapsed_time->total < (float)config.slowdown_below_layer_time.values.front()) { if (elapsed_time.total < (float)config.slowdown_below_layer_time.values.front()) {
// Layer time very short. Enable the fan to a full throttle and slow down the print // 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). // (stretch the layer print time to slowdown_below_layer_time).
fan_speed = config.max_fan_speed.values.front(); fan_speed = config.max_fan_speed.values.front();
// We are not altering speed of bridges. // We are not altering speed of bridges.
float time_to_stretch = m_elapsed_time->stretchable(); float time_to_stretch = elapsed_time.stretchable();
float target_time = (float)config.slowdown_below_layer_time.values.front() - m_elapsed_time->non_stretchable(); float target_time = (float)config.slowdown_below_layer_time.values.front() - elapsed_time.non_stretchable();
// If we spend most of our time on external perimeters include them in the slowdown, // If we spend most of our time on external perimeters include them in the slowdown,
// otherwise only alter other extrusions. // otherwise only alter other extrusions.
if (m_elapsed_time->external_perimeters < 0.5f * time_to_stretch) { if (elapsed_time.external_perimeters < 0.5f * time_to_stretch) {
time_to_stretch -= m_elapsed_time->external_perimeters; time_to_stretch -= elapsed_time.external_perimeters;
target_time -= m_elapsed_time->external_perimeters; target_time -= elapsed_time.external_perimeters;
slowdown_external = false; slowdown_external = false;
} }
speed_factor = time_to_stretch / target_time; speed_factor = time_to_stretch / target_time;
} else if (m_elapsed_time->total < (float)config.fan_below_layer_time.values.front()) { } else if (elapsed_time.total < (float)config.fan_below_layer_time.values.front()) {
// Layer time quite short. Enable the fan proportionally according to the current layer time. // Layer time quite short. Enable the fan proportionally according to the current layer time.
fan_speed = config.max_fan_speed.values.front() fan_speed = config.max_fan_speed.values.front()
- (config.max_fan_speed.values.front() - config.min_fan_speed.values.front()) - (config.max_fan_speed.values.front() - config.min_fan_speed.values.front())
* (m_elapsed_time->total - (float)config.slowdown_below_layer_time.values.front()) * (elapsed_time.total - (float)config.slowdown_below_layer_time.values.front())
/ (config.fan_below_layer_time.values.front() - config.slowdown_below_layer_time.values.front()); / (config.fan_below_layer_time.values.front() - config.slowdown_below_layer_time.values.front());
} }
@ -131,13 +103,13 @@ std::string CoolingBuffer::flush()
gcode = new_gcode; gcode = new_gcode;
} }
} }
if (m_layer_id < config.disable_fan_first_layers.values.front()) if (layer_id < config.disable_fan_first_layers.values.front())
fan_speed = 0; fan_speed = 0;
gcode = m_gcodegen.writer().set_fan(fan_speed) + gcode; gcode = m_gcodegen.writer().set_fan(fan_speed) + gcode;
// bridge fan speed // bridge fan speed
if (!config.cooling.values.front() || config.bridge_fan_speed.values.front() == 0 || m_layer_id < config.disable_fan_first_layers.values.front()) { if (!config.cooling.values.front() || config.bridge_fan_speed.values.front() == 0 || layer_id < config.disable_fan_first_layers.values.front()) {
boost::replace_all(gcode, ";_BRIDGE_FAN_START", ""); boost::replace_all(gcode, ";_BRIDGE_FAN_START", "");
boost::replace_all(gcode, ";_BRIDGE_FAN_END", ""); boost::replace_all(gcode, ";_BRIDGE_FAN_END", "");
} else { } else {
@ -149,7 +121,7 @@ std::string CoolingBuffer::flush()
boost::replace_all(gcode, ";_EXTERNAL_PERIMETER", ""); boost::replace_all(gcode, ";_EXTERNAL_PERIMETER", "");
m_object_ids_visited.clear(); m_object_ids_visited.clear();
m_elapsed_time->reset(); m_gcodegen.writer().reset_elapsed_times();
return gcode; return gcode;
} }

View file

@ -7,10 +7,45 @@
namespace Slic3r { namespace Slic3r {
struct ElapsedTime;
class GCode; class GCode;
class Layer; class Layer;
struct ElapsedTime
{
ElapsedTime(unsigned int extruder_id = 0) : extruder_id(extruder_id) { this->reset(); }
void reset() { total = bridges = external_perimeters = travel = other = 0.f; }
ElapsedTime& operator+=(const ElapsedTime &rhs) {
this->total += rhs.total;
this->bridges += rhs.bridges;
this->external_perimeters += rhs.external_perimeters;
this->travel += rhs.travel;
this->other += rhs.other;
return *this;
}
// Potion of the total time, which cannot be stretched to heed the minimum layer print time.
float non_stretchable() const { return this->bridges + this->travel + this->other; }
// Potion of the total time, which could be stretched to heed the minimum layer print time.
float stretchable() const { return this->total - this->non_stretchable(); }
// For which extruder ID has this statistics been collected?
unsigned int extruder_id;
// Total time.
float total;
// Per feature time slices.
float bridges;
float external_perimeters;
float travel;
float other;
};
// Sort ElapsedTime objects by the extruder id by default.
inline bool operator==(const ElapsedTime &e1, const ElapsedTime &e2) { return e1.extruder_id == e2.extruder_id; }
inline bool operator!=(const ElapsedTime &e1, const ElapsedTime &e2) { return e1.extruder_id != e2.extruder_id; }
inline bool operator< (const ElapsedTime &e1, const ElapsedTime &e2) { return e1.extruder_id < e2.extruder_id; }
inline bool operator> (const ElapsedTime &e1, const ElapsedTime &e2) { return e1.extruder_id > e2.extruder_id; }
/* /*
A standalone G-code filter, to control cooling of the print. 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 The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited
@ -19,19 +54,17 @@ and the print is modified to stretch over a minimum layer time.
class CoolingBuffer { class CoolingBuffer {
public: public:
CoolingBuffer(GCode &gcodegen); CoolingBuffer(GCode &gcodegen) : m_gcodegen(gcodegen) {}
~CoolingBuffer(); void set_current_extruder(unsigned int extruder_id) { m_current_extruder = extruder_id; }
std::string append(const std::string &gcode, size_t object_id, size_t layer_id, bool is_support); std::string process_layer(const std::string &gcode, size_t layer_id);
std::string flush(); GCode* gcodegen() { return &m_gcodegen; }
GCode* gcodegen() { return &m_gcodegen; };
private: private:
CoolingBuffer& operator=(const CoolingBuffer&); CoolingBuffer& operator=(const CoolingBuffer&);
GCode& m_gcodegen; GCode& m_gcodegen;
std::string m_gcode; std::string m_gcode;
ElapsedTime *m_elapsed_time; unsigned int m_current_extruder;
size_t m_layer_id;
std::set<size_t> m_object_ids_visited; std::set<size_t> m_object_ids_visited;
}; };

View file

@ -27,6 +27,11 @@ void GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
for (unsigned int extruder_id : extruder_ids) for (unsigned int extruder_id : extruder_ids)
m_extruders.emplace_back(Extruder(extruder_id, &this->config)); m_extruders.emplace_back(Extruder(extruder_id, &this->config));
m_elapsed_times.clear();
m_elapsed_times.reserve(extruder_ids.size());
for (unsigned int extruder_id : extruder_ids)
m_elapsed_times.emplace_back(ElapsedTime(extruder_id));
/* we enable support for multiple extruder if any extruder greater than 0 is used /* we enable support for multiple extruder if any extruder greater than 0 is used
(even if prints only uses that one) since we need to output Tx commands (even if prints only uses that one) since we need to output Tx commands
first extruder has index 0 */ first extruder has index 0 */
@ -235,6 +240,10 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id)
assert(it_extruder != m_extruders.end()); assert(it_extruder != m_extruders.end());
m_extruder = const_cast<Extruder*>(&*it_extruder); m_extruder = const_cast<Extruder*>(&*it_extruder);
auto it_elapsed_time = std::lower_bound(m_elapsed_times.begin(), m_elapsed_times.end(), ElapsedTime(extruder_id));
assert(it_elapsed_time != m_elapsed_times.end());
m_elapsed_time = const_cast<ElapsedTime*>(&*it_elapsed_time);
// return the toolchange command // return the toolchange command
// if we are running a single-extruder setup, just set the extruder and return nothing // if we are running a single-extruder setup, just set the extruder and return nothing
std::ostringstream gcode; std::ostringstream gcode;

View file

@ -6,6 +6,7 @@
#include "Extruder.hpp" #include "Extruder.hpp"
#include "Point.hpp" #include "Point.hpp"
#include "PrintConfig.hpp" #include "PrintConfig.hpp"
#include "GCode/CoolingBuffer.hpp"
namespace Slic3r { namespace Slic3r {
@ -15,18 +16,23 @@ public:
bool multiple_extruders; bool multiple_extruders;
GCodeWriter() : GCodeWriter() :
multiple_extruders(false), m_extrusion_axis("E"), m_extruder(nullptr), multiple_extruders(false), m_extrusion_axis("E"), m_extruder(nullptr), m_elapsed_time(nullptr),
m_single_extruder_multi_material(false), m_single_extruder_multi_material(false),
m_last_acceleration(0), m_last_fan_speed(0), m_last_acceleration(0), m_last_fan_speed(0),
m_last_bed_temperature(0), m_last_bed_temperature_reached(true), m_last_bed_temperature(0), m_last_bed_temperature_reached(true),
m_lifted(0) m_lifted(0)
{} {}
Extruder* extruder() { return m_extruder; } Extruder* extruder() { return m_extruder; }
const Extruder* extruder() const { return m_extruder; } const Extruder* extruder() const { return m_extruder; }
std::string extrusion_axis() const { return m_extrusion_axis; } ElapsedTime* elapsed_time() { return m_elapsed_time; }
void apply_print_config(const PrintConfig &print_config); const ElapsedTime* elapsed_time() const { return m_elapsed_time; }
const std::vector<ElapsedTime>& elapsed_times() const { return m_elapsed_times; }
void reset_elapsed_times() { for (auto &et : m_elapsed_times) et.reset(); }
std::string extrusion_axis() const { return m_extrusion_axis; }
void apply_print_config(const PrintConfig &print_config);
// Extruders are expected to be sorted in an increasing order. // Extruders are expected to be sorted in an increasing order.
void set_extruders(const std::vector<unsigned int> &extruder_ids); void set_extruders(const std::vector<unsigned int> &extruder_ids);
const std::vector<Extruder>& extruders() const { return m_extruders; } const std::vector<Extruder>& extruders() const { return m_extruders; }
std::vector<unsigned int> extruder_ids() const { std::vector<unsigned int> extruder_ids() const {
std::vector<unsigned int> out; std::vector<unsigned int> out;
@ -64,10 +70,12 @@ public:
Pointf3 get_position() const { return m_pos; } Pointf3 get_position() const { return m_pos; }
private: private:
std::vector<Extruder> m_extruders; std::vector<Extruder> m_extruders;
std::vector<ElapsedTime> m_elapsed_times;
std::string m_extrusion_axis; std::string m_extrusion_axis;
bool m_single_extruder_multi_material; bool m_single_extruder_multi_material;
Extruder* m_extruder; Extruder* m_extruder;
ElapsedTime* m_elapsed_time;
unsigned int m_last_acceleration; unsigned int m_last_acceleration;
unsigned int m_last_fan_speed; unsigned int m_last_fan_speed;
unsigned int m_last_bed_temperature; unsigned int m_last_bed_temperature;

View file

@ -550,7 +550,8 @@ std::string Print::validate() const
size_t total_copies_count = 0; size_t total_copies_count = 0;
for (const PrintObject *object : this->objects) for (const PrintObject *object : this->objects)
total_copies_count += object->copies().size(); total_copies_count += object->copies().size();
if (total_copies_count > 1) // #4043
if (total_copies_count > 1 && ! this->config.complete_objects.value)
return "The Spiral Vase option can only be used when printing a single object."; return "The Spiral Vase option can only be used when printing a single object.";
if (this->regions.size() > 1) if (this->regions.size() > 1)
return "The Spiral Vase option can only be used when printing single material objects."; return "The Spiral Vase option can only be used when printing single material objects.";

View file

@ -11,9 +11,7 @@
%code{% RETVAL = new CoolingBuffer(*gcode); %}; %code{% RETVAL = new CoolingBuffer(*gcode); %};
~CoolingBuffer(); ~CoolingBuffer();
Ref<GCode> gcodegen(); Ref<GCode> gcodegen();
std::string process_layer(std::string gcode, size_t layer_id);
std::string append(std::string gcode, size_t object_id, size_t layer_id, bool support_layer);
std::string flush();
}; };
%name{Slic3r::GCode} class GCode { %name{Slic3r::GCode} class GCode {