From 070b2af3fd02f8b68ef8dd61b609f5f40e2faa52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 24 Jun 2024 10:19:02 +0200 Subject: [PATCH 1/2] Refactoring of the dynamic speed on overhangs. --- src/libslic3r/GCode.cpp | 33 +++--- src/libslic3r/GCode/ExtrusionProcessor.cpp | 125 +++++++++++++-------- src/libslic3r/GCode/ExtrusionProcessor.hpp | 20 ++-- src/slic3r/GUI/Tab.cpp | 2 +- 4 files changed, 110 insertions(+), 70 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index cd12ad34cb..86279de57c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3217,21 +3217,20 @@ std::string GCodeGenerator::_extrude( else if (this->object_layer_over_raft()) speed = m_config.get_abs_value("first_layer_speed_over_raft", speed); - std::pair dynamic_speed_and_fan_speed{-1, -1}; + ExtrusionProcessor::OverhangSpeeds dynamic_print_and_fan_speeds = {-1.f, -1.f}; if (path_attr.overhang_attributes.has_value()) { - double external_perim_reference_speed = m_config.get_abs_value("external_perimeter_speed"); - if (external_perim_reference_speed == 0) - external_perim_reference_speed = m_volumetric_speed / path_attr.mm3_per_mm; - external_perim_reference_speed = cap_speed( - external_perim_reference_speed, m_config, m_writer.extruder()->id(), path_attr - ); + double external_perimeter_reference_speed = m_config.get_abs_value("external_perimeter_speed"); + if (external_perimeter_reference_speed == 0) { + external_perimeter_reference_speed = m_volumetric_speed / path_attr.mm3_per_mm; + } - dynamic_speed_and_fan_speed = ExtrusionProcessor::calculate_overhang_speed(path_attr, this->m_config, m_writer.extruder()->id(), - external_perim_reference_speed, speed); + external_perimeter_reference_speed = cap_speed(external_perimeter_reference_speed, m_config, m_writer.extruder()->id(), path_attr); + dynamic_print_and_fan_speeds = ExtrusionProcessor::calculate_overhang_speed(path_attr, this->m_config, m_writer.extruder()->id(), + float(external_perimeter_reference_speed), float(speed)); } - if (dynamic_speed_and_fan_speed.first > -1) { - speed = dynamic_speed_and_fan_speed.first; + if (dynamic_print_and_fan_speeds.print_speed > -1) { + speed = dynamic_print_and_fan_speeds.print_speed; } // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) @@ -3292,8 +3291,10 @@ std::string GCodeGenerator::_extrude( // F is mm per minute. gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments); - if (dynamic_speed_and_fan_speed.second >= 0) - gcode += ";_SET_FAN_SPEED" + std::to_string(int(dynamic_speed_and_fan_speed.second)) + "\n"; + + if (dynamic_print_and_fan_speeds.fan_speed >= 0) { + gcode += ";_SET_FAN_SPEED" + std::to_string(int(dynamic_print_and_fan_speeds.fan_speed)) + "\n"; + } std::string comment; if (m_config.gcode_comments) { @@ -3343,11 +3344,13 @@ std::string GCodeGenerator::_extrude( } } - if (m_enable_cooling_markers) + if (m_enable_cooling_markers) { gcode += path_attr.role.is_bridge() ? ";_BRIDGE_FAN_END\n" : ";_EXTRUDE_END\n"; + } - if (dynamic_speed_and_fan_speed.second >= 0) + if (dynamic_print_and_fan_speeds.fan_speed >= 0) { gcode += ";_RESET_FAN_SPEED\n"; + } this->last_position = path.back().point; return gcode; diff --git a/src/libslic3r/GCode/ExtrusionProcessor.cpp b/src/libslic3r/GCode/ExtrusionProcessor.cpp index 51acead1cc..d818581e8f 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.cpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.cpp @@ -11,7 +11,7 @@ #include "libslic3r/Line.hpp" #include "libslic3r/Polygon.hpp" -namespace Slic3r { namespace ExtrusionProcessor { +namespace Slic3r::ExtrusionProcessor { ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path, const AABBTreeLines::LinesDistancer &unscaled_prev_layer, @@ -146,57 +146,84 @@ ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const Extru return result; }; - -std::pair calculate_overhang_speed(const ExtrusionAttributes &attributes, - const FullPrintConfig &config, - size_t extruder_id, - float external_perim_reference_speed, - float default_speed) +static std::map calc_print_speed_sections(const ExtrusionAttributes &attributes, + const FullPrintConfig &config, + const float external_perimeter_reference_speed, + const float default_speed) { - assert(attributes.overhang_attributes.has_value()); - std::vector> overhangs_with_speeds = { - {100, ConfigOptionFloatOrPercent{default_speed, false}}}; + struct OverhangWithSpeed + { + int percent; + ConfigOptionFloatOrPercent print_speed; + }; + + std::vector overhangs_with_speeds = {{100, ConfigOptionFloatOrPercent{default_speed, false}}}; if (config.enable_dynamic_overhang_speeds) { - overhangs_with_speeds = {{0, config.overhang_speed_0}, - {25, config.overhang_speed_1}, - {50, config.overhang_speed_2}, - {75, config.overhang_speed_3}, + overhangs_with_speeds = {{ 0, config.overhang_speed_0}, + { 25, config.overhang_speed_1}, + { 50, config.overhang_speed_2}, + { 75, config.overhang_speed_3}, {100, ConfigOptionFloatOrPercent{default_speed, false}}}; } - std::vector> overhang_with_fan_speeds = {{100, ConfigOptionInts{0}}}; - if (config.enable_dynamic_fan_speeds.get_at(extruder_id)) { - overhang_with_fan_speeds = {{0, config.overhang_fan_speed_0}, - {25, config.overhang_fan_speed_1}, - {50, config.overhang_fan_speed_2}, - {75, config.overhang_fan_speed_3}, - {100, ConfigOptionInts{0}}}; - } - - float speed_base = external_perim_reference_speed > 0 ? external_perim_reference_speed : default_speed; + const float speed_base = external_perimeter_reference_speed > 0 ? external_perimeter_reference_speed : default_speed; std::map speed_sections; - for (size_t i = 0; i < overhangs_with_speeds.size(); i++) { - float distance = attributes.width * (1.0 - (overhangs_with_speeds[i].first / 100.0)); - float speed = overhangs_with_speeds[i].second.percent ? (speed_base * overhangs_with_speeds[i].second.value / 100.0) : - overhangs_with_speeds[i].second.value; - if (speed < EPSILON) + for (OverhangWithSpeed &overhangs_with_speed : overhangs_with_speeds) { + const float distance = attributes.width * (1.f - (float(overhangs_with_speed.percent) / 100.f)); + float speed = float(overhangs_with_speed.print_speed.get_abs_value(speed_base)); + + if (speed < EPSILON) { speed = speed_base; + } + speed_sections[distance] = speed; } + return speed_sections; +} + +static std::map calc_fan_speed_sections(const ExtrusionAttributes &attributes, + const FullPrintConfig &config, + const size_t extruder_id) +{ + struct OverhangWithFanSpeed + { + int percent; + ConfigOptionInts fan_speed; + }; + + std::vector overhang_with_fan_speeds = {{100, ConfigOptionInts{0}}}; + if (config.enable_dynamic_fan_speeds.get_at(extruder_id)) { + overhang_with_fan_speeds = {{ 0, config.overhang_fan_speed_0}, + { 25, config.overhang_fan_speed_1}, + { 50, config.overhang_fan_speed_2}, + { 75, config.overhang_fan_speed_3}, + {100, ConfigOptionInts{0}}}; + } + std::map fan_speed_sections; - for (size_t i = 0; i < overhang_with_fan_speeds.size(); i++) { - float distance = attributes.width * (1.0 - (overhang_with_fan_speeds[i].first / 100.0)); - float fan_speed = overhang_with_fan_speeds[i].second.get_at(extruder_id); + for (OverhangWithFanSpeed &overhang_with_fan_speed : overhang_with_fan_speeds) { + float distance = attributes.width * (1.f - (float(overhang_with_fan_speed.percent) / 100.f)); + float fan_speed = float(overhang_with_fan_speed.fan_speed.get_at(extruder_id)); fan_speed_sections[distance] = fan_speed; } + return fan_speed_sections; +} + +OverhangSpeeds calculate_overhang_speed(const ExtrusionAttributes &attributes, + const FullPrintConfig &config, + const size_t extruder_id, + const float external_perimeter_reference_speed, + const float default_speed) +{ + assert(attributes.overhang_attributes.has_value()); + auto interpolate_speed = [](const std::map &values, float distance) { auto upper_dist = values.lower_bound(distance); if (upper_dist == values.end()) { return values.rbegin()->second; - } - if (upper_dist == values.begin()) { + } else if (upper_dist == values.begin()) { return upper_dist->second; } @@ -205,22 +232,26 @@ std::pair calculate_overhang_speed(const ExtrusionAttributes &attri return (1.0f - t) * lower_dist->second + t * upper_dist->second; }; - float extrusion_speed = std::min(interpolate_speed(speed_sections, attributes.overhang_attributes->start_distance_from_prev_layer), - interpolate_speed(speed_sections, attributes.overhang_attributes->end_distance_from_prev_layer)); - float curled_base_speed = interpolate_speed(speed_sections, - attributes.width * attributes.overhang_attributes->proximity_to_curled_lines); - float final_speed = std::min(curled_base_speed, extrusion_speed); - float fan_speed = std::min(interpolate_speed(fan_speed_sections, attributes.overhang_attributes->start_distance_from_prev_layer), - interpolate_speed(fan_speed_sections, attributes.overhang_attributes->end_distance_from_prev_layer)); + const std::map speed_sections = calc_print_speed_sections(attributes, config, external_perimeter_reference_speed, default_speed); + const std::map fan_speed_sections = calc_fan_speed_sections(attributes, config, extruder_id); + const float extrusion_speed = std::min(interpolate_speed(speed_sections, attributes.overhang_attributes->start_distance_from_prev_layer), + interpolate_speed(speed_sections, attributes.overhang_attributes->end_distance_from_prev_layer)); + const float curled_base_speed = interpolate_speed(speed_sections, attributes.width * attributes.overhang_attributes->proximity_to_curled_lines); + + const float fan_speed = std::min(interpolate_speed(fan_speed_sections, attributes.overhang_attributes->start_distance_from_prev_layer), + interpolate_speed(fan_speed_sections, attributes.overhang_attributes->end_distance_from_prev_layer)); + + OverhangSpeeds overhang_speeds = {std::min(curled_base_speed, extrusion_speed), fan_speed}; if (!config.enable_dynamic_overhang_speeds) { - final_speed = -1; - } - if (!config.enable_dynamic_fan_speeds.get_at(extruder_id)) { - fan_speed = -1; + overhang_speeds.print_speed = -1; } - return {final_speed, fan_speed}; + if (!config.enable_dynamic_fan_speeds.get_at(extruder_id)) { + overhang_speeds.fan_speed = -1; + } + + return overhang_speeds; } -}} // namespace Slic3r::ExtrusionProcessor +} // namespace Slic3r::ExtrusionProcessor diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 2dc94b2f65..0b0610bc30 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -42,7 +42,7 @@ class CurledLine; class Linef; } // namespace Slic3r -namespace Slic3r { namespace ExtrusionProcessor { +namespace Slic3r::ExtrusionProcessor { struct ExtendedPoint { @@ -51,6 +51,12 @@ struct ExtendedPoint float curvature; }; +struct OverhangSpeeds +{ + float print_speed; + float fan_speed; +}; + template std::vector estimate_points_properties(const POINTS &input_points, const AABBTreeLines::LinesDistancer &unscaled_prev_layer, @@ -266,12 +272,12 @@ ExtrusionEntityCollection calculate_and_split_overhanging_extrusions( const AABBTreeLines::LinesDistancer &unscaled_prev_layer, const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); -std::pair calculate_overhang_speed(const ExtrusionAttributes &attributes, - const FullPrintConfig &config, - size_t extruder_id, - float external_perim_reference_speed, - float default_speed); +OverhangSpeeds calculate_overhang_speed(const ExtrusionAttributes &attributes, + const FullPrintConfig &config, + size_t extruder_id, + float external_perimeter_reference_speed, + float default_speed); -}} // namespace Slic3r::ExtrusionProcessor +} // namespace Slic3r::ExtrusionProcessor #endif // slic3r_ExtrusionProcessor_hpp_ diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index fc00ac88b9..c68c7128fc 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2389,7 +2389,7 @@ void TabFilament::toggle_options() bool dynamic_fan_speeds = m_config->opt_bool("enable_dynamic_fan_speeds", 0); for (int i = 0; i < 4; i++) { - toggle_option("overhang_fan_speed_"+std::to_string(i),dynamic_fan_speeds); + toggle_option("overhang_fan_speed_"+std::to_string(i),dynamic_fan_speeds); } } From 7f666265e1c9746fdb2e019c7d882f0f79d18ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 18 Jul 2024 17:22:07 +0200 Subject: [PATCH 2/2] SPE-2368: Reduce the number of emitted M106 when dynamic fan speed on overhangs is enabled. This fixed duplicity M106, which doesn't have any effect. Also, it was implemented hysteresis that filters out small back-and-forth changes in fan speeds. --- src/libslic3r/GCode.cpp | 67 ++++++++++++++--- src/libslic3r/GCode.hpp | 23 +++++- src/libslic3r/GCode/CoolingBuffer.cpp | 85 +++++++++++++--------- src/libslic3r/GCode/ExtrusionProcessor.cpp | 6 +- src/libslic3r/GCode/ExtrusionProcessor.hpp | 7 +- 5 files changed, 138 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 86279de57c..36fa60da34 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2874,10 +2874,37 @@ std::string GCodeGenerator::change_layer( std::string GCodeGenerator::extrude_smooth_path( const GCode::SmoothPath &smooth_path, const bool is_loop, const std::string_view description, const double speed ) { - // Extrude along the smooth path. std::string gcode; - for (const GCode::SmoothPathElement &el : smooth_path) - gcode += this->_extrude(el.path_attributes, el.path, description, speed); + + // Extrude along the smooth path. + bool is_bridge_extruded = false; + EmitModifiers emit_modifiers = EmitModifiers::create_with_disabled_emits(); + for (auto el_it = smooth_path.begin(); el_it != smooth_path.end(); ++el_it) { + const auto next_el_it = next(el_it); + + // By default, GCodeGenerator::_extrude() emit markers _BRIDGE_FAN_START, _BRIDGE_FAN_END and _RESET_FAN_SPEED for every extrusion. + // Together with split extrusions because of different ExtrusionAttributes, this could flood g-code with those markers and then + // produce an unnecessary number of duplicity M106. + // To prevent this, we control when each marker should be emitted by EmitModifiers, which allows determining when a bridge starts and ends, + // even when it is split into several extrusions. + if (el_it->path_attributes.role.is_bridge()) { + emit_modifiers.emit_bridge_fan_start = !is_bridge_extruded; + emit_modifiers.emit_bridge_fan_end = next_el_it == smooth_path.end() || !next_el_it->path_attributes.role.is_bridge(); + is_bridge_extruded = true; + } else if (is_bridge_extruded) { + emit_modifiers.emit_bridge_fan_start = false; + emit_modifiers.emit_bridge_fan_end = false; + is_bridge_extruded = false; + } + + // Ensure that just for the last extrusion from the smooth path, the fan speed will be reset back + // to the value calculated by the CoolingBuffer. + if (next_el_it == smooth_path.end()) { + emit_modifiers.emit_fan_speed_reset = true; + } + + gcode += this->_extrude(el_it->path_attributes, el_it->path, description, speed, emit_modifiers); + } // reset acceleration gcode += m_writer.set_print_acceleration(fast_round_up(m_config.default_acceleration.value)); @@ -2889,6 +2916,7 @@ std::string GCodeGenerator::extrude_smooth_path( GCode::reverse(reversed_smooth_path); m_wipe.set_path(std::move(reversed_smooth_path)); } + return gcode; } @@ -3115,7 +3143,8 @@ std::string GCodeGenerator::_extrude( const ExtrusionAttributes &path_attr, const Geometry::ArcWelder::Path &path, const std::string_view description, - double speed) + double speed, + const EmitModifiers &emit_modifiers) { std::string gcode; const std::string_view description_bridge = path_attr.role.is_bridge() ? " (bridge)"sv : ""sv; @@ -3226,7 +3255,8 @@ std::string GCodeGenerator::_extrude( external_perimeter_reference_speed = cap_speed(external_perimeter_reference_speed, m_config, m_writer.extruder()->id(), path_attr); dynamic_print_and_fan_speeds = ExtrusionProcessor::calculate_overhang_speed(path_attr, this->m_config, m_writer.extruder()->id(), - float(external_perimeter_reference_speed), float(speed)); + float(external_perimeter_reference_speed), float(speed), + m_current_dynamic_fan_speed); } if (dynamic_print_and_fan_speeds.print_speed > -1) { @@ -3281,19 +3311,29 @@ std::string GCodeGenerator::_extrude( std::string cooling_marker_setspeed_comments; if (m_enable_cooling_markers) { - if (path_attr.role.is_bridge()) + if (path_attr.role.is_bridge() && emit_modifiers.emit_bridge_fan_start) { gcode += ";_BRIDGE_FAN_START\n"; - else + } else if (!path_attr.role.is_bridge()) { cooling_marker_setspeed_comments = ";_EXTRUDE_SET_SPEED"; - if (path_attr.role == ExtrusionRole::ExternalPerimeter) + } + + if (path_attr.role == ExtrusionRole::ExternalPerimeter) { cooling_marker_setspeed_comments += ";_EXTERNAL_PERIMETER"; + } } // F is mm per minute. gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments); if (dynamic_print_and_fan_speeds.fan_speed >= 0) { - gcode += ";_SET_FAN_SPEED" + std::to_string(int(dynamic_print_and_fan_speeds.fan_speed)) + "\n"; + const int fan_speed = int(dynamic_print_and_fan_speeds.fan_speed); + if (!m_current_dynamic_fan_speed.has_value() || (m_current_dynamic_fan_speed.has_value() && m_current_dynamic_fan_speed != fan_speed)) { + m_current_dynamic_fan_speed = fan_speed; + gcode += ";_SET_FAN_SPEED" + std::to_string(fan_speed) + "\n"; + } + } else if (m_current_dynamic_fan_speed.has_value() && dynamic_print_and_fan_speeds.fan_speed < 0) { + m_current_dynamic_fan_speed.reset(); + gcode += ";_RESET_FAN_SPEED\n"; } std::string comment; @@ -3345,10 +3385,15 @@ std::string GCodeGenerator::_extrude( } if (m_enable_cooling_markers) { - gcode += path_attr.role.is_bridge() ? ";_BRIDGE_FAN_END\n" : ";_EXTRUDE_END\n"; + if (path_attr.role.is_bridge() && emit_modifiers.emit_bridge_fan_end) { + gcode += ";_BRIDGE_FAN_END\n"; + } else if (!path_attr.role.is_bridge()) { + gcode += ";_EXTRUDE_END\n"; + } } - if (dynamic_print_and_fan_speeds.fan_speed >= 0) { + if (m_current_dynamic_fan_speed.has_value() && emit_modifiers.emit_fan_speed_reset) { + m_current_dynamic_fan_speed.reset(); gcode += ";_RESET_FAN_SPEED\n"; } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index b35bd567e0..82e2dc151a 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -436,6 +436,9 @@ private: std::unique_ptr m_pressure_equalizer; std::unique_ptr m_wipe_tower; + // Current fan speed set by dynamic fan speed control. + std::optional m_current_dynamic_fan_speed; + // Heights (print_z) at which the skirt has already been extruded. std::vector m_skirt_done; // Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print. @@ -455,8 +458,24 @@ private: // Back-pointer to Print (const). const Print* m_print; - std::string _extrude( - const ExtrusionAttributes &attribs, const Geometry::ArcWelder::Path &path, const std::string_view description, double speed = -1); + struct EmitModifiers { + EmitModifiers(bool emit_fan_speed_reset, bool emit_bridge_fan_start, bool emit_bridge_fan_end) + : emit_fan_speed_reset(emit_fan_speed_reset), emit_bridge_fan_start(emit_bridge_fan_start), emit_bridge_fan_end(emit_bridge_fan_end) {} + + EmitModifiers() : EmitModifiers(true, true, true) {}; + + static EmitModifiers create_with_disabled_emits() { + return {false, false, false}; + } + + bool emit_fan_speed_reset = true; + + bool emit_bridge_fan_start = true; + bool emit_bridge_fan_end = true; + }; + + std::string _extrude(const ExtrusionAttributes &attribs, const Geometry::ArcWelder::Path &path, std::string_view description, double speed, const EmitModifiers &emit_modifiers = EmitModifiers()); + void print_machine_envelope(GCodeOutputStream &file, const Print &print); void _print_first_layer_chamber_temperature(GCodeOutputStream &file, const Print &print, const std::string &gcode, int temp, bool wait, bool accurate); void _print_first_layer_bed_temperature(GCodeOutputStream &file, const Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index efee06644c..815f28c728 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -95,6 +95,7 @@ struct CoolingLine TYPE_ADJUSTABLE_EMPTY = 1 << 16, // Custom fan speed (introduced for overhang fan speed) TYPE_SET_FAN_SPEED = 1 << 17, + // Reset fan speed back to speed calculate by the CoolingBuffer. TYPE_RESET_FAN_SPEED = 1 << 18, }; @@ -571,19 +572,20 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: fast_float::from_chars(sline.data() + (has_S ? pos_S : pos_P) + 1, sline.data() + sline.size(), line.time); if (has_P) line.time *= 0.001f; - } else + } else { line.time = 0; - line.time_max = line.time; - } + } - if (boost::contains(sline, ";_SET_FAN_SPEED")) { + line.time_max = line.time; + } else if (boost::contains(sline, ";_SET_FAN_SPEED")) { auto speed_start = sline.find_last_of('D'); int speed = 0; for (char num : sline.substr(speed_start + 1)) { speed = speed * 10 + (num - '0'); } - line.type |= CoolingLine::TYPE_SET_FAN_SPEED; + line.fan_speed = speed; + line.type |= CoolingLine::TYPE_SET_FAN_SPEED; } else if (boost::contains(sline, ";_RESET_FAN_SPEED")) { line.type |= CoolingLine::TYPE_RESET_FAN_SPEED; } @@ -820,14 +822,22 @@ std::string CoolingBuffer::apply_layer_cooldown( new_gcode.reserve(gcode.size() * 2); bool bridge_fan_control = false; int bridge_fan_speed = 0; - auto change_extruder_set_fan = [this, layer_id, layer_time, &new_gcode, &bridge_fan_control, &bridge_fan_speed]() { + auto change_extruder_set_fan = [this, layer_id, layer_time, &new_gcode, &bridge_fan_control, &bridge_fan_speed](const int requested_fan_speed = -1) { #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder) - int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed); - int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0; - std::pair custom_fan_speed_limits{fan_speed_new, 100 }; - int disable_fan_first_layers = EXTRUDER_CONFIG(disable_fan_first_layers); + const int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed); // Is the fan speed ramp enabled? - int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer); + const int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer); + int disable_fan_first_layers = EXTRUDER_CONFIG(disable_fan_first_layers); + int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0; + + struct FanSpeedRange + { + int min_speed; + int max_speed; + }; + + FanSpeedRange requested_fan_speed_limits{fan_speed_new, 100}; + if (disable_fan_first_layers <= 0 && full_fan_speed_layer > 0) { // When ramping up fan speed from disable_fan_first_layers to full_fan_speed_layer, force disable_fan_first_layers above zero, // so there will be a zero fan speed at least at the 1st layer. @@ -840,43 +850,52 @@ std::string CoolingBuffer::apply_layer_cooldown( if (EXTRUDER_CONFIG(cooling)) { if (layer_time < slowdown_below_layer_time) { // Layer time very short. Enable the fan to a full throttle. - fan_speed_new = max_fan_speed; - custom_fan_speed_limits.first = fan_speed_new; + fan_speed_new = max_fan_speed; + requested_fan_speed_limits.min_speed = max_fan_speed; } else if (layer_time < fan_below_layer_time) { // Layer time quite short. Enable the fan proportionally according to the current layer time. assert(layer_time >= slowdown_below_layer_time); - double t = (layer_time - slowdown_below_layer_time) / (fan_below_layer_time - slowdown_below_layer_time); - fan_speed_new = int(floor(t * min_fan_speed + (1. - t) * max_fan_speed) + 0.5); - custom_fan_speed_limits.first = fan_speed_new; + const double t = (layer_time - slowdown_below_layer_time) / (fan_below_layer_time - slowdown_below_layer_time); + + fan_speed_new = int(floor(t * min_fan_speed + (1. - t) * max_fan_speed) + 0.5); + requested_fan_speed_limits.min_speed = fan_speed_new; } } - bridge_fan_speed = EXTRUDER_CONFIG(bridge_fan_speed); + + bridge_fan_speed = EXTRUDER_CONFIG(bridge_fan_speed); if (int(layer_id) >= disable_fan_first_layers && int(layer_id) + 1 < full_fan_speed_layer) { // Ramp up the fan speed from disable_fan_first_layers to full_fan_speed_layer. - float factor = float(int(layer_id + 1) - disable_fan_first_layers) / float(full_fan_speed_layer - disable_fan_first_layers); - fan_speed_new = std::clamp(int(float(fan_speed_new) * factor + 0.5f), 0, 100); - bridge_fan_speed = std::clamp(int(float(bridge_fan_speed) * factor + 0.5f), 0, 100); - custom_fan_speed_limits.second = fan_speed_new; + const float factor = float(int(layer_id + 1) - disable_fan_first_layers) / float(full_fan_speed_layer - disable_fan_first_layers); + + fan_speed_new = std::clamp(int(float(fan_speed_new) * factor + 0.5f), 0, 100); + bridge_fan_speed = std::clamp(int(float(bridge_fan_speed) * factor + 0.5f), 0, 100); + requested_fan_speed_limits.max_speed = fan_speed_new; } + #undef EXTRUDER_CONFIG bridge_fan_control = bridge_fan_speed > fan_speed_new; } else { // fan disabled - bridge_fan_control = false; - bridge_fan_speed = 0; - fan_speed_new = 0; - custom_fan_speed_limits.second = 0; + bridge_fan_control = false; + bridge_fan_speed = 0; + fan_speed_new = 0; + requested_fan_speed_limits.max_speed = 0; } + + requested_fan_speed_limits.min_speed = std::min(requested_fan_speed_limits.min_speed, requested_fan_speed_limits.max_speed); + if (requested_fan_speed >= 0) { + fan_speed_new = std::clamp(requested_fan_speed, requested_fan_speed_limits.min_speed, requested_fan_speed_limits.max_speed); + } + if (fan_speed_new != m_fan_speed) { m_fan_speed = fan_speed_new; new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, m_fan_speed); } - custom_fan_speed_limits.first = std::min(custom_fan_speed_limits.first, custom_fan_speed_limits.second); - return custom_fan_speed_limits; }; const char *pos = gcode.c_str(); int current_feedrate = 0; - std::pair fan_speed_limits = change_extruder_set_fan(); + + change_extruder_set_fan(); for (const CoolingLine *line : lines) { const char *line_start = gcode.c_str() + line->line_start; const char *line_end = gcode.c_str() + line->line_end; @@ -887,17 +906,13 @@ std::string CoolingBuffer::apply_layer_cooldown( auto res = std::from_chars(line_start + m_toolchange_prefix.size(), line_end, new_extruder); if (res.ec != std::errc::invalid_argument && new_extruder != m_current_extruder) { m_current_extruder = new_extruder; - fan_speed_limits = change_extruder_set_fan(); + change_extruder_set_fan(); } new_gcode.append(line_start, line_end - line_start); } else if (line->type & CoolingLine::TYPE_SET_FAN_SPEED) { - int new_speed = std::clamp(line->fan_speed, fan_speed_limits.first, fan_speed_limits.second); - if (m_fan_speed != new_speed) { - new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, new_speed); - m_fan_speed = new_speed; - } + change_extruder_set_fan(line->fan_speed); } else if (line->type & CoolingLine::TYPE_RESET_FAN_SPEED){ - fan_speed_limits = change_extruder_set_fan(); + change_extruder_set_fan(); } else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_START) { if (bridge_fan_control) new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, bridge_fan_speed); diff --git a/src/libslic3r/GCode/ExtrusionProcessor.cpp b/src/libslic3r/GCode/ExtrusionProcessor.cpp index d818581e8f..eb89b5ed57 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.cpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.cpp @@ -215,7 +215,8 @@ OverhangSpeeds calculate_overhang_speed(const ExtrusionAttributes &attributes, const FullPrintConfig &config, const size_t extruder_id, const float external_perimeter_reference_speed, - const float default_speed) + const float default_speed, + const std::optional ¤t_fan_speed) { assert(attributes.overhang_attributes.has_value()); @@ -249,6 +250,9 @@ OverhangSpeeds calculate_overhang_speed(const ExtrusionAttributes &attributes, if (!config.enable_dynamic_fan_speeds.get_at(extruder_id)) { overhang_speeds.fan_speed = -1; + } else if (current_fan_speed.has_value() && (fan_speed < *current_fan_speed) && (*current_fan_speed - fan_speed) <= MIN_FAN_SPEED_NEGATIVE_CHANGE_TO_EMIT) { + // Always allow the fan speed to be increased without any hysteresis, but the speed will be decreased only when it exceeds a limit for minimum change. + overhang_speeds.fan_speed = *current_fan_speed; } return overhang_speeds; diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 0b0610bc30..c197f91a76 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -44,6 +44,10 @@ class Linef; namespace Slic3r::ExtrusionProcessor { +// Minimum decrease of the fan speed in percents that will be emitted into g-code. +// Decreases below this limit will be omitted to not overflow the g-code with fan speed changes. +const constexpr float MIN_FAN_SPEED_NEGATIVE_CHANGE_TO_EMIT = 3.f; + struct ExtendedPoint { Vec2d position; @@ -276,7 +280,8 @@ OverhangSpeeds calculate_overhang_speed(const ExtrusionAttributes &attributes, const FullPrintConfig &config, size_t extruder_id, float external_perimeter_reference_speed, - float default_speed); + float default_speed, + const std::optional ¤t_fan_speed); } // namespace Slic3r::ExtrusionProcessor