From 32fa84c5a576febe4fab04f0fb7ffa53aec530ff Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 23 Jun 2017 10:13:09 +0200 Subject: [PATCH] Fix of https://github.com/alexrj/Slic3r/issues/4043 , thanks to @lordofhyphens. Further refactoring of the cooling logic to collect per extruder data. --- t/cooling.t | 37 +++++++------ xs/src/libslic3r/GCode.cpp | 23 ++++---- xs/src/libslic3r/GCode.hpp | 36 +----------- xs/src/libslic3r/GCode/CoolingBuffer.cpp | 70 +++++++----------------- xs/src/libslic3r/GCode/CoolingBuffer.hpp | 51 ++++++++++++++--- xs/src/libslic3r/GCodeWriter.cpp | 9 +++ xs/src/libslic3r/GCodeWriter.hpp | 22 +++++--- xs/src/libslic3r/Print.cpp | 3 +- xs/xsp/GCode.xsp | 4 +- 9 files changed, 123 insertions(+), 132 deletions(-) diff --git a/t/cooling.t b/t/cooling.t index 29996c789..e6426546b 100644 --- a/t/cooling.t +++ b/t/cooling.t @@ -32,20 +32,19 @@ $config->set('disable_fan_first_layers', [ 0 ]); { my $buffer = buffer($config); $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'; } { my $buffer = buffer($config); $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 F3000;_EXTRUDE_SET_SPEED\n" . "G1 X100 E1\n" . "G1 E4 F400", - 0, 0, 0 - ) . $buffer->flush; + 0); 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/F400/, 'speed is not altered for extruder-only moves'; @@ -54,7 +53,7 @@ $config->set('disable_fan_first_layers', [ 0 ]); { my $buffer = buffer($config); $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'; } @@ -62,25 +61,26 @@ $config->set('disable_fan_first_layers', [ 0 ]); my $buffer = buffer($config); 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->gcodegen->config->slowdown_below_layer_time->[0] - 1); - $gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, 0, 0); + $gcode .= "G1 X100 E1 F3000\n"; } - $gcode .= $buffer->flush; - like $gcode, qr/F3000/, 'slowdown is computed on all objects printing at same Z'; + # use an elapsed time which is < the slowdown threshold but greater than it when summed twice + $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 $gcode = ""; for my $layer_id (0 .. 1) { + my $layer_gcode = ""; 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->gcodegen->config->fan_below_layer_time->[0] - 1); - $gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0); + $layer_gcode .= "G1 X100 E1 F3000\n"; } + # 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'; } @@ -88,13 +88,14 @@ $config->set('disable_fan_first_layers', [ 0 ]); my $buffer = buffer($config); my $gcode = ""; for my $layer_id (0 .. 1) { + my $layer_gcode = ""; for my $obj_id (0 .. 1) { - # use an elapsed time which is < the threshold even when summed twice - $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); + $layer_gcode .= "G1 X100 E1 F3000\n"; } + # 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'; } diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index a1678ec68..6ff0775af 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -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. // Inform the G-code writer about the changes done behind its back. 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. if (new_extruder_id >= 0 && gcodegen.writer().need_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. 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)); @@ -470,6 +470,8 @@ bool GCode::do_export(FILE *file, Print &print) assert(final_extruder_id != (unsigned int)-1); } + m_cooling_buffer->set_current_extruder(initial_extruder_id); + // Disable fan. 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)); @@ -568,6 +570,7 @@ bool GCode::do_export(FILE *file, Print &print) initial_extruder_id = new_extruder_id; final_extruder_id = tool_ordering.last_extruder(); 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)); if (finished_objects > 0) { @@ -594,7 +597,6 @@ bool GCode::do_export(FILE *file, Print &print) lrs.emplace_back(std::move(ltp)); 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; // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. // 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(); 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) // Purge the extruder, pull out the active filament. 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 // once all extrusions of a layer are processed at once. // 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)); } @@ -1865,11 +1866,11 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, if (m_config.cooling.values.front()) { float t = path_length / F * 60.f; - m_elapsed_time.total += t; + m_writer.elapsed_time()->total += t; if (is_bridge(path.role())) - m_elapsed_time.bridges += t; + m_writer.elapsed_time()->bridges += t; if (path.role() == erExternalPerimeter) - m_elapsed_time.external_perimeters += t; + m_writer.elapsed_time()->external_perimeters += t; } 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); 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; } diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 3760aae84..c85f28daa 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -107,32 +107,6 @@ private: 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 { public: GCode() : @@ -165,13 +139,12 @@ public: const Layer* layer() const { return m_layer; } GCodeWriter& writer() { return m_writer; } 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. unsigned int layer_count() const { return m_layer_count; } void set_layer_count(unsigned int value) { m_layer_count = value; } - float elapsed_time() const { return m_elapsed_time.total; } - void set_elapsed_time(float value) { m_elapsed_time.total = value; } + float elapsed_time() const { return m_writer.elapsed_time()->total; } + void set_elapsed_time(float value) { std::vector 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); protected: @@ -268,11 +241,6 @@ protected: // In non-sequential mode, all its copies will be printed. const Layer* m_layer; std::map 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; // Support for the extrusion role markers. Which marker is active? ExtrusionRole m_last_extrusion_role; diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.cpp b/xs/src/libslic3r/GCode/CoolingBuffer.cpp index 632bdb6da..4d8a7ef34 100644 --- a/xs/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/xs/src/libslic3r/GCode/CoolingBuffer.cpp @@ -6,36 +6,6 @@ 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) { // 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(); - std::string gcode = std::move(m_gcode); - m_gcode.clear(); + std::string gcode = gcode_src; + 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; - - float speed_factor = 1.0; - bool slowdown_external = true; + const std::vector &elapsed_times = m_gcodegen.writer().elapsed_times(); + ElapsedTime elapsed_time; + for (const ElapsedTime &et : elapsed_times) + elapsed_time += et; if (config.cooling.values.front()) { #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 - 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 // (stretch the layer print time to slowdown_below_layer_time). fan_speed = config.max_fan_speed.values.front(); // We are not altering speed of bridges. - float time_to_stretch = m_elapsed_time->stretchable(); - float target_time = (float)config.slowdown_below_layer_time.values.front() - m_elapsed_time->non_stretchable(); + float time_to_stretch = elapsed_time.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, // otherwise only alter other extrusions. - if (m_elapsed_time->external_perimeters < 0.5f * time_to_stretch) { - time_to_stretch -= m_elapsed_time->external_perimeters; - target_time -= m_elapsed_time->external_perimeters; + if (elapsed_time.external_perimeters < 0.5f * time_to_stretch) { + time_to_stretch -= elapsed_time.external_perimeters; + target_time -= elapsed_time.external_perimeters; slowdown_external = false; } 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. fan_speed = config.max_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()); } @@ -131,13 +103,13 @@ std::string CoolingBuffer::flush() 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; gcode = m_gcodegen.writer().set_fan(fan_speed) + gcode; // 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_END", ""); } else { @@ -149,7 +121,7 @@ std::string CoolingBuffer::flush() boost::replace_all(gcode, ";_EXTERNAL_PERIMETER", ""); m_object_ids_visited.clear(); - m_elapsed_time->reset(); + m_gcodegen.writer().reset_elapsed_times(); return gcode; } diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.hpp b/xs/src/libslic3r/GCode/CoolingBuffer.hpp index da8677e1e..354a51768 100644 --- a/xs/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/xs/src/libslic3r/GCode/CoolingBuffer.hpp @@ -7,10 +7,45 @@ namespace Slic3r { -struct ElapsedTime; class GCode; 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. 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 { public: - CoolingBuffer(GCode &gcodegen); - ~CoolingBuffer(); - std::string append(const std::string &gcode, size_t object_id, size_t layer_id, bool is_support); - std::string flush(); - GCode* gcodegen() { return &m_gcodegen; }; - + CoolingBuffer(GCode &gcodegen) : m_gcodegen(gcodegen) {} + void set_current_extruder(unsigned int extruder_id) { m_current_extruder = extruder_id; } + std::string process_layer(const std::string &gcode, size_t layer_id); + GCode* gcodegen() { return &m_gcodegen; } + private: CoolingBuffer& operator=(const CoolingBuffer&); GCode& m_gcodegen; std::string m_gcode; - ElapsedTime *m_elapsed_time; - size_t m_layer_id; + unsigned int m_current_extruder; std::set m_object_ids_visited; }; diff --git a/xs/src/libslic3r/GCodeWriter.cpp b/xs/src/libslic3r/GCodeWriter.cpp index 06ec5d1af..2de35b664 100644 --- a/xs/src/libslic3r/GCodeWriter.cpp +++ b/xs/src/libslic3r/GCodeWriter.cpp @@ -26,6 +26,11 @@ void GCodeWriter::set_extruders(const std::vector &extruder_ids) m_extruders.reserve(extruder_ids.size()); for (unsigned int extruder_id : extruder_ids) 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 (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()); m_extruder = const_cast(&*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(&*it_elapsed_time); + // return the toolchange command // if we are running a single-extruder setup, just set the extruder and return nothing std::ostringstream gcode; diff --git a/xs/src/libslic3r/GCodeWriter.hpp b/xs/src/libslic3r/GCodeWriter.hpp index 02fca4129..37f15cc53 100644 --- a/xs/src/libslic3r/GCodeWriter.hpp +++ b/xs/src/libslic3r/GCodeWriter.hpp @@ -6,6 +6,7 @@ #include "Extruder.hpp" #include "Point.hpp" #include "PrintConfig.hpp" +#include "GCode/CoolingBuffer.hpp" namespace Slic3r { @@ -15,18 +16,23 @@ public: bool multiple_extruders; 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_last_acceleration(0), m_last_fan_speed(0), m_last_bed_temperature(0), m_last_bed_temperature_reached(true), m_lifted(0) {} - Extruder* extruder() { return m_extruder; } - const Extruder* extruder() const { return m_extruder; } - std::string extrusion_axis() const { return m_extrusion_axis; } - void apply_print_config(const PrintConfig &print_config); + Extruder* extruder() { return m_extruder; } + const Extruder* extruder() const { return m_extruder; } + ElapsedTime* elapsed_time() { return m_elapsed_time; } + const ElapsedTime* elapsed_time() const { return m_elapsed_time; } + const std::vector& 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. - void set_extruders(const std::vector &extruder_ids); + void set_extruders(const std::vector &extruder_ids); const std::vector& extruders() const { return m_extruders; } std::vector extruder_ids() const { std::vector out; @@ -64,10 +70,12 @@ public: Pointf3 get_position() const { return m_pos; } private: - std::vector m_extruders; + std::vector m_extruders; + std::vector m_elapsed_times; std::string m_extrusion_axis; bool m_single_extruder_multi_material; Extruder* m_extruder; + ElapsedTime* m_elapsed_time; unsigned int m_last_acceleration; unsigned int m_last_fan_speed; unsigned int m_last_bed_temperature; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 712570ff9..45c13b52e 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -550,7 +550,8 @@ std::string Print::validate() const size_t total_copies_count = 0; for (const PrintObject *object : this->objects) 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."; if (this->regions.size() > 1) return "The Spiral Vase option can only be used when printing single material objects."; diff --git a/xs/xsp/GCode.xsp b/xs/xsp/GCode.xsp index f634958f2..256799897 100644 --- a/xs/xsp/GCode.xsp +++ b/xs/xsp/GCode.xsp @@ -11,9 +11,7 @@ %code{% RETVAL = new CoolingBuffer(*gcode); %}; ~CoolingBuffer(); Ref gcodegen(); - - std::string append(std::string gcode, size_t object_id, size_t layer_id, bool support_layer); - std::string flush(); + std::string process_layer(std::string gcode, size_t layer_id); }; %name{Slic3r::GCode} class GCode {