diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index cd12ad34cb..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; @@ -3217,21 +3246,21 @@ 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), + m_current_dynamic_fan_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) @@ -3282,18 +3311,30 @@ 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_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) { + 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; if (m_config.gcode_comments) { @@ -3343,11 +3384,18 @@ std::string GCodeGenerator::_extrude( } } - if (m_enable_cooling_markers) - gcode += path_attr.role.is_bridge() ? ";_BRIDGE_FAN_END\n" : ";_EXTRUDE_END\n"; + if (m_enable_cooling_markers) { + 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_speed_and_fan_speed.second >= 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"; + } this->last_position = path.back().point; return gcode; 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 51acead1cc..eb89b5ed57 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,85 @@ 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, + const std::optional ¤t_fan_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 +233,29 @@ 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; + } 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; } -}} // namespace Slic3r::ExtrusionProcessor +} // namespace Slic3r::ExtrusionProcessor diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 2dc94b2f65..c197f91a76 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -42,7 +42,11 @@ class CurledLine; class Linef; } // namespace Slic3r -namespace Slic3r { namespace ExtrusionProcessor { +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 { @@ -51,6 +55,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 +276,13 @@ 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, + const std::optional ¤t_fan_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); } }