Fix of https://github.com/alexrj/Slic3r/issues/4043 , thanks to @lordofhyphens.
Further refactoring of the cooling logic to collect per extruder data.
This commit is contained in:
parent
39b9341359
commit
32fa84c5a5
9 changed files with 123 additions and 132 deletions
37
t/cooling.t
37
t/cooling.t
|
@ -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';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()), © - object._shifted_copies.data());
|
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), © - 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,11 @@ void GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
|
||||||
m_extruders.reserve(extruder_ids.size());
|
m_extruders.reserve(extruder_ids.size());
|
||||||
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
|
||||||
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.";
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue