From 0463518dc3977219f72d755d6116b29191a6f74c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 5 Apr 2022 11:56:10 +0200 Subject: [PATCH 1/3] Attempted to reenable the old pressure equalizer and fix its issues. --- src/libslic3r/CMakeLists.txt | 4 +- src/libslic3r/GCode.cpp | 81 ++++++---- src/libslic3r/GCode.hpp | 6 +- src/libslic3r/GCode/PressureEqualizer.cpp | 186 +++++++++++++--------- src/libslic3r/GCode/PressureEqualizer.hpp | 24 +-- src/libslic3r/Preset.cpp | 2 - src/libslic3r/Print.cpp | 2 - src/libslic3r/PrintConfig.cpp | 4 - src/libslic3r/PrintConfig.hpp | 8 +- src/slic3r/GUI/Tab.cpp | 3 +- 10 files changed, 178 insertions(+), 142 deletions(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 3e9acaced..498c01d87 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -104,8 +104,8 @@ add_library(libslic3r STATIC GCode/FindReplace.hpp GCode/PostProcessor.cpp GCode/PostProcessor.hpp -# GCode/PressureEqualizer.cpp -# GCode/PressureEqualizer.hpp + GCode/PressureEqualizer.cpp + GCode/PressureEqualizer.hpp GCode/PrintExtents.cpp GCode/PrintExtents.hpp GCode/SpiralVase.cpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ff5aff03c..0f8f31e4c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1108,14 +1108,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato if (print.config().spiral_vase.value) m_spiral_vase = make_unique(print.config()); -#ifdef HAS_PRESSURE_EQUALIZER + if (print.config().max_volumetric_extrusion_rate_slope_positive.value > 0 || print.config().max_volumetric_extrusion_rate_slope_negative.value > 0) - m_pressure_equalizer = make_unique(&print.config()); + m_pressure_equalizer = make_unique(print.config()); m_enable_extrusion_role_markers = (bool)m_pressure_equalizer; -#else /* HAS_PRESSURE_EQUALIZER */ - m_enable_extrusion_role_markers = false; -#endif /* HAS_PRESSURE_EQUALIZER */ // Write information on the generator. file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); @@ -1364,10 +1361,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // and export G-code into file. this->process_layers(print, tool_ordering, collect_layers_to_print(object), *print_object_instance_sequential_active - object.instances().data(), file); -#ifdef HAS_PRESSURE_EQUALIZER - if (m_pressure_equalizer) - file.write(m_pressure_equalizer->process("", true)); -#endif /* HAS_PRESSURE_EQUALIZER */ ++ 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. @@ -1424,10 +1417,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // and export G-code into file. this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print, file); -#ifdef HAS_PRESSURE_EQUALIZER - if (m_pressure_equalizer) - file.write(m_pressure_equalizer->process("", true)); -#endif /* HAS_PRESSURE_EQUALIZER */ if (m_wipe_tower) // Purge the extruder, pull out the active filament. file.write(m_wipe_tower->finalize(*this)); @@ -1555,16 +1544,20 @@ void GCode::process_layers( } }); const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&spiral_vase = *this->m_spiral_vase.get()](GCode::LayerResult in) -> GCode::LayerResult { + [&spiral_vase = *this->m_spiral_vase](GCode::LayerResult in) -> GCode::LayerResult { spiral_vase.enable(in.spiral_vase_enable); - return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush }; + return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush, in.pressure_equalizer_buffer_flush }; + }); + const auto pressure_equalizer = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&pressure_equalizer = *this->m_pressure_equalizer](GCode::LayerResult in) -> GCode::LayerResult { + return { std::string(pressure_equalizer.process_layer(std::move(in.gcode.c_str()), in.pressure_equalizer_buffer_flush)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush, in.pressure_equalizer_buffer_flush }; }); const auto cooling = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string { - return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); + [&cooling_buffer = *this->m_cooling_buffer](GCode::LayerResult in) -> std::string { + return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); }); const auto find_replace = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&self = *this->m_find_replace.get()](std::string s) -> std::string { + [&self = *this->m_find_replace](std::string s) -> std::string { return self.process_layer(std::move(s)); }); const auto output = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, @@ -1577,14 +1570,22 @@ void GCode::process_layers( // The pipeline elements are joined using const references, thus no copying is performed. output_stream.find_replace_supress(); - if (m_spiral_vase && m_find_replace) - tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output); + if (m_spiral_vase && m_find_replace && m_pressure_equalizer) + tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & find_replace & output); + else if (m_spiral_vase && m_find_replace) + tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output); + else if (m_spiral_vase && m_pressure_equalizer) + tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & output); + else if (m_find_replace && m_pressure_equalizer) + tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & find_replace & output); else if (m_spiral_vase) - tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output); + tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output); else if (m_find_replace) - tbb::parallel_pipeline(12, generator & cooling & find_replace & output); + tbb::parallel_pipeline(12, generator & cooling & find_replace & output); + else if (m_pressure_equalizer) + tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & output); else - tbb::parallel_pipeline(12, generator & cooling & output); + tbb::parallel_pipeline(12, generator & cooling & output); output_stream.find_replace_enable(); } @@ -1612,16 +1613,20 @@ void GCode::process_layers( } }); const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&spiral_vase = *this->m_spiral_vase.get()](GCode::LayerResult in)->GCode::LayerResult { + [&spiral_vase = *this->m_spiral_vase](GCode::LayerResult in)->GCode::LayerResult { spiral_vase.enable(in.spiral_vase_enable); - return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush }; + return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush, in.pressure_equalizer_buffer_flush }; + }); + const auto pressure_equalizer = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&pressure_equalizer = *this->m_pressure_equalizer](GCode::LayerResult in) -> GCode::LayerResult { + return { std::string(pressure_equalizer.process_layer(std::move(in.gcode.c_str()), in.pressure_equalizer_buffer_flush)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush, in.pressure_equalizer_buffer_flush }; }); const auto cooling = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in)->std::string { + [&cooling_buffer = *this->m_cooling_buffer](GCode::LayerResult in)->std::string { return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); }); const auto find_replace = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&self = *this->m_find_replace.get()](std::string s) -> std::string { + [&self = *this->m_find_replace](std::string s) -> std::string { return self.process_layer(std::move(s)); }); const auto output = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, @@ -1634,14 +1639,22 @@ void GCode::process_layers( // The pipeline elements are joined using const references, thus no copying is performed. output_stream.find_replace_supress(); - if (m_spiral_vase && m_find_replace) - tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output); + if (m_spiral_vase && m_find_replace && m_pressure_equalizer) + tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & find_replace & output); + else if (m_spiral_vase && m_find_replace) + tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output); + else if (m_spiral_vase && m_pressure_equalizer) + tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & output); + else if (m_find_replace && m_pressure_equalizer) + tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & find_replace & output); else if (m_spiral_vase) - tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output); + tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output); else if (m_find_replace) - tbb::parallel_pipeline(12, generator & cooling & find_replace & output); + tbb::parallel_pipeline(12, generator & cooling & find_replace & output); + else if (m_pressure_equalizer) + tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & output); else - tbb::parallel_pipeline(12, generator & cooling & output); + tbb::parallel_pipeline(12, generator & cooling & output); output_stream.find_replace_enable(); } @@ -2097,7 +2110,7 @@ GCode::LayerResult GCode::process_layer( } } const Layer &layer = (object_layer != nullptr) ? *object_layer : *support_layer; - GCode::LayerResult result { {}, layer.id(), false, last_layer }; + GCode::LayerResult result { {}, layer.id(), false, last_layer, last_layer}; if (layer_tools.extruders.empty()) // Nothing to extrude. return result; @@ -2480,13 +2493,11 @@ GCode::LayerResult GCode::process_layer( // Flush the cooling buffer at each object layer or possibly at the last layer, even if it contains just supports (This should not happen). object_layer || last_layer); -#ifdef HAS_PRESSURE_EQUALIZER // Apply pressure equalization if enabled; // printf("G-code before filter:\n%s\n", gcode.c_str()); if (m_pressure_equalizer) gcode = m_pressure_equalizer->process(gcode.c_str(), false); // printf("G-code after filter:\n%s\n", out.c_str()); -#endif /* HAS_PRESSURE_EQUALIZER */ file.write(gcode); #endif diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 08b212007..d01fa8e3e 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -23,9 +23,7 @@ #include #include -#ifdef HAS_PRESSURE_EQUALIZER #include "GCode/PressureEqualizer.hpp" -#endif /* HAS_PRESSURE_EQUALIZER */ namespace Slic3r { @@ -235,6 +233,8 @@ private: bool spiral_vase_enable { false }; // Should the cooling buffer content be flushed at the end of this layer? bool cooling_buffer_flush { false }; + // Should the PressureEqualizer buffer content be flushed at the end of this layer? + bool pressure_equalizer_buffer_flush { false }; }; LayerResult process_layer( const Print &print, @@ -406,9 +406,7 @@ private: std::unique_ptr m_cooling_buffer; std::unique_ptr m_spiral_vase; std::unique_ptr m_find_replace; -#ifdef HAS_PRESSURE_EQUALIZER std::unique_ptr m_pressure_equalizer; -#endif /* HAS_PRESSURE_EQUALIZER */ std::unique_ptr m_wipe_tower; // Heights (print_z) at which the skirt has already been extruded. diff --git a/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp index 48a16a8d5..f7d816695 100644 --- a/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/src/libslic3r/GCode/PressureEqualizer.cpp @@ -7,19 +7,24 @@ #include "../LocalesUtils.hpp" #include "PressureEqualizer.hpp" +#include "fast_float/fast_float.h" namespace Slic3r { -PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig *config) : - m_config(config) +static constexpr const std::array EXTRUSION_ROLE_TAG = {";_EXTRUSION_ROLE:"}; +static constexpr const size_t EXTRUSION_ROLE_TAG_LENGTH = EXTRUSION_ROLE_TAG.size() - 1; +static constexpr const std::array EXTRUDE_END_TAG = {";_EXTRUDE_END"}; +static constexpr const size_t EXTRUDE_END_TAG_LENGTH = EXTRUDE_END_TAG.size() - 1; +static constexpr const std::array EXTRUDE_SET_SPEED_TAG = {";_EXTRUDE_SET_SPEED"}; +static constexpr const size_t EXTRUDE_SET_SPEED_TAG_LENGTH = EXTRUDE_SET_SPEED_TAG.size() - 1; +static constexpr const std::array EXTERNAL_PERIMETER_TAG = {";_EXTERNAL_PERIMETER"}; +static constexpr const size_t EXTERNAL_PERIMETER_TAG_LENGTH = EXTERNAL_PERIMETER_TAG.size() - 1; + +PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_config(config) { reset(); } -PressureEqualizer::~PressureEqualizer() -{ -} - void PressureEqualizer::reset() { circular_buffer_pos = 0; @@ -40,9 +45,8 @@ void PressureEqualizer::reset() // Calculate filamet crossections for the multiple extruders. m_filament_crossections.clear(); - for (size_t i = 0; i < m_config->filament_diameter.values.size(); ++ i) { - double r = m_config->filament_diameter.values[i]; - double a = 0.25f*M_PI*r*r; + for (double r : m_config.filament_diameter.values) { + double a = 0.25f * M_PI * r * r; m_filament_crossections.push_back(float(a)); } @@ -50,10 +54,8 @@ void PressureEqualizer::reset() // Volumetric rate of a 0.45mm x 0.2mm extrusion at 60mm/s XY movement: 0.45*0.2*60*60=5.4*60 = 324 mm^3/min // Volumetric rate of a 0.45mm x 0.2mm extrusion at 20mm/s XY movement: 0.45*0.2*20*60=1.8*60 = 108 mm^3/min // Slope of the volumetric rate, changing from 20mm/s to 60mm/s over 2 seconds: (5.4-1.8)*60*60/2=60*60*1.8 = 6480 mm^3/min^2 = 1.8 mm^3/s^2 - m_max_volumetric_extrusion_rate_slope_positive = (m_config == NULL) ? 6480.f : - m_config->max_volumetric_extrusion_rate_slope_positive.value * 60.f * 60.f; - m_max_volumetric_extrusion_rate_slope_negative = (m_config == NULL) ? 6480.f : - m_config->max_volumetric_extrusion_rate_slope_negative.value * 60.f * 60.f; + m_max_volumetric_extrusion_rate_slope_positive = float(m_config.max_volumetric_extrusion_rate_slope_positive.value) * 60.f * 60.f; + m_max_volumetric_extrusion_rate_slope_negative = float(m_config.max_volumetric_extrusion_rate_slope_negative.value) * 60.f * 60.f; for (size_t i = 0; i < numExtrusionRoles; ++ i) { m_max_volumetric_extrusion_rate_slopes[i].negative = m_max_volumetric_extrusion_rate_slope_negative; @@ -67,16 +69,19 @@ void PressureEqualizer::reset() m_max_volumetric_extrusion_rate_slopes[erGapFill].negative = 0; m_max_volumetric_extrusion_rate_slopes[erGapFill].positive = 0; +#ifdef PRESSURE_EQUALIZER_STATISTIC m_stat.reset(); +#endif + line_idx = 0; } -const char* PressureEqualizer::process(const char *szGCode, bool flush) +const char* PressureEqualizer::process_layer(const char *szGCode, bool flush) { // Reset length of the output_buffer. output_buffer_length = 0; - if (szGCode != 0) { + if (szGCode != nullptr) { const char *p = szGCode; while (*p != 0) { // Find end of the line. @@ -91,7 +96,7 @@ const char* PressureEqualizer::process(const char *szGCode, bool flush) // Process a G-code line, store it into the provided GCodeLine object. size_t idx_tail = circular_buffer_pos; circular_buffer_pos = circular_buffer_idx_next(circular_buffer_pos); - if (! process_line(p, endl - p, circular_buffer[idx_tail])) { + if (! process_line(p, endl, circular_buffer[idx_tail])) { // The line has to be forgotten. It contains comment marks, which shall be // filtered out of the target g-code. circular_buffer_pos = idx_tail; @@ -114,8 +119,8 @@ const char* PressureEqualizer::process(const char *szGCode, bool flush) assert(circular_buffer_items == 0); circular_buffer_pos = 0; -#if 1 - printf("Statistics: \n"); +#ifdef PRESSURE_EQUALIZER_STATISTIC + printf("Statistics: \n"); printf("Minimum volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_min); printf("Maximum volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_max); if (m_stat.extrusion_length > 0) @@ -125,7 +130,10 @@ const char* PressureEqualizer::process(const char *szGCode, bool flush) #endif } - return output_buffer.data(); + if (output_buffer_length > 0) + return output_buffer.data(); + + return "\0"; } // Is a white space? @@ -146,32 +154,41 @@ static void eatws(const char *&line) // If succeeded, the line pointer is advanced. static inline int parse_int(const char *&line) { - char *endptr = NULL; + char *endptr = nullptr; long result = strtol(line, &endptr, 10); - if (endptr == NULL || !is_ws_or_eol(*endptr)) + if (endptr == nullptr || !is_ws_or_eol(*endptr)) throw Slic3r::RuntimeError("PressureEqualizer: Error parsing an int"); line = endptr; return int(result); }; +float string_to_float_decimal_point(const char *line, const size_t str_len, size_t* pos) +{ + float out; + size_t p = fast_float::from_chars(line, line + str_len, out).ptr - line; + if (pos) + *pos = p; + return out; +} + // Parse an int starting at the current position of a line. // If succeeded, the line pointer is advanced. -static inline float parse_float(const char *&line) +static inline float parse_float(const char *&line, const size_t line_length) { - char *endptr = NULL; - float result = string_to_double_decimal_point(line, &endptr); - if (endptr == NULL || !is_ws_or_eol(*endptr)) + size_t endptr = 0; + auto result = string_to_float_decimal_point(line, line_length, &endptr); + if (endptr == 0 || !is_ws_or_eol(*(line + endptr))) throw Slic3r::RuntimeError("PressureEqualizer: Error parsing a float"); - line = endptr; + line = line + endptr; return result; }; -bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLine &buf) +bool PressureEqualizer::process_line(const char *line, const char *line_end, GCodeLine &buf) { - static constexpr const char *EXTRUSION_ROLE_TAG = ";_EXTRUSION_ROLE:"; + const size_t len = line_end - line; - if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) { - line += strlen(EXTRUSION_ROLE_TAG); + if (strncmp(line, EXTRUSION_ROLE_TAG.data(), EXTRUSION_ROLE_TAG_LENGTH) == 0) { + line += EXTRUSION_ROLE_TAG_LENGTH; int role = atoi(line); m_current_extrusion_role = ExtrusionRole(role); ++ line_idx; @@ -233,8 +250,8 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi if (i == -1) throw Slic3r::RuntimeError(std::string("GCode::PressureEqualizer: Invalid axis for G0/G1: ") + axis); buf.pos_provided[i] = true; - new_pos[i] = parse_float(line); - if (i == 3 && m_config->use_relative_e_distances.value) + new_pos[i] = parse_float(line, line_end - line); + if (i == 3 && m_config.use_relative_e_distances.value) new_pos[i] += m_current_pos[i]; changed[i] = new_pos[i] != m_current_pos[i]; eatws(line); @@ -263,15 +280,17 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi buf.volumetric_extrusion_rate = rate; buf.volumetric_extrusion_rate_start = rate; buf.volumetric_extrusion_rate_end = rate; + +#ifdef PRESSURE_EQUALIZER_STATISTIC m_stat.update(rate, sqrt(len2)); +#endif +#ifdef PRESSURE_EQUALIZER_DEBUG if (rate < 40.f) { - printf("Extremely low flow rate: %f. Line %d, Length: %f, extrusion: %f Old position: (%f, %f, %f), new position: (%f, %f, %f)\n", - rate, - int(line_idx), - sqrt(len2), sqrt((diff[3]*diff[3])/len2), - m_current_pos[0], m_current_pos[1], m_current_pos[2], - new_pos[0], new_pos[1], new_pos[2]); + printf("Extremely low flow rate: %f. Line %d, Length: %f, extrusion: %f Old position: (%f, %f, %f), new position: (%f, %f, %f)\n", + rate, int(line_idx), sqrt(len2), sqrt((diff[3] * diff[3]) / len2), m_current_pos[0], m_current_pos[1], m_current_pos[2], + new_pos[0], new_pos[1], new_pos[2]); } +#endif } } else if (changed[0] || changed[1] || changed[2]) { // Moving without extrusion. @@ -292,11 +311,11 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi case 'X': case 'Y': case 'Z': - m_current_pos[axis - 'X'] = (!is_ws_or_eol(*line)) ? parse_float(line) : 0.f; + m_current_pos[axis - 'X'] = (!is_ws_or_eol(*line)) ? parse_float(line, line_end - line) : 0.f; set = true; break; case 'E': - m_current_pos[3] = (!is_ws_or_eol(*line)) ? parse_float(line) : 0.f; + m_current_pos[3] = (!is_ws_or_eol(*line)) ? parse_float(line, line_end - line) : 0.f; set = true; break; default: @@ -360,7 +379,7 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi void PressureEqualizer::output_gcode_line(GCodeLine &line) { - if (! line.modified) { + if (!line.modified) { push_to_output(line.raw.data(), line.raw_length, true); return; } @@ -370,14 +389,11 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line) const char *comment = line.raw.data(); while (*comment != ';' && *comment != 0) ++comment; if (*comment != ';') - comment = NULL; - + comment = nullptr; + // Emit the line with lowered extrusion rates. - float l2 = line.dist_xyz2(); - float l = sqrt(l2); - size_t nSegments = size_t(ceil(l / m_max_segment_length)); - if (nSegments == 1) { - // Just update this segment. + float l = line.dist_xyz(); + if (auto nSegments = size_t(ceil(l / m_max_segment_length)); nSegments == 1) { // Just update this segment. push_line_to_output(line, line.feedrate() * line.volumetric_correction_avg(), comment); } else { bool accelerating = line.volumetric_extrusion_rate_start < line.volumetric_extrusion_rate_end; @@ -386,8 +402,8 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line) line.pos_end [4] = line.volumetric_extrusion_rate_end * line.pos_end[4] / line.volumetric_extrusion_rate; float feed_avg = 0.5f * (line.pos_start[4] + line.pos_end[4]); // Limiting volumetric extrusion rate slope for this segment. - float max_volumetric_extrusion_rate_slope = accelerating ? - line.max_volumetric_extrusion_rate_slope_positive : line.max_volumetric_extrusion_rate_slope_negative; + float max_volumetric_extrusion_rate_slope = accelerating ? line.max_volumetric_extrusion_rate_slope_positive : + line.max_volumetric_extrusion_rate_slope_negative; // Total time for the segment, corrected for the possibly lowered volumetric feed rate, // if accelerating / decelerating over the complete segment. float t_total = line.dist_xyz() / feed_avg; @@ -398,8 +414,8 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line) float l_steady = 0.f; if (t_acc < t_total) { // One may achieve higher print speeds if part of the segment is not speed limited. - float l_acc = t_acc * feed_avg; - float l_steady = l - l_acc; + l_acc = t_acc * feed_avg; + l_steady = l - l_acc; if (l_steady < 0.5f * m_max_segment_length) { l_acc = l; l_steady = 0.f; @@ -407,10 +423,10 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line) nSegments = size_t(ceil(l_acc / m_max_segment_length)); } float pos_start[5]; - float pos_end [5]; - float pos_end2 [4]; - memcpy(pos_start, line.pos_start, sizeof(float)*5); - memcpy(pos_end , line.pos_end , sizeof(float)*5); + float pos_end[5]; + float pos_end2[4]; + memcpy(pos_start, line.pos_start, sizeof(float) * 5); + memcpy(pos_end, line.pos_end, sizeof(float) * 5); if (l_steady > 0.f) { // There will be a steady feed segment emitted. if (accelerating) { @@ -429,9 +445,15 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line) line.pos_provided[i] = true; } push_line_to_output(line, pos_start[4], comment); - comment = NULL; + comment = nullptr; + + float new_pos_start_feedrate = pos_start[4]; + memcpy(line.pos_start, line.pos_end, sizeof(float)*5); memcpy(pos_start, line.pos_end, sizeof(float)*5); + + line.pos_start[4] = new_pos_start_feedrate; + pos_start[4] = new_pos_start_feedrate; } } // Split the segment into pieces. @@ -443,7 +465,7 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line) } // Interpolate the feed rate at the center of the segment. push_line_to_output(line, pos_start[4] + (pos_end[4] - pos_start[4]) * (float(i) - 0.5f) / float(nSegments), comment); - comment = NULL; + comment = nullptr; memcpy(line.pos_start, line.pos_end, sizeof(float)*5); } if (l_steady > 0.f && accelerating) { @@ -452,6 +474,12 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line) line.pos_provided[i] = true; } push_line_to_output(line, pos_end[4], comment); + } else { + for (int i = 0; i < 4; ++ i) { + line.pos_end[i] = pos_end[i]; + line.pos_provided[i] = true; + } + push_line_to_output(line, pos_end[4], comment); } } } @@ -477,9 +505,9 @@ void PressureEqualizer::adjust_volumetric_rate() bool modified = true; while (modified && idx != idx_head) { size_t idx_prev = circular_buffer_idx_prev(idx); - for (; ! circular_buffer[idx_prev].extruding() && idx_prev != idx_head; idx_prev = circular_buffer_idx_prev(idx_prev)) ; - if (! circular_buffer[idx_prev].extruding()) - break; + for (; !circular_buffer[idx_prev].extruding() && idx_prev != idx_head; idx_prev = circular_buffer_idx_prev(idx_prev)); + if (!circular_buffer[idx_prev].extruding()) + break; // Volumetric extrusion rate at the start of the succeding segment. float rate_succ = circular_buffer[idx].volumetric_extrusion_rate_start; // What is the gradient of the extrusion rate between idx_prev and idx? @@ -496,6 +524,7 @@ void PressureEqualizer::adjust_volumetric_rate() rate_end = rate_succ; if (line.volumetric_extrusion_rate_end > rate_end) { line.volumetric_extrusion_rate_end = rate_end; + line.max_volumetric_extrusion_rate_slope_negative = rate_slope; line.modified = true; } else if (iRole == line.extrusion_role) { rate_end = line.volumetric_extrusion_rate_end; @@ -508,7 +537,7 @@ void PressureEqualizer::adjust_volumetric_rate() // modified = false; float rate_start = rate_end + rate_slope * line.time_corrected(); if (rate_start < line.volumetric_extrusion_rate_start) { - // Limit the volumetric extrusion rate at the start of this segment due to a segment + // Limit the volumetric extrusion rate at the start of this segment due to a segment // of ExtrusionType iRole, which will be extruded in the future. line.volumetric_extrusion_rate_start = rate_start; line.max_volumetric_extrusion_rate_slope_negative = rate_slope; @@ -520,16 +549,16 @@ void PressureEqualizer::adjust_volumetric_rate() } // Go forward and adjust the feedrate to decrease the slope of the extrusion rate changes. - for (size_t i = 0; i < numExtrusionRoles; ++ i) + for (size_t i = 0; i < numExtrusionRoles; ++i) feedrate_per_extrusion_role[i] = FLT_MAX; feedrate_per_extrusion_role[circular_buffer[idx].extrusion_role] = circular_buffer[idx].volumetric_extrusion_rate_end; assert(circular_buffer[idx].extruding()); while (idx != idx_tail) { size_t idx_next = circular_buffer_idx_next(idx); - for (; ! circular_buffer[idx_next].extruding() && idx_next != idx_tail; idx_next = circular_buffer_idx_next(idx_next)) ; - if (! circular_buffer[idx_next].extruding()) - break; + for (; !circular_buffer[idx_next].extruding() && idx_next != idx_tail; idx_next = circular_buffer_idx_next(idx_next)); + if (!circular_buffer[idx_next].extruding()) + break; float rate_prec = circular_buffer[idx].volumetric_extrusion_rate_end; // What is the gradient of the extrusion rate between idx_prev and idx? idx = idx_next; @@ -544,6 +573,7 @@ void PressureEqualizer::adjust_volumetric_rate() rate_start = rate_prec; if (line.volumetric_extrusion_rate_start > rate_start) { line.volumetric_extrusion_rate_start = rate_start; + line.max_volumetric_extrusion_rate_slope_positive = rate_slope; line.modified = true; } else if (iRole == line.extrusion_role) { rate_start = line.volumetric_extrusion_rate_start; @@ -555,7 +585,7 @@ void PressureEqualizer::adjust_volumetric_rate() } float rate_end = (rate_slope == 0) ? FLT_MAX : rate_start + rate_slope * line.time_corrected(); if (rate_end < line.volumetric_extrusion_rate_end) { - // Limit the volumetric extrusion rate at the start of this segment due to a segment + // Limit the volumetric extrusion rate at the start of this segment due to a segment // of ExtrusionType iRole, which was extruded before. line.volumetric_extrusion_rate_end = rate_end; line.max_volumetric_extrusion_rate_slope_positive = rate_slope; @@ -569,9 +599,7 @@ void PressureEqualizer::adjust_volumetric_rate() void PressureEqualizer::push_axis_to_output(const char axis, const float value, bool add_eol) { char buf[2048]; - int len = sprintf(buf, - (axis == 'E') ? " %c%.3f" : " %c%.5f", - axis, value); + int len = sprintf(buf, (axis == 'E') ? " %c%.3f" : " %c%.5f", axis, value); push_to_output(buf, len, add_eol); } @@ -609,15 +637,23 @@ void PressureEqualizer::push_to_output(const char *text, const size_t len, bool void PressureEqualizer::push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment) { + push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG_LENGTH, true); + + push_to_output("G1", 2, false); + push_axis_to_output('F', new_feedrate); + + push_to_output(EXTRUDE_SET_SPEED_TAG.data(), EXTRUDE_SET_SPEED_TAG_LENGTH, line.extrusion_role != erExternalPerimeter); + if (line.extrusion_role == erExternalPerimeter) + push_to_output(EXTERNAL_PERIMETER_TAG.data(), EXTERNAL_PERIMETER_TAG_LENGTH, true); + push_to_output("G1", 2, false); for (char i = 0; i < 3; ++ i) if (line.pos_provided[i]) push_axis_to_output('X'+i, line.pos_end[i]); - push_axis_to_output('E', m_config->use_relative_e_distances.value ? (line.pos_end[3] - line.pos_start[3]) : line.pos_end[3]); -// if (line.pos_provided[4] || fabs(line.feedrate() - new_feedrate) > 1e-5) - push_axis_to_output('F', new_feedrate); + push_axis_to_output('E', m_config.use_relative_e_distances.value ? (line.pos_end[3] - line.pos_start[3]) : line.pos_end[3]); + // output comment and EOL - push_to_output(comment, (comment == NULL) ? 0 : strlen(comment), true); -} + push_to_output(comment, (comment == nullptr) ? 0 : strlen(comment), true); +} } // namespace Slic3r diff --git a/src/libslic3r/GCode/PressureEqualizer.hpp b/src/libslic3r/GCode/PressureEqualizer.hpp index 13cdc9418..37a109725 100644 --- a/src/libslic3r/GCode/PressureEqualizer.hpp +++ b/src/libslic3r/GCode/PressureEqualizer.hpp @@ -7,18 +7,21 @@ namespace Slic3r { +//#define PRESSURE_EQUALIZER_STATISTIC +//#define PRESSURE_EQUALIZER_DEBUG + // Processes a G-code. Finds changes in the volumetric extrusion speed and adjusts the transitions // between these paths to limit fast changes in the volumetric extrusion speed. class PressureEqualizer { public: - PressureEqualizer(const Slic3r::GCodeConfig *config); - ~PressureEqualizer(); + explicit PressureEqualizer(const Slic3r::GCodeConfig &config); + ~PressureEqualizer() = default; void reset(); // Process a next batch of G-code lines. Flush the internal buffers if asked for. - const char* process(const char *szGCode, bool flush); + const char* process_layer(const char *szGCode, bool flush); size_t get_output_buffer_length() const { return output_buffer_length; } @@ -43,10 +46,12 @@ private: float extrusion_length; }; +#ifdef PRESSURE_EQUALIZER_STATISTIC struct Statistics m_stat; +#endif // Keeps the reference, does not own the config. - const Slic3r::GCodeConfig *m_config; + const Slic3r::GCodeConfig &m_config; // Private configuration values // How fast could the volumetric extrusion rate increase / decrase? mm^3/sec^2 @@ -72,8 +77,7 @@ private: ExtrusionRole m_current_extrusion_role; bool m_retracted; - enum GCodeLineType - { + enum GCodeLineType { GCODELINETYPE_INVALID, GCODELINETYPE_NOOP, GCODELINETYPE_OTHER, @@ -170,7 +174,7 @@ private: // For debugging purposes. Index of the G-code line processed. size_t line_idx; - bool process_line(const char *line, const size_t len, GCodeLine &buf); + bool process_line(const char *line, const char *line_end, GCodeLine &buf); void output_gcode_line(GCodeLine &buf); // Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes. @@ -178,11 +182,11 @@ private: void adjust_volumetric_rate(); // Push the text to the end of the output_buffer. - void push_to_output(const char *text, const size_t len, bool add_eol = true); + void push_to_output(const char *text, size_t len, bool add_eol = true); // Push an axis assignment to the end of the output buffer. - void push_axis_to_output(const char axis, const float value, bool add_eol = false); + void push_axis_to_output(char axis, float value, bool add_eol = false); // Push a G-code line to the output, - void push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment); + void push_line_to_output(const GCodeLine &line, float new_feedrate, const char *comment); size_t circular_buffer_idx_head() const { size_t idx = circular_buffer_pos + circular_buffer_size - circular_buffer_items; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 13c2384e7..4664cffc1 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -427,9 +427,7 @@ static std::vector s_Preset_print_options { "ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing", "max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour", "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist", -#ifdef HAS_PRESSURE_EQUALIZER "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", -#endif /* HAS_PRESSURE_EQUALIZER */ "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed", "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", "bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 882aca997..de31301d8 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -103,10 +103,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "min_print_speed", "max_print_speed", "max_volumetric_speed", -#ifdef HAS_PRESSURE_EQUALIZER "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", -#endif /* HAS_PRESSURE_EQUALIZER */ "notes", "only_retract_when_crossing_perimeters", "output_filename_format", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0d2e6d5f8..9b5581417 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1802,7 +1802,6 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0)); -#ifdef HAS_PRESSURE_EQUALIZER def = this->add("max_volumetric_extrusion_rate_slope_positive", coFloat); def->label = L("Max volumetric slope positive"); def->tooltip = L("This experimental setting is used to limit the speed of change in extrusion rate. " @@ -1824,7 +1823,6 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0)); -#endif /* HAS_PRESSURE_EQUALIZER */ def = this->add("min_fan_speed", coInts); def->label = L("Min"); @@ -4008,9 +4006,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va "start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start", "seal_position", "vibration_limit", "bed_size", "print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe" -#ifndef HAS_PRESSURE_EQUALIZER , "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", -#endif /* HAS_PRESSURE_EQUALIZER */ "serial_port", "serial_speed", // Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed. "fuzzy_skin_perimeter_mode", "fuzzy_skin_shape", diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index af02c840f..3006f0954 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -27,8 +27,6 @@ #include #include -// #define HAS_PRESSURE_EQUALIZER - namespace Slic3r { enum GCodeFlavor : unsigned char { @@ -670,10 +668,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionString, layer_gcode)) ((ConfigOptionFloat, max_print_speed)) ((ConfigOptionFloat, max_volumetric_speed)) -//#ifdef HAS_PRESSURE_EQUALIZER -// ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_positive)) -// ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_negative)) -//#endif + ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_positive)) + ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_negative)) ((ConfigOptionPercents, retract_before_wipe)) ((ConfigOptionFloats, retract_length)) ((ConfigOptionFloats, retract_length_toolchange)) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 179081207..fc9440e45 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1611,10 +1611,9 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Autospeed (advanced)")); optgroup->append_single_option_line("max_print_speed", "max-volumetric-speed_127176"); optgroup->append_single_option_line("max_volumetric_speed", "max-volumetric-speed_127176"); -#ifdef HAS_PRESSURE_EQUALIZER + optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_positive"); optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_negative"); -#endif /* HAS_PRESSURE_EQUALIZER */ page = add_options_page(L("Multiple Extruders"), "funnel"); optgroup = page->new_optgroup(L("Extruders")); From a497769558a67f2921294c8a1172272eecf61b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 6 Jun 2022 08:58:54 +0200 Subject: [PATCH 2/3] Modified the description of pressure equalizer parameters and moved it to the separate section. --- src/libslic3r/PrintConfig.cpp | 10 ++++++---- src/slic3r/GUI/Tab.cpp | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 9b5581417..180b58a62 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1804,7 +1804,8 @@ void PrintConfigDef::init_fff_params() def = this->add("max_volumetric_extrusion_rate_slope_positive", coFloat); def->label = L("Max volumetric slope positive"); - def->tooltip = L("This experimental setting is used to limit the speed of change in extrusion rate. " + def->tooltip = L("This experimental setting is used to limit the speed of change in extrusion rate" + "for a transition from lower speed to higher speed. " "A value of 1.8 mm³/s² ensures, that a change from the extrusion rate " "of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 20 mm/s) " "to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds."); @@ -1815,10 +1816,11 @@ void PrintConfigDef::init_fff_params() def = this->add("max_volumetric_extrusion_rate_slope_negative", coFloat); def->label = L("Max volumetric slope negative"); - def->tooltip = L("This experimental setting is used to limit the speed of change in extrusion rate. " + def->tooltip = L("This experimental setting is used to limit the speed of change in extrusion rate" + "for a transition from higher speed to lower speed. " "A value of 1.8 mm³/s² ensures, that a change from the extrusion rate " - "of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 20 mm/s) " - "to 5.4 mm³/s (feedrate 60 mm/s) will take at least 2 seconds."); + "of 1.8 mm³/s (0.45mm extrusion width, 0.2mm extrusion height, feedrate 60 mm/s) " + "to 5.4 mm³/s (feedrate 20 mm/s) will take at least 2 seconds."); def->sidetext = L("mm³/s²"); def->min = 0; def->mode = comExpert; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index fc9440e45..4530068e1 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1612,6 +1612,7 @@ void TabPrint::build() optgroup->append_single_option_line("max_print_speed", "max-volumetric-speed_127176"); optgroup->append_single_option_line("max_volumetric_speed", "max-volumetric-speed_127176"); + optgroup = page->new_optgroup(L("Pressure equalizer (experimental)")); optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_positive"); optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_negative"); From 9c07218d820985a2c8cd3a606392c9317ad8b466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 9 Jun 2022 21:00:14 +0200 Subject: [PATCH 3/3] Reworked pressure equalizer and GCode processing pipeline to make pressure equalizer always returns one whole layer at once. Now pressure equalizer is returning one layer back (the previous layer). GCode produced by pressure equalizer now has the same number of decimal places as non-processed GCode. Pressure equalizer was disabled for external perimeters and gap-fill. --- src/libslic3r/GCode.cpp | 80 ++++-- src/libslic3r/GCode.hpp | 25 +- src/libslic3r/GCode/PressureEqualizer.cpp | 310 +++++++++++----------- src/libslic3r/GCode/PressureEqualizer.hpp | 90 +++---- 4 files changed, 253 insertions(+), 252 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0f8f31e4c..a6506db60 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1529,11 +1529,18 @@ void GCode::process_layers( { // The pipeline is variable: The vase mode filter is optional. size_t layer_to_print_idx = 0; - const auto generator = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &layer_to_print_idx](tbb::flow_control& fc) -> GCode::LayerResult { - if (layer_to_print_idx == layers_to_print.size()) { - fc.stop(); - return {}; + const auto generator = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &layer_to_print_idx](tbb::flow_control& fc) -> LayerResult { + if (layer_to_print_idx >= layers_to_print.size()) { + if ((!m_pressure_equalizer && layer_to_print_idx == layers_to_print.size()) || (m_pressure_equalizer && layer_to_print_idx == (layers_to_print.size() + 1))) { + fc.stop(); + return {}; + } else { + // Pressure equalizer need insert empty input. Because it returns one layer back. + // Insert NOP (no operation) layer; + ++layer_to_print_idx; + return LayerResult::make_nop_layer_result(); + } } else { const std::pair>& layer = layers_to_print[layer_to_print_idx++]; const LayerTools& layer_tools = tool_ordering.tools_for_layer(layer.first); @@ -1543,17 +1550,23 @@ void GCode::process_layers( return this->process_layer(print, layer.second, layer_tools, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1)); } }); - const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&spiral_vase = *this->m_spiral_vase](GCode::LayerResult in) -> GCode::LayerResult { + const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&spiral_vase = *this->m_spiral_vase](LayerResult in) -> LayerResult { + if (in.nop_layer_result) + return in; + spiral_vase.enable(in.spiral_vase_enable); - return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush, in.pressure_equalizer_buffer_flush }; + return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush}; }); - const auto pressure_equalizer = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&pressure_equalizer = *this->m_pressure_equalizer](GCode::LayerResult in) -> GCode::LayerResult { - return { std::string(pressure_equalizer.process_layer(std::move(in.gcode.c_str()), in.pressure_equalizer_buffer_flush)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush, in.pressure_equalizer_buffer_flush }; + const auto pressure_equalizer = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&pressure_equalizer = *this->m_pressure_equalizer](LayerResult in) -> LayerResult { + return pressure_equalizer.process_layer(std::move(in)); }); - const auto cooling = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&cooling_buffer = *this->m_cooling_buffer](GCode::LayerResult in) -> std::string { + const auto cooling = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&cooling_buffer = *this->m_cooling_buffer](LayerResult in) -> std::string { + if (in.nop_layer_result) + return in.gcode; + return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); }); const auto find_replace = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, @@ -1601,28 +1614,39 @@ void GCode::process_layers( { // The pipeline is variable: The vase mode filter is optional. size_t layer_to_print_idx = 0; - const auto generator = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx](tbb::flow_control& fc) -> GCode::LayerResult { - if (layer_to_print_idx == layers_to_print.size()) { - fc.stop(); - return {}; + const auto generator = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx](tbb::flow_control& fc) -> LayerResult { + if (layer_to_print_idx >= layers_to_print.size()) { + if ((!m_pressure_equalizer && layer_to_print_idx == layers_to_print.size()) || (m_pressure_equalizer && layer_to_print_idx == (layers_to_print.size() + 1))) { + fc.stop(); + return {}; + } else { + // Pressure equalizer need insert empty input. Because it returns one layer back. + // Insert NOP (no operation) layer; + ++layer_to_print_idx; + return LayerResult::make_nop_layer_result(); + } } else { LayerToPrint &layer = layers_to_print[layer_to_print_idx ++]; print.throw_if_canceled(); return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &layer == &layers_to_print.back(), nullptr, single_object_idx); } }); - const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&spiral_vase = *this->m_spiral_vase](GCode::LayerResult in)->GCode::LayerResult { + const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&spiral_vase = *this->m_spiral_vase](LayerResult in)->LayerResult { + if (in.nop_layer_result) + return in; spiral_vase.enable(in.spiral_vase_enable); - return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush, in.pressure_equalizer_buffer_flush }; + return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush }; }); - const auto pressure_equalizer = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&pressure_equalizer = *this->m_pressure_equalizer](GCode::LayerResult in) -> GCode::LayerResult { - return { std::string(pressure_equalizer.process_layer(std::move(in.gcode.c_str()), in.pressure_equalizer_buffer_flush)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush, in.pressure_equalizer_buffer_flush }; + const auto pressure_equalizer = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&pressure_equalizer = *this->m_pressure_equalizer](LayerResult in) -> LayerResult { + return pressure_equalizer.process_layer(std::move(in)); }); - const auto cooling = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&cooling_buffer = *this->m_cooling_buffer](GCode::LayerResult in)->std::string { + const auto cooling = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&cooling_buffer = *this->m_cooling_buffer](LayerResult in)->std::string { + if (in.nop_layer_result) + return in.gcode; return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); }); const auto find_replace = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, @@ -2079,7 +2103,7 @@ namespace Skirt { // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. // For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths // and performing the extruder specific extrusions together. -GCode::LayerResult GCode::process_layer( +LayerResult GCode::process_layer( const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, @@ -2110,7 +2134,7 @@ GCode::LayerResult GCode::process_layer( } } const Layer &layer = (object_layer != nullptr) ? *object_layer : *support_layer; - GCode::LayerResult result { {}, layer.id(), false, last_layer, last_layer}; + LayerResult result { {}, layer.id(), false, last_layer, false}; if (layer_tools.extruders.empty()) // Nothing to extrude. return result; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index d01fa8e3e..783cfcd33 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -114,6 +114,20 @@ public: static const std::vector& get() { return Colors; } }; +struct LayerResult { + std::string gcode; + size_t layer_id; + // Is spiral vase post processing enabled for this layer? + bool spiral_vase_enable { false }; + // Should the cooling buffer content be flushed at the end of this layer? + bool cooling_buffer_flush { false }; + // Is indicating if this LayerResult should be processed, or it is just inserted artificial LayerResult. + // It is used for the pressure equalizer because it needs to buffer one layer back. + bool nop_layer_result { false }; + + static LayerResult make_nop_layer_result() { return {"", std::numeric_limits::max(), false, false, true}; } +}; + class GCode { public: GCode() : @@ -226,16 +240,6 @@ private: static std::vector collect_layers_to_print(const PrintObject &object); static std::vector>> collect_layers_to_print(const Print &print); - struct LayerResult { - std::string gcode; - size_t layer_id; - // Is spiral vase post processing enabled for this layer? - bool spiral_vase_enable { false }; - // Should the cooling buffer content be flushed at the end of this layer? - bool cooling_buffer_flush { false }; - // Should the PressureEqualizer buffer content be flushed at the end of this layer? - bool pressure_equalizer_buffer_flush { false }; - }; LayerResult process_layer( const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. @@ -446,6 +450,7 @@ private: friend class Wipe; friend class WipeTowerIntegration; + friend class PressureEqualizer; }; std::vector sort_object_instances_by_model_order(const Print& print); diff --git a/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp index f7d816695..6ff5840bc 100644 --- a/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/src/libslic3r/GCode/PressureEqualizer.cpp @@ -5,36 +5,24 @@ #include "../libslic3r.h" #include "../PrintConfig.hpp" #include "../LocalesUtils.hpp" +#include "../GCode.hpp" #include "PressureEqualizer.hpp" #include "fast_float/fast_float.h" +#include "GCodeWriter.hpp" namespace Slic3r { -static constexpr const std::array EXTRUSION_ROLE_TAG = {";_EXTRUSION_ROLE:"}; -static constexpr const size_t EXTRUSION_ROLE_TAG_LENGTH = EXTRUSION_ROLE_TAG.size() - 1; -static constexpr const std::array EXTRUDE_END_TAG = {";_EXTRUDE_END"}; -static constexpr const size_t EXTRUDE_END_TAG_LENGTH = EXTRUDE_END_TAG.size() - 1; -static constexpr const std::array EXTRUDE_SET_SPEED_TAG = {";_EXTRUDE_SET_SPEED"}; -static constexpr const size_t EXTRUDE_SET_SPEED_TAG_LENGTH = EXTRUDE_SET_SPEED_TAG.size() - 1; -static constexpr const std::array EXTERNAL_PERIMETER_TAG = {";_EXTERNAL_PERIMETER"}; -static constexpr const size_t EXTERNAL_PERIMETER_TAG_LENGTH = EXTERNAL_PERIMETER_TAG.size() - 1; +static const std::string EXTRUSION_ROLE_TAG = ";_EXTRUSION_ROLE:"; +static const std::string EXTRUDE_END_TAG = ";_EXTRUDE_END"; +static const std::string EXTRUDE_SET_SPEED_TAG = ";_EXTRUDE_SET_SPEED"; +static const std::string EXTERNAL_PERIMETER_TAG = ";_EXTERNAL_PERIMETER"; -PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_config(config) +PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_relative_e_distances(config.use_relative_e_distances.value) { - reset(); -} - -void PressureEqualizer::reset() -{ - circular_buffer_pos = 0; - circular_buffer_size = 100; - circular_buffer_items = 0; - circular_buffer.assign(circular_buffer_size, GCodeLine()); - // Preallocate some data, so that output_buffer.data() will return an empty string. output_buffer.assign(32, 0); - output_buffer_length = 0; + output_buffer_length = 0; m_current_extruder = 0; // Zero the position of the XYZE axes + the current feed @@ -45,7 +33,7 @@ void PressureEqualizer::reset() // Calculate filamet crossections for the multiple extruders. m_filament_crossections.clear(); - for (double r : m_config.filament_diameter.values) { + for (double r : config.filament_diameter.values) { double a = 0.25f * M_PI * r * r; m_filament_crossections.push_back(float(a)); } @@ -54,86 +42,82 @@ void PressureEqualizer::reset() // Volumetric rate of a 0.45mm x 0.2mm extrusion at 60mm/s XY movement: 0.45*0.2*60*60=5.4*60 = 324 mm^3/min // Volumetric rate of a 0.45mm x 0.2mm extrusion at 20mm/s XY movement: 0.45*0.2*20*60=1.8*60 = 108 mm^3/min // Slope of the volumetric rate, changing from 20mm/s to 60mm/s over 2 seconds: (5.4-1.8)*60*60/2=60*60*1.8 = 6480 mm^3/min^2 = 1.8 mm^3/s^2 - m_max_volumetric_extrusion_rate_slope_positive = float(m_config.max_volumetric_extrusion_rate_slope_positive.value) * 60.f * 60.f; - m_max_volumetric_extrusion_rate_slope_negative = float(m_config.max_volumetric_extrusion_rate_slope_negative.value) * 60.f * 60.f; + m_max_volumetric_extrusion_rate_slope_positive = float(config.max_volumetric_extrusion_rate_slope_positive.value) * 60.f * 60.f; + m_max_volumetric_extrusion_rate_slope_negative = float(config.max_volumetric_extrusion_rate_slope_negative.value) * 60.f * 60.f; - for (size_t i = 0; i < numExtrusionRoles; ++ i) { - m_max_volumetric_extrusion_rate_slopes[i].negative = m_max_volumetric_extrusion_rate_slope_negative; - m_max_volumetric_extrusion_rate_slopes[i].positive = m_max_volumetric_extrusion_rate_slope_positive; + for (ExtrusionRateSlope &extrusion_rate_slope : m_max_volumetric_extrusion_rate_slopes) { + extrusion_rate_slope.negative = m_max_volumetric_extrusion_rate_slope_negative; + extrusion_rate_slope.positive = m_max_volumetric_extrusion_rate_slope_positive; } - // Don't regulate the pressure in infill. - m_max_volumetric_extrusion_rate_slopes[erBridgeInfill].negative = 0; - m_max_volumetric_extrusion_rate_slopes[erBridgeInfill].positive = 0; - // Don't regulate the pressure in gap fill. - m_max_volumetric_extrusion_rate_slopes[erGapFill].negative = 0; - m_max_volumetric_extrusion_rate_slopes[erGapFill].positive = 0; + // Don't regulate the pressure in infill and gap fill. + // TODO: Do we want to regulate pressure in erWipeTower, erCustom and erMixed? + for (const ExtrusionRole er : {erBridgeInfill, erGapFill}) { + m_max_volumetric_extrusion_rate_slopes[er].negative = 0; + m_max_volumetric_extrusion_rate_slopes[er].positive = 0; + } #ifdef PRESSURE_EQUALIZER_STATISTIC m_stat.reset(); #endif +#ifdef PRESSURE_EQUALIZER_DEBUG line_idx = 0; +#endif } -const char* PressureEqualizer::process_layer(const char *szGCode, bool flush) +void PressureEqualizer::process_layer(const std::string &gcode) { - // Reset length of the output_buffer. - output_buffer_length = 0; - - if (szGCode != nullptr) { - const char *p = szGCode; - while (*p != 0) { + if (!gcode.empty()) { + const char *gcode_begin = gcode.c_str(); + while (*gcode_begin != 0) { // Find end of the line. - const char *endl = p; + const char *gcode_end = gcode_begin; // Slic3r always generates end of lines in a Unix style. - for (; *endl != 0 && *endl != '\n'; ++ endl) ; - if (circular_buffer_items == circular_buffer_size) - // Buffer is full. Push out the oldest line. - output_gcode_line(circular_buffer[circular_buffer_pos]); - else - ++ circular_buffer_items; - // Process a G-code line, store it into the provided GCodeLine object. - size_t idx_tail = circular_buffer_pos; - circular_buffer_pos = circular_buffer_idx_next(circular_buffer_pos); - if (! process_line(p, endl, circular_buffer[idx_tail])) { - // The line has to be forgotten. It contains comment marks, which shall be - // filtered out of the target g-code. - circular_buffer_pos = idx_tail; - -- circular_buffer_items; + for (; *gcode_end != 0 && *gcode_end != '\n'; ++gcode_end); + + m_gcode_lines.emplace_back(); + if (!this->process_line(gcode_begin, gcode_end, m_gcode_lines.back())) { + // The line has to be forgotten. It contains comment marks, which shall be filtered out of the target g-code. + m_gcode_lines.pop_back(); } - p = endl; - if (*p == '\n') - ++ p; + gcode_begin = gcode_end; + if (*gcode_begin == '\n') + ++gcode_begin; } } +} - if (flush) { - // Flush the remaining valid lines of the circular buffer. - for (size_t idx = circular_buffer_idx_head(); circular_buffer_items > 0; -- circular_buffer_items) { - output_gcode_line(circular_buffer[idx]); - if (++ idx == circular_buffer_size) - idx = 0; - } - // Reset the index pointer. - assert(circular_buffer_items == 0); - circular_buffer_pos = 0; +LayerResult PressureEqualizer::process_layer(LayerResult &&input) +{ + const bool is_first_layer = m_layer_results.empty(); + const size_t next_layer_first_idx = m_gcode_lines.size(); -#ifdef PRESSURE_EQUALIZER_STATISTIC - printf("Statistics: \n"); - printf("Minimum volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_min); - printf("Maximum volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_max); - if (m_stat.extrusion_length > 0) - m_stat.volumetric_extrusion_rate_avg /= m_stat.extrusion_length; - printf("Average volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_avg); - m_stat.reset(); -#endif - } + if (!input.nop_layer_result) { + this->process_layer(input.gcode); + input.gcode.clear(); // GCode is already processed, so it isn't needed to store it. + m_layer_results.emplace(new LayerResult(input)); + } + + if (is_first_layer) // Buffer previous input result and output NOP. + return LayerResult::make_nop_layer_result(); + + // Export previous layer. + LayerResult *prev_layer_result = m_layer_results.front(); + m_layer_results.pop(); + + output_buffer_length = 0; + for (size_t line_idx = 0; line_idx < next_layer_first_idx; ++line_idx) + output_gcode_line(m_gcode_lines[line_idx]); + m_gcode_lines.erase(m_gcode_lines.begin(), m_gcode_lines.begin() + int(next_layer_first_idx)); if (output_buffer_length > 0) - return output_buffer.data(); + prev_layer_result->gcode = std::string(output_buffer.data()); - return "\0"; + assert(!input.nop_layer_result || m_layer_results.empty()); + LayerResult out = *prev_layer_result; + delete prev_layer_result; + return out; } // Is a white space? @@ -186,12 +170,13 @@ static inline float parse_float(const char *&line, const size_t line_length) bool PressureEqualizer::process_line(const char *line, const char *line_end, GCodeLine &buf) { const size_t len = line_end - line; - - if (strncmp(line, EXTRUSION_ROLE_TAG.data(), EXTRUSION_ROLE_TAG_LENGTH) == 0) { - line += EXTRUSION_ROLE_TAG_LENGTH; + if (strncmp(line, EXTRUSION_ROLE_TAG.data(), EXTRUSION_ROLE_TAG.length()) == 0) { + line += EXTRUSION_ROLE_TAG.length(); int role = atoi(line); m_current_extrusion_role = ExtrusionRole(role); - ++ line_idx; +#ifdef PRESSURE_EQUALIZER_DEBUG + ++line_idx; +#endif return false; } @@ -251,7 +236,7 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo throw Slic3r::RuntimeError(std::string("GCode::PressureEqualizer: Invalid axis for G0/G1: ") + axis); buf.pos_provided[i] = true; new_pos[i] = parse_float(line, line_end - line); - if (i == 3 && m_config.use_relative_e_distances.value) + if (i == 3 && m_use_relative_e_distances) new_pos[i] += m_current_pos[i]; changed[i] = new_pos[i] != m_current_pos[i]; eatws(line); @@ -358,7 +343,7 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo { // Activate an extruder head. int new_extruder = parse_int(line); - if (new_extruder != m_current_extruder) { + if (new_extruder != int(m_current_extruder)) { m_current_extruder = new_extruder; m_retracted = true; buf.type = GCODELINETYPE_TOOL_CHANGE; @@ -373,8 +358,10 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo memcpy(buf.pos_end, m_current_pos, sizeof(float)*5); adjust_volumetric_rate(); - ++ line_idx; - return true; +#ifdef PRESSURE_EQUALIZER_DEBUG + ++line_idx; +#endif + return true; } void PressureEqualizer::output_gcode_line(GCodeLine &line) @@ -486,55 +473,56 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line) void PressureEqualizer::adjust_volumetric_rate() { - if (circular_buffer_items < 2) + if (m_gcode_lines.size() < 2) return; // Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes. - const size_t idx_head = circular_buffer_idx_head(); - const size_t idx_tail = circular_buffer_idx_prev(circular_buffer_idx_tail()); - size_t idx = idx_tail; - if (idx == idx_head || ! circular_buffer[idx].extruding()) +// size_t fist_line_idx = size_t(std::max(0, int(m_gcode_lines.size()) - 100)); + size_t fist_line_idx = 0; + const size_t last_line_idx = m_gcode_lines.size() - 1; + + size_t line_idx = last_line_idx; + if (line_idx == fist_line_idx || !m_gcode_lines[line_idx].extruding()) // Nothing to do, the last move is not extruding. return; - float feedrate_per_extrusion_role[numExtrusionRoles]; - for (size_t i = 0; i < numExtrusionRoles; ++ i) - feedrate_per_extrusion_role[i] = FLT_MAX; - feedrate_per_extrusion_role[circular_buffer[idx].extrusion_role] = circular_buffer[idx].volumetric_extrusion_rate_start; + std::array feedrate_per_extrusion_role{}; + feedrate_per_extrusion_role.fill(std::numeric_limits::max()); + feedrate_per_extrusion_role[m_gcode_lines[line_idx].extrusion_role] = m_gcode_lines[line_idx].volumetric_extrusion_rate_start; - bool modified = true; - while (modified && idx != idx_head) { - size_t idx_prev = circular_buffer_idx_prev(idx); - for (; !circular_buffer[idx_prev].extruding() && idx_prev != idx_head; idx_prev = circular_buffer_idx_prev(idx_prev)); - if (!circular_buffer[idx_prev].extruding()) + while (line_idx != fist_line_idx) { + size_t idx_prev = line_idx - 1; + for (; !m_gcode_lines[idx_prev].extruding() && idx_prev != fist_line_idx; --idx_prev); + if (!m_gcode_lines[idx_prev].extruding()) break; // Volumetric extrusion rate at the start of the succeding segment. - float rate_succ = circular_buffer[idx].volumetric_extrusion_rate_start; + float rate_succ = m_gcode_lines[line_idx].volumetric_extrusion_rate_start; // What is the gradient of the extrusion rate between idx_prev and idx? - idx = idx_prev; - GCodeLine &line = circular_buffer[idx]; - for (size_t iRole = 1; iRole < numExtrusionRoles; ++ iRole) { - float rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].negative; - if (rate_slope == 0) - // The negative rate is unlimited. - continue; + line_idx = idx_prev; + GCodeLine &line = m_gcode_lines[line_idx]; + + for (size_t iRole = 1; iRole < erCount; ++ iRole) { + const float &rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].negative; + if (rate_slope == 0 || feedrate_per_extrusion_role[iRole] == std::numeric_limits::max()) + continue; // The negative rate is unlimited or the rate for ExtrusionRole iRole is unlimited. + float rate_end = feedrate_per_extrusion_role[iRole]; if (iRole == line.extrusion_role && rate_succ < rate_end) // Limit by the succeeding volumetric flow rate. rate_end = rate_succ; - if (line.volumetric_extrusion_rate_end > rate_end) { + + if (line.extrusion_role == erExternalPerimeter || line.extrusion_role == erGapFill || line.extrusion_role == erBridgeInfill) { + rate_end = line.volumetric_extrusion_rate_end; + } else if (line.volumetric_extrusion_rate_end > rate_end) { line.volumetric_extrusion_rate_end = rate_end; line.max_volumetric_extrusion_rate_slope_negative = rate_slope; line.modified = true; } else if (iRole == line.extrusion_role) { rate_end = line.volumetric_extrusion_rate_end; - } else if (rate_end == FLT_MAX) { - // The rate for ExtrusionRole iRole is unlimited. - continue; } else { // Use the original, 'floating' extrusion rate as a starting point for the limiter. } -// modified = false; + float rate_start = rate_end + rate_slope * line.time_corrected(); if (rate_start < line.volumetric_extrusion_rate_start) { // Limit the volumetric extrusion rate at the start of this segment due to a segment @@ -542,34 +530,35 @@ void PressureEqualizer::adjust_volumetric_rate() line.volumetric_extrusion_rate_start = rate_start; line.max_volumetric_extrusion_rate_slope_negative = rate_slope; line.modified = true; -// modified = true; } - feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : rate_start; +// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : rate_start; + feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_start; } } - // Go forward and adjust the feedrate to decrease the slope of the extrusion rate changes. - for (size_t i = 0; i < numExtrusionRoles; ++i) - feedrate_per_extrusion_role[i] = FLT_MAX; - feedrate_per_extrusion_role[circular_buffer[idx].extrusion_role] = circular_buffer[idx].volumetric_extrusion_rate_end; + feedrate_per_extrusion_role.fill(std::numeric_limits::max()); + feedrate_per_extrusion_role[m_gcode_lines[line_idx].extrusion_role] = m_gcode_lines[line_idx].volumetric_extrusion_rate_end; - assert(circular_buffer[idx].extruding()); - while (idx != idx_tail) { - size_t idx_next = circular_buffer_idx_next(idx); - for (; !circular_buffer[idx_next].extruding() && idx_next != idx_tail; idx_next = circular_buffer_idx_next(idx_next)); - if (!circular_buffer[idx_next].extruding()) + assert(m_gcode_lines[line_idx].extruding()); + while (line_idx != last_line_idx) { + size_t idx_next = line_idx + 1; + for (; !m_gcode_lines[idx_next].extruding() && idx_next != last_line_idx; ++idx_next); + if (!m_gcode_lines[idx_next].extruding()) break; - float rate_prec = circular_buffer[idx].volumetric_extrusion_rate_end; + float rate_prec = m_gcode_lines[line_idx].volumetric_extrusion_rate_end; // What is the gradient of the extrusion rate between idx_prev and idx? - idx = idx_next; - GCodeLine &line = circular_buffer[idx]; - for (size_t iRole = 1; iRole < numExtrusionRoles; ++ iRole) { - float rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].positive; - if (rate_slope == 0) - // The positive rate is unlimited. - continue; + line_idx = idx_next; + GCodeLine &line = m_gcode_lines[line_idx]; + + for (size_t iRole = 1; iRole < erCount; ++ iRole) { + const float &rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].positive; + if (rate_slope == 0 || feedrate_per_extrusion_role[iRole] == std::numeric_limits::max()) + continue; // The positive rate is unlimited or the rate for ExtrusionRole iRole is unlimited. + float rate_start = feedrate_per_extrusion_role[iRole]; - if (iRole == line.extrusion_role && rate_prec < rate_start) + if (line.extrusion_role == erExternalPerimeter || line.extrusion_role == erGapFill || line.extrusion_role == erBridgeInfill) { + rate_start = line.volumetric_extrusion_rate_start; + } else if (iRole == line.extrusion_role && rate_prec < rate_start) rate_start = rate_prec; if (line.volumetric_extrusion_rate_start > rate_start) { line.volumetric_extrusion_rate_start = rate_start; @@ -577,13 +566,10 @@ void PressureEqualizer::adjust_volumetric_rate() line.modified = true; } else if (iRole == line.extrusion_role) { rate_start = line.volumetric_extrusion_rate_start; - } else if (rate_start == FLT_MAX) { - // The rate for ExtrusionRole iRole is unlimited. - continue; } else { // Use the original, 'floating' extrusion rate as a starting point for the limiter. } - float rate_end = (rate_slope == 0) ? FLT_MAX : rate_start + rate_slope * line.time_corrected(); + float rate_end = rate_start + rate_slope * line.time_corrected(); if (rate_end < line.volumetric_extrusion_rate_end) { // Limit the volumetric extrusion rate at the start of this segment due to a segment // of ExtrusionType iRole, which was extruded before. @@ -591,24 +577,28 @@ void PressureEqualizer::adjust_volumetric_rate() line.max_volumetric_extrusion_rate_slope_positive = rate_slope; line.modified = true; } - feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end; +// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end; + feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_end; } } } -void PressureEqualizer::push_axis_to_output(const char axis, const float value, bool add_eol) +inline void PressureEqualizer::push_to_output(GCodeG1Formatter &formatter) { - char buf[2048]; - int len = sprintf(buf, (axis == 'E') ? " %c%.3f" : " %c%.5f", axis, value); - push_to_output(buf, len, add_eol); + return this->push_to_output(formatter.string(), false); } -void PressureEqualizer::push_to_output(const char *text, const size_t len, bool add_eol) +inline void PressureEqualizer::push_to_output(const std::string &text, bool add_eol) +{ + return this->push_to_output(text.data(), text.size(), add_eol); +} + +inline void PressureEqualizer::push_to_output(const char *text, const size_t len, bool add_eol) { // New length of the output buffer content. size_t len_new = output_buffer_length + len + 1; if (add_eol) - ++ len_new; + ++len_new; // Resize the output buffer to a power of 2 higher than the required memory. if (output_buffer.size() < len_new) { @@ -631,29 +621,31 @@ void PressureEqualizer::push_to_output(const char *text, const size_t len, bool output_buffer_length += len; } if (add_eol) - output_buffer[output_buffer_length ++] = '\n'; + output_buffer[output_buffer_length++] = '\n'; output_buffer[output_buffer_length] = 0; } void PressureEqualizer::push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment) { - push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG_LENGTH, true); + push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true); - push_to_output("G1", 2, false); - push_axis_to_output('F', new_feedrate); - - push_to_output(EXTRUDE_SET_SPEED_TAG.data(), EXTRUDE_SET_SPEED_TAG_LENGTH, line.extrusion_role != erExternalPerimeter); + GCodeG1Formatter feedrate_formatter; + feedrate_formatter.emit_f(new_feedrate); + feedrate_formatter.emit_string(std::string(EXTRUDE_SET_SPEED_TAG.data(), EXTRUDE_SET_SPEED_TAG.length())); if (line.extrusion_role == erExternalPerimeter) - push_to_output(EXTERNAL_PERIMETER_TAG.data(), EXTERNAL_PERIMETER_TAG_LENGTH, true); + feedrate_formatter.emit_string(std::string(EXTERNAL_PERIMETER_TAG.data(), EXTERNAL_PERIMETER_TAG.length())); + push_to_output(feedrate_formatter); - push_to_output("G1", 2, false); - for (char i = 0; i < 3; ++ i) - if (line.pos_provided[i]) - push_axis_to_output('X'+i, line.pos_end[i]); - push_axis_to_output('E', m_config.use_relative_e_distances.value ? (line.pos_end[3] - line.pos_start[3]) : line.pos_end[3]); + GCodeG1Formatter extrusion_formatter; + for (size_t axis_idx = 0; axis_idx < 3; ++axis_idx) + if (line.pos_provided[axis_idx]) + extrusion_formatter.emit_axis(char('X' + axis_idx), line.pos_end[axis_idx], GCodeFormatter::XYZF_EXPORT_DIGITS); + extrusion_formatter.emit_axis('E', m_use_relative_e_distances ? (line.pos_end[3] - line.pos_start[3]) : line.pos_end[3], GCodeFormatter::E_EXPORT_DIGITS); - // output comment and EOL - push_to_output(comment, (comment == nullptr) ? 0 : strlen(comment), true); + if (comment != nullptr) + extrusion_formatter.emit_string(std::string(comment)); + + push_to_output(extrusion_formatter); } } // namespace Slic3r diff --git a/src/libslic3r/GCode/PressureEqualizer.hpp b/src/libslic3r/GCode/PressureEqualizer.hpp index 37a109725..029f19b48 100644 --- a/src/libslic3r/GCode/PressureEqualizer.hpp +++ b/src/libslic3r/GCode/PressureEqualizer.hpp @@ -5,8 +5,14 @@ #include "../PrintConfig.hpp" #include "../ExtrusionEntity.hpp" +#include + namespace Slic3r { +struct LayerResult; + +class GCodeG1Formatter; + //#define PRESSURE_EQUALIZER_STATISTIC //#define PRESSURE_EQUALIZER_DEBUG @@ -15,30 +21,34 @@ namespace Slic3r { class PressureEqualizer { public: + PressureEqualizer() = delete; explicit PressureEqualizer(const Slic3r::GCodeConfig &config); ~PressureEqualizer() = default; - void reset(); - - // Process a next batch of G-code lines. Flush the internal buffers if asked for. - const char* process_layer(const char *szGCode, bool flush); - - size_t get_output_buffer_length() const { return output_buffer_length; } - + // Process a next batch of G-code lines. + // The last LayerResult must be LayerResult::make_nop_layer_result() because it always returns GCode for the previous layer. + // When process_layer is called for the first layer, then LayerResult::make_nop_layer_result() is returned. + LayerResult process_layer(LayerResult &&input); private: + + void process_layer(const std::string &gcode); + +#ifdef PRESSURE_EQUALIZER_STATISTIC struct Statistics { - void reset() { - volumetric_extrusion_rate_min = std::numeric_limits::max(); + void reset() + { + volumetric_extrusion_rate_min = std::numeric_limits::max(); volumetric_extrusion_rate_max = 0.f; volumetric_extrusion_rate_avg = 0.f; - extrusion_length = 0.f; + extrusion_length = 0.f; } - void update(float volumetric_extrusion_rate, float length) { - volumetric_extrusion_rate_min = std::min(volumetric_extrusion_rate_min, volumetric_extrusion_rate); - volumetric_extrusion_rate_max = std::max(volumetric_extrusion_rate_max, volumetric_extrusion_rate); + void update(float volumetric_extrusion_rate, float length) + { + volumetric_extrusion_rate_min = std::min(volumetric_extrusion_rate_min, volumetric_extrusion_rate); + volumetric_extrusion_rate_max = std::max(volumetric_extrusion_rate_max, volumetric_extrusion_rate); volumetric_extrusion_rate_avg += volumetric_extrusion_rate * length; - extrusion_length += length; + extrusion_length += length; } float volumetric_extrusion_rate_min; float volumetric_extrusion_rate_max; @@ -46,21 +56,16 @@ private: float extrusion_length; }; -#ifdef PRESSURE_EQUALIZER_STATISTIC struct Statistics m_stat; #endif - // Keeps the reference, does not own the config. - const Slic3r::GCodeConfig &m_config; - // Private configuration values // How fast could the volumetric extrusion rate increase / decrase? mm^3/sec^2 struct ExtrusionRateSlope { float positive; float negative; }; - enum { numExtrusionRoles = erSupportMaterialInterface + 1 }; - ExtrusionRateSlope m_max_volumetric_extrusion_rate_slopes[numExtrusionRoles]; + ExtrusionRateSlope m_max_volumetric_extrusion_rate_slopes[erCount]; float m_max_volumetric_extrusion_rate_slope_positive; float m_max_volumetric_extrusion_rate_slope_negative; // Maximum segment length to split a long segment, if the initial and the final flow rate differ. @@ -76,6 +81,7 @@ private: size_t m_current_extruder; ExtrusionRole m_current_extrusion_role; bool m_retracted; + bool m_use_relative_e_distances; enum GCodeLineType { GCODELINETYPE_INVALID, @@ -132,8 +138,6 @@ private: // or maybe the line needs to be split into multiple lines. bool modified; - // float timeStart; - // float timeEnd; // X,Y,Z,E,F. Storing the state of the currently active extruder only. float pos_start[5]; float pos_end[5]; @@ -158,21 +162,14 @@ private: float max_volumetric_extrusion_rate_slope_negative; }; - // Circular buffer of GCode lines. The circular buffer size will be limited to circular_buffer_size. - std::vector circular_buffer; - // Current position of the circular buffer (index, where to write the next line to, the line has to be pushed out before it is overwritten). - size_t circular_buffer_pos; - // Circular buffer size, configuration value. - size_t circular_buffer_size; - // Number of valid lines in the circular buffer. Lower or equal to circular_buffer_size. - size_t circular_buffer_items; - // Output buffer will only grow. It will not be reallocated over and over. std::vector output_buffer; size_t output_buffer_length; +#ifdef PRESSURE_EQUALIZER_DEBUG // For debugging purposes. Index of the G-code line processed. size_t line_idx; +#endif bool process_line(const char *line, const char *line_end, GCodeLine &buf); void output_gcode_line(GCodeLine &buf); @@ -182,33 +179,16 @@ private: void adjust_volumetric_rate(); // Push the text to the end of the output_buffer. - void push_to_output(const char *text, size_t len, bool add_eol = true); - // Push an axis assignment to the end of the output buffer. - void push_axis_to_output(char axis, float value, bool add_eol = false); - // Push a G-code line to the output, + inline void push_to_output(GCodeG1Formatter &formatter); + inline void push_to_output(const std::string &text, bool add_eol); + inline void push_to_output(const char *text, size_t len, bool add_eol = true); + // Push a G-code line to the output. void push_line_to_output(const GCodeLine &line, float new_feedrate, const char *comment); - size_t circular_buffer_idx_head() const { - size_t idx = circular_buffer_pos + circular_buffer_size - circular_buffer_items; - if (idx >= circular_buffer_size) - idx -= circular_buffer_size; - return idx; - } +public: + std::queue m_layer_results; - size_t circular_buffer_idx_tail() const { return circular_buffer_pos; } - - size_t circular_buffer_idx_prev(size_t idx) const { - idx += circular_buffer_size - 1; - if (idx >= circular_buffer_size) - idx -= circular_buffer_size; - return idx; - } - - size_t circular_buffer_idx_next(size_t idx) const { - if (++ idx >= circular_buffer_size) - idx -= circular_buffer_size; - return idx; - } + std::vector m_gcode_lines; }; } // namespace Slic3r