Merge branch 'lh_dynamic_fan_speed_fix'

This commit is contained in:
Lukas Matena 2024-08-09 09:57:10 +02:00
commit a2cabdd74b
6 changed files with 243 additions and 115 deletions

View File

@ -2874,10 +2874,37 @@ std::string GCodeGenerator::change_layer(
std::string GCodeGenerator::extrude_smooth_path( std::string GCodeGenerator::extrude_smooth_path(
const GCode::SmoothPath &smooth_path, const bool is_loop, const std::string_view description, const double speed 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; 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 // reset acceleration
gcode += m_writer.set_print_acceleration(fast_round_up<unsigned int>(m_config.default_acceleration.value)); gcode += m_writer.set_print_acceleration(fast_round_up<unsigned int>(m_config.default_acceleration.value));
@ -2889,6 +2916,7 @@ std::string GCodeGenerator::extrude_smooth_path(
GCode::reverse(reversed_smooth_path); GCode::reverse(reversed_smooth_path);
m_wipe.set_path(std::move(reversed_smooth_path)); m_wipe.set_path(std::move(reversed_smooth_path));
} }
return gcode; return gcode;
} }
@ -3115,7 +3143,8 @@ std::string GCodeGenerator::_extrude(
const ExtrusionAttributes &path_attr, const ExtrusionAttributes &path_attr,
const Geometry::ArcWelder::Path &path, const Geometry::ArcWelder::Path &path,
const std::string_view description, const std::string_view description,
double speed) double speed,
const EmitModifiers &emit_modifiers)
{ {
std::string gcode; std::string gcode;
const std::string_view description_bridge = path_attr.role.is_bridge() ? " (bridge)"sv : ""sv; 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()) else if (this->object_layer_over_raft())
speed = m_config.get_abs_value("first_layer_speed_over_raft", speed); speed = m_config.get_abs_value("first_layer_speed_over_raft", speed);
std::pair<float, float> 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()) { if (path_attr.overhang_attributes.has_value()) {
double external_perim_reference_speed = m_config.get_abs_value("external_perimeter_speed"); double external_perimeter_reference_speed = m_config.get_abs_value("external_perimeter_speed");
if (external_perim_reference_speed == 0) if (external_perimeter_reference_speed == 0) {
external_perim_reference_speed = m_volumetric_speed / path_attr.mm3_per_mm; external_perimeter_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
);
dynamic_speed_and_fan_speed = ExtrusionProcessor::calculate_overhang_speed(path_attr, this->m_config, m_writer.extruder()->id(), external_perimeter_reference_speed = cap_speed(external_perimeter_reference_speed, m_config, m_writer.extruder()->id(), path_attr);
external_perim_reference_speed, speed); 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) { if (dynamic_print_and_fan_speeds.print_speed > -1) {
speed = dynamic_speed_and_fan_speed.first; speed = dynamic_print_and_fan_speeds.print_speed;
} }
// cap speed with max_volumetric_speed anyway (even if user is not using autospeed) // 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; std::string cooling_marker_setspeed_comments;
if (m_enable_cooling_markers) { 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"; gcode += ";_BRIDGE_FAN_START\n";
else } else if (!path_attr.role.is_bridge()) {
cooling_marker_setspeed_comments = ";_EXTRUDE_SET_SPEED"; 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"; cooling_marker_setspeed_comments += ";_EXTERNAL_PERIMETER";
}
} }
// F is mm per minute. // F is mm per minute.
gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments); 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; std::string comment;
if (m_config.gcode_comments) { if (m_config.gcode_comments) {
@ -3343,11 +3384,18 @@ 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 (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"; gcode += ";_RESET_FAN_SPEED\n";
}
this->last_position = path.back().point; this->last_position = path.back().point;
return gcode; return gcode;

View File

@ -436,6 +436,9 @@ private:
std::unique_ptr<PressureEqualizer> m_pressure_equalizer; std::unique_ptr<PressureEqualizer> m_pressure_equalizer;
std::unique_ptr<GCode::WipeTowerIntegration> m_wipe_tower; std::unique_ptr<GCode::WipeTowerIntegration> m_wipe_tower;
// Current fan speed set by dynamic fan speed control.
std::optional<float> m_current_dynamic_fan_speed;
// Heights (print_z) at which the skirt has already been extruded. // Heights (print_z) at which the skirt has already been extruded.
std::vector<coordf_t> m_skirt_done; std::vector<coordf_t> m_skirt_done;
// Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print. // 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). // Back-pointer to Print (const).
const Print* m_print; const Print* m_print;
std::string _extrude( struct EmitModifiers {
const ExtrusionAttributes &attribs, const Geometry::ArcWelder::Path &path, const std::string_view description, double speed = -1); 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_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_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); void _print_first_layer_bed_temperature(GCodeOutputStream &file, const Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);

View File

@ -95,6 +95,7 @@ struct CoolingLine
TYPE_ADJUSTABLE_EMPTY = 1 << 16, TYPE_ADJUSTABLE_EMPTY = 1 << 16,
// Custom fan speed (introduced for overhang fan speed) // Custom fan speed (introduced for overhang fan speed)
TYPE_SET_FAN_SPEED = 1 << 17, TYPE_SET_FAN_SPEED = 1 << 17,
// Reset fan speed back to speed calculate by the CoolingBuffer.
TYPE_RESET_FAN_SPEED = 1 << 18, TYPE_RESET_FAN_SPEED = 1 << 18,
}; };
@ -571,19 +572,20 @@ std::vector<PerExtruderAdjustments> 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); fast_float::from_chars(sline.data() + (has_S ? pos_S : pos_P) + 1, sline.data() + sline.size(), line.time);
if (has_P) if (has_P)
line.time *= 0.001f; line.time *= 0.001f;
} else } else {
line.time = 0; 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'); auto speed_start = sline.find_last_of('D');
int speed = 0; int speed = 0;
for (char num : sline.substr(speed_start + 1)) { for (char num : sline.substr(speed_start + 1)) {
speed = speed * 10 + (num - '0'); speed = speed * 10 + (num - '0');
} }
line.type |= CoolingLine::TYPE_SET_FAN_SPEED;
line.fan_speed = speed; line.fan_speed = speed;
line.type |= CoolingLine::TYPE_SET_FAN_SPEED;
} else if (boost::contains(sline, ";_RESET_FAN_SPEED")) { } else if (boost::contains(sline, ";_RESET_FAN_SPEED")) {
line.type |= CoolingLine::TYPE_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); new_gcode.reserve(gcode.size() * 2);
bool bridge_fan_control = false; bool bridge_fan_control = false;
int bridge_fan_speed = 0; 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) #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder)
int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed); const int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed);
int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0;
std::pair<int, int> custom_fan_speed_limits{fan_speed_new, 100 };
int disable_fan_first_layers = EXTRUDER_CONFIG(disable_fan_first_layers);
// Is the fan speed ramp enabled? // 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) { 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, // 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. // 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 (EXTRUDER_CONFIG(cooling)) {
if (layer_time < slowdown_below_layer_time) { if (layer_time < slowdown_below_layer_time) {
// Layer time very short. Enable the fan to a full throttle. // Layer time very short. Enable the fan to a full throttle.
fan_speed_new = max_fan_speed; fan_speed_new = max_fan_speed;
custom_fan_speed_limits.first = fan_speed_new; requested_fan_speed_limits.min_speed = max_fan_speed;
} else if (layer_time < fan_below_layer_time) { } else if (layer_time < fan_below_layer_time) {
// Layer time quite short. Enable the fan proportionally according to the current layer time. // Layer time quite short. Enable the fan proportionally according to the current layer time.
assert(layer_time >= slowdown_below_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); 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);
custom_fan_speed_limits.first = fan_speed_new; 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) { 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. // 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); 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); fan_speed_new = std::clamp(int(float(fan_speed_new) * factor + 0.5f), 0, 100);
custom_fan_speed_limits.second = fan_speed_new; 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 #undef EXTRUDER_CONFIG
bridge_fan_control = bridge_fan_speed > fan_speed_new; bridge_fan_control = bridge_fan_speed > fan_speed_new;
} else { // fan disabled } else { // fan disabled
bridge_fan_control = false; bridge_fan_control = false;
bridge_fan_speed = 0; bridge_fan_speed = 0;
fan_speed_new = 0; fan_speed_new = 0;
custom_fan_speed_limits.second = 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) { if (fan_speed_new != m_fan_speed) {
m_fan_speed = fan_speed_new; m_fan_speed = fan_speed_new;
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, m_fan_speed); 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(); const char *pos = gcode.c_str();
int current_feedrate = 0; int current_feedrate = 0;
std::pair<int,int> fan_speed_limits = change_extruder_set_fan();
change_extruder_set_fan();
for (const CoolingLine *line : lines) { for (const CoolingLine *line : lines) {
const char *line_start = gcode.c_str() + line->line_start; const char *line_start = gcode.c_str() + line->line_start;
const char *line_end = gcode.c_str() + line->line_end; 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); 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) { if (res.ec != std::errc::invalid_argument && new_extruder != m_current_extruder) {
m_current_extruder = new_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); new_gcode.append(line_start, line_end - line_start);
} else if (line->type & CoolingLine::TYPE_SET_FAN_SPEED) { } 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); change_extruder_set_fan(line->fan_speed);
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;
}
} else if (line->type & CoolingLine::TYPE_RESET_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) { } else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_START) {
if (bridge_fan_control) if (bridge_fan_control)
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, bridge_fan_speed); new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, bridge_fan_speed);

View File

@ -11,7 +11,7 @@
#include "libslic3r/Line.hpp" #include "libslic3r/Line.hpp"
#include "libslic3r/Polygon.hpp" #include "libslic3r/Polygon.hpp"
namespace Slic3r { namespace ExtrusionProcessor { namespace Slic3r::ExtrusionProcessor {
ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path, ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path,
const AABBTreeLines::LinesDistancer<Linef> &unscaled_prev_layer, const AABBTreeLines::LinesDistancer<Linef> &unscaled_prev_layer,
@ -146,57 +146,85 @@ ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const Extru
return result; return result;
}; };
static std::map<float, float> calc_print_speed_sections(const ExtrusionAttributes &attributes,
std::pair<float,float> calculate_overhang_speed(const ExtrusionAttributes &attributes, const FullPrintConfig &config,
const FullPrintConfig &config, const float external_perimeter_reference_speed,
size_t extruder_id, const float default_speed)
float external_perim_reference_speed,
float default_speed)
{ {
assert(attributes.overhang_attributes.has_value()); struct OverhangWithSpeed
std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_with_speeds = { {
{100, ConfigOptionFloatOrPercent{default_speed, false}}}; int percent;
ConfigOptionFloatOrPercent print_speed;
};
std::vector<OverhangWithSpeed> overhangs_with_speeds = {{100, ConfigOptionFloatOrPercent{default_speed, false}}};
if (config.enable_dynamic_overhang_speeds) { if (config.enable_dynamic_overhang_speeds) {
overhangs_with_speeds = {{0, config.overhang_speed_0}, overhangs_with_speeds = {{ 0, config.overhang_speed_0},
{25, config.overhang_speed_1}, { 25, config.overhang_speed_1},
{50, config.overhang_speed_2}, { 50, config.overhang_speed_2},
{75, config.overhang_speed_3}, { 75, config.overhang_speed_3},
{100, ConfigOptionFloatOrPercent{default_speed, false}}}; {100, ConfigOptionFloatOrPercent{default_speed, false}}};
} }
std::vector<std::pair<int, ConfigOptionInts>> overhang_with_fan_speeds = {{100, ConfigOptionInts{0}}}; const float speed_base = external_perimeter_reference_speed > 0 ? external_perimeter_reference_speed : default_speed;
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;
std::map<float, float> speed_sections; std::map<float, float> speed_sections;
for (size_t i = 0; i < overhangs_with_speeds.size(); i++) { for (OverhangWithSpeed &overhangs_with_speed : overhangs_with_speeds) {
float distance = attributes.width * (1.0 - (overhangs_with_speeds[i].first / 100.0)); const float distance = attributes.width * (1.f - (float(overhangs_with_speed.percent) / 100.f));
float speed = overhangs_with_speeds[i].second.percent ? (speed_base * overhangs_with_speeds[i].second.value / 100.0) : float speed = float(overhangs_with_speed.print_speed.get_abs_value(speed_base));
overhangs_with_speeds[i].second.value;
if (speed < EPSILON) if (speed < EPSILON) {
speed = speed_base; speed = speed_base;
}
speed_sections[distance] = speed; speed_sections[distance] = speed;
} }
return speed_sections;
}
static std::map<float, float> calc_fan_speed_sections(const ExtrusionAttributes &attributes,
const FullPrintConfig &config,
const size_t extruder_id)
{
struct OverhangWithFanSpeed
{
int percent;
ConfigOptionInts fan_speed;
};
std::vector<OverhangWithFanSpeed> 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<float, float> fan_speed_sections; std::map<float, float> fan_speed_sections;
for (size_t i = 0; i < overhang_with_fan_speeds.size(); i++) { for (OverhangWithFanSpeed &overhang_with_fan_speed : overhang_with_fan_speeds) {
float distance = attributes.width * (1.0 - (overhang_with_fan_speeds[i].first / 100.0)); float distance = attributes.width * (1.f - (float(overhang_with_fan_speed.percent) / 100.f));
float fan_speed = overhang_with_fan_speeds[i].second.get_at(extruder_id); float fan_speed = float(overhang_with_fan_speed.fan_speed.get_at(extruder_id));
fan_speed_sections[distance] = fan_speed; 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<float> &current_fan_speed)
{
assert(attributes.overhang_attributes.has_value());
auto interpolate_speed = [](const std::map<float, float> &values, float distance) { auto interpolate_speed = [](const std::map<float, float> &values, float distance) {
auto upper_dist = values.lower_bound(distance); auto upper_dist = values.lower_bound(distance);
if (upper_dist == values.end()) { if (upper_dist == values.end()) {
return values.rbegin()->second; return values.rbegin()->second;
} } else if (upper_dist == values.begin()) {
if (upper_dist == values.begin()) {
return upper_dist->second; return upper_dist->second;
} }
@ -205,22 +233,29 @@ std::pair<float,float> calculate_overhang_speed(const ExtrusionAttributes &attri
return (1.0f - t) * lower_dist->second + t * upper_dist->second; 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), const std::map<float, float> speed_sections = calc_print_speed_sections(attributes, config, external_perimeter_reference_speed, default_speed);
interpolate_speed(speed_sections, attributes.overhang_attributes->end_distance_from_prev_layer)); const std::map<float, float> fan_speed_sections = calc_fan_speed_sections(attributes, config, extruder_id);
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 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) { if (!config.enable_dynamic_overhang_speeds) {
final_speed = -1; overhang_speeds.print_speed = -1;
}
if (!config.enable_dynamic_fan_speeds.get_at(extruder_id)) {
fan_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

View File

@ -42,7 +42,11 @@ class CurledLine;
class Linef; class Linef;
} // namespace Slic3r } // 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 struct ExtendedPoint
{ {
@ -51,6 +55,12 @@ struct ExtendedPoint
float curvature; float curvature;
}; };
struct OverhangSpeeds
{
float print_speed;
float fan_speed;
};
template<bool SCALED_INPUT, bool ADD_INTERSECTIONS, bool PREV_LAYER_BOUNDARY_OFFSET, bool SIGNED_DISTANCE, typename POINTS, typename L> template<bool SCALED_INPUT, bool ADD_INTERSECTIONS, bool PREV_LAYER_BOUNDARY_OFFSET, bool SIGNED_DISTANCE, typename POINTS, typename L>
std::vector<ExtendedPoint> estimate_points_properties(const POINTS &input_points, std::vector<ExtendedPoint> estimate_points_properties(const POINTS &input_points,
const AABBTreeLines::LinesDistancer<L> &unscaled_prev_layer, const AABBTreeLines::LinesDistancer<L> &unscaled_prev_layer,
@ -266,12 +276,13 @@ ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(
const AABBTreeLines::LinesDistancer<Linef> &unscaled_prev_layer, const AABBTreeLines::LinesDistancer<Linef> &unscaled_prev_layer,
const AABBTreeLines::LinesDistancer<CurledLine> &prev_layer_curled_lines); const AABBTreeLines::LinesDistancer<CurledLine> &prev_layer_curled_lines);
std::pair<float, float> calculate_overhang_speed(const ExtrusionAttributes &attributes, OverhangSpeeds calculate_overhang_speed(const ExtrusionAttributes &attributes,
const FullPrintConfig &config, const FullPrintConfig &config,
size_t extruder_id, size_t extruder_id,
float external_perim_reference_speed, float external_perimeter_reference_speed,
float default_speed); float default_speed,
const std::optional<float> &current_fan_speed);
}} // namespace Slic3r::ExtrusionProcessor } // namespace Slic3r::ExtrusionProcessor
#endif // slic3r_ExtrusionProcessor_hpp_ #endif // slic3r_ExtrusionProcessor_hpp_

View File

@ -2389,7 +2389,7 @@ void TabFilament::toggle_options()
bool dynamic_fan_speeds = m_config->opt_bool("enable_dynamic_fan_speeds", 0); bool dynamic_fan_speeds = m_config->opt_bool("enable_dynamic_fan_speeds", 0);
for (int i = 0; i < 4; i++) { 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);
} }
} }