Apply per-variant cooling, ramming and temperature range on nozzle-rack slices

Fan speeds, multi-tool ramming, the tower interface and flush temperature
fallbacks and the custom G-code placeholders now use the extruder variant a
filament prints with on each layer, instead of reading by filament id.
This commit is contained in:
SoftFever
2026-10-01 02:54:18 +08:00
parent 0028e65763
commit c03f4925a9
12 changed files with 166 additions and 78 deletions
@@ -308,8 +308,8 @@ DynamicPrintConfig slice_config(PresetBundle &bundle)
// type (Direct Drive + Bowden) the mismatched lookup spams [error] lines. Single-nozzle and non-BBL // type (Direct Drive + Bowden) the mismatched lookup spams [error] lines. Single-nozzle and non-BBL
// printers keep the default map (their toolchange rides the AMS/tool-changer path unchanged). // printers keep the default map (their toolchange rides the AMS/tool-changer path unchanged).
const bool pin_filament_map = bundle.is_bbl_vendor() && nozzles > 1; const bool pin_filament_map = bundle.is_bbl_vendor() && nozzles > 1;
auto &fmap = bundle.project_config.option<ConfigOptionInts>("filament_map", true)->values;
if (pin_filament_map) { if (pin_filament_map) {
auto &fmap = bundle.project_config.option<ConfigOptionInts>("filament_map", true)->values;
for (size_t i = 0; i < fmap.size(); ++i) for (size_t i = 0; i < fmap.size(); ++i)
fmap[i] = int(i % nozzles) + 1; fmap[i] = int(i % nozzles) + 1;
} }
@@ -317,9 +317,8 @@ DynamicPrintConfig slice_config(PresetBundle &bundle)
// A fresh printer selection uses its declared nozzle volumes, just like the // A fresh printer selection uses its declared nozzle volumes, just like the
// app. Otherwise a high-flow preset is silently sliced with Standard tuning. // app. Otherwise a high-flow preset is silently sliced with Standard tuning.
bundle.reset_default_nozzle_volume_type(); bundle.reset_default_nozzle_volume_type();
auto &filament_map = bundle.project_config.option<ConfigOptionInts>("filament_map", true)->values;
bundle.project_config.option<ConfigOptionInts>("filament_volume_map", true)->values = bundle.project_config.option<ConfigOptionInts>("filament_volume_map", true)->values =
bundle.get_default_nozzle_volume_types_for_filaments(filament_map); bundle.get_default_nozzle_volume_types_for_filaments(fmap);
DynamicPrintConfig cfg = bundle.full_config(); DynamicPrintConfig cfg = bundle.full_config();
cfg.set_key_value("enable_prime_tower", new ConfigOptionBool(true)); // force a purge tower so the change is detectable cfg.set_key_value("enable_prime_tower", new ConfigOptionBool(true)); // force a purge tower so the change is detectable
+27 -8
View File
@@ -1258,7 +1258,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
config.set_key_value("old_filament_temp", new ConfigOptionInt(old_filament_temp)); config.set_key_value("old_filament_temp", new ConfigOptionInt(old_filament_temp));
int interface_temp = full_config.filament_tower_interface_print_temp.get_at(new_filament_id); int interface_temp = full_config.filament_tower_interface_print_temp.get_at(new_filament_id);
if (interface_temp == -1) if (interface_temp == -1)
interface_temp = full_config.nozzle_temperature_range_high.get_at(new_filament_id); interface_temp = full_config.nozzle_temperature_range_high.get_at(new_fi);
if (full_config.enable_tower_interface_features && tcr.is_contact) if (full_config.enable_tower_interface_features && tcr.is_contact)
new_filament_temp = interface_temp; new_filament_temp = interface_temp;
config.set_key_value("new_filament_temp", new ConfigOptionInt(new_filament_temp)); config.set_key_value("new_filament_temp", new ConfigOptionInt(new_filament_temp));
@@ -1309,7 +1309,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
flush_temps[idx] = use_fast_flush ? m_print_config->filament_flush_temp_fast.get_at(fi) flush_temps[idx] = use_fast_flush ? m_print_config->filament_flush_temp_fast.get_at(fi)
: m_print_config->filament_flush_temp.get_at(fi); : m_print_config->filament_flush_temp.get_at(fi);
if (flush_temps[idx] == 0) if (flush_temps[idx] == 0)
flush_temps[idx] = m_print_config->nozzle_temperature_range_high.get_at(idx); flush_temps[idx] = m_print_config->nozzle_temperature_range_high.get_at(fi);
filament_cooling_before_tower[idx] = m_print_config->filament_cooling_before_tower.get_at(fi); filament_cooling_before_tower[idx] = m_print_config->filament_cooling_before_tower.get_at(fi);
} }
if (tcr.is_contact || gcodegen.m_layer_index == 0) if (tcr.is_contact || gcodegen.m_layer_index == 0)
@@ -1563,7 +1563,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
const bool will_go_down = !is_approx(z, current_z); const bool will_go_down = !is_approx(z, current_z);
const bool is_ramming = (gcodegen.config().single_extruder_multi_material) || const bool is_ramming = (gcodegen.config().single_extruder_multi_material) ||
(!gcodegen.config().single_extruder_multi_material && (!gcodegen.config().single_extruder_multi_material &&
gcodegen.config().filament_multitool_ramming.get_at(tcr.initial_tool)); gcodegen.config().filament_multitool_ramming.get_at(gcodegen.get_filament_config_index(tcr.initial_tool)));
// Orca: user-facing override (Printer Settings > Wipe tower > "Tool change on wipe tower"). // Orca: user-facing override (Printer Settings > Wipe tower > "Tool change on wipe tower").
// Forces the toolhead to travel over the wipe tower before issuing Tx even on multi-toolhead // Forces the toolhead to travel over the wipe tower before issuing Tx even on multi-toolhead
// printers without ramming, where Orca would otherwise emit Tx in place (potentially over the part). // printers without ramming, where Orca would otherwise emit Tx in place (potentially over the part).
@@ -1620,7 +1620,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
if (gcodegen.config().enable_tower_interface_features && tcr.is_contact) { if (gcodegen.config().enable_tower_interface_features && tcr.is_contact) {
interface_temp = gcodegen.config().filament_tower_interface_print_temp.get_at(new_extruder_id); interface_temp = gcodegen.config().filament_tower_interface_print_temp.get_at(new_extruder_id);
if (interface_temp == -1) if (interface_temp == -1)
interface_temp = gcodegen.config().nozzle_temperature_range_high.get_at(new_extruder_id); interface_temp = gcodegen.config().nozzle_temperature_range_high.get_at(gcodegen.get_filament_config_index(new_extruder_id));
toolchange_temp_override = interface_temp; toolchange_temp_override = interface_temp;
} }
toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z, false, toolchange_temp_override, toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z, false, toolchange_temp_override,
@@ -4342,6 +4342,12 @@ size_t GCode::get_filament_config_index(int filament_id) const
return filament_id; return filament_id;
} }
size_t GCode::get_filament_config_index(int filament_id, size_t layer_id) const
{
// Orca: uncached, as the stages after the generator run concurrently with it.
return m_print ? m_print->get_filament_config_indx(filament_id, (int) layer_id, false) : filament_id;
}
size_t GCode::get_nozzle_config_index(int filament_id) const size_t GCode::get_nozzle_config_index(int filament_id) const
{ {
if (m_print) { if (m_print) {
@@ -9409,10 +9415,13 @@ void GCode::update_placeholder_parser_with_variant_params()
// Helper: remap config arrays from variant index space to filament_id index space. // Helper: remap config arrays from variant index space to filament_id index space.
// After remapping, gcode templates can use param[filament_id] directly. // After remapping, gcode templates can use param[filament_id] directly.
std::vector<size_t> config_index(num_filaments);
for (size_t i = 0; i < num_filaments; ++i)
config_index[i] = get_filament_config_index(i);
auto remap_by_filament = [&](const auto &src) { auto remap_by_filament = [&](const auto &src) {
std::decay_t<decltype(src.values)> dst(num_filaments); std::decay_t<decltype(src.values)> dst(num_filaments);
for (size_t i = 0; i < num_filaments; ++i) for (size_t i = 0; i < num_filaments; ++i)
dst[i] = src.get_at(get_filament_config_index(i)); dst[i] = src.get_at(config_index[i]);
return dst; return dst;
}; };
@@ -9427,6 +9436,16 @@ void GCode::update_placeholder_parser_with_variant_params()
this->placeholder_parser().set("first_layer_temperature", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature_initial_layer))); this->placeholder_parser().set("first_layer_temperature", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature_initial_layer)));
this->placeholder_parser().set("pressure_advance", new ConfigOptionFloats(remap_by_filament(m_config.pressure_advance))); this->placeholder_parser().set("pressure_advance", new ConfigOptionFloats(remap_by_filament(m_config.pressure_advance)));
this->placeholder_parser().set("enable_pressure_advance", new ConfigOptionBools(remap_by_filament(m_config.enable_pressure_advance))); this->placeholder_parser().set("enable_pressure_advance", new ConfigOptionBools(remap_by_filament(m_config.enable_pressure_advance)));
this->placeholder_parser().set("fan_min_speed", new ConfigOptionFloats(remap_by_filament(m_config.fan_min_speed)));
this->placeholder_parser().set("fan_max_speed", new ConfigOptionFloats(remap_by_filament(m_config.fan_max_speed)));
this->placeholder_parser().set("additional_cooling_fan_speed", new ConfigOptionInts(remap_by_filament(m_config.additional_cooling_fan_speed)));
this->placeholder_parser().set("filament_minimal_purge_on_wipe_tower", new ConfigOptionFloats(remap_by_filament(m_config.filament_minimal_purge_on_wipe_tower)));
this->placeholder_parser().set("filament_multitool_ramming", new ConfigOptionBools(remap_by_filament(m_config.filament_multitool_ramming)));
this->placeholder_parser().set("filament_multitool_ramming_volume", new ConfigOptionFloats(remap_by_filament(m_config.filament_multitool_ramming_volume)));
this->placeholder_parser().set("filament_multitool_ramming_flow", new ConfigOptionFloats(remap_by_filament(m_config.filament_multitool_ramming_flow)));
this->placeholder_parser().set("nozzle_temperature_range_low", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature_range_low)));
const auto nozzle_temperature_range_high = remap_by_filament(m_config.nozzle_temperature_range_high);
this->placeholder_parser().set("nozzle_temperature_range_high", new ConfigOptionInts(nozzle_temperature_range_high));
// --- printer_options_with_variant_1: in m_config these are already merged as filament-indexed --- // --- printer_options_with_variant_1: in m_config these are already merged as filament-indexed ---
this->placeholder_parser().set("retraction_distances_when_cut", new ConfigOptionFloats(remap_by_filament(m_config.retraction_distances_when_cut))); this->placeholder_parser().set("retraction_distances_when_cut", new ConfigOptionFloats(remap_by_filament(m_config.retraction_distances_when_cut)));
@@ -9447,7 +9466,7 @@ void GCode::update_placeholder_parser_with_variant_params()
if (flush_v_speed[i] == 0) if (flush_v_speed[i] == 0)
flush_v_speed[i] = filament_max_v[i]; flush_v_speed[i] = filament_max_v[i];
if (flush_temps[i] == 0) if (flush_temps[i] == 0)
flush_temps[i] = m_config.nozzle_temperature_range_high.get_at(i); flush_temps[i] = nozzle_temperature_range_high[i];
} }
this->placeholder_parser().set("flush_volumetric_speeds", new ConfigOptionFloats(flush_v_speed)); this->placeholder_parser().set("flush_volumetric_speeds", new ConfigOptionFloats(flush_v_speed));
this->placeholder_parser().set("flush_temperatures", new ConfigOptionInts(flush_temps)); this->placeholder_parser().set("flush_temperatures", new ConfigOptionInts(flush_temps));
@@ -9706,7 +9725,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
{ {
int interface_temp = m_config.filament_tower_interface_print_temp.get_at(new_filament_id); int interface_temp = m_config.filament_tower_interface_print_temp.get_at(new_filament_id);
if (interface_temp == -1) if (interface_temp == -1)
interface_temp = m_config.nozzle_temperature_range_high.get_at(new_filament_id); interface_temp = m_config.nozzle_temperature_range_high.get_at(new_fi);
dyn_config.set_key_value("filament_tower_interface_print_temp", new ConfigOptionInt(interface_temp)); dyn_config.set_key_value("filament_tower_interface_print_temp", new ConfigOptionInt(interface_temp));
} }
if (toolchange_temp_override > 0) { if (toolchange_temp_override > 0) {
@@ -9745,7 +9764,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
flush_temps[idx] = use_fast_flush ? m_print->config().filament_flush_temp_fast.get_at(fi) flush_temps[idx] = use_fast_flush ? m_print->config().filament_flush_temp_fast.get_at(fi)
: m_print->config().filament_flush_temp.get_at(fi); : m_print->config().filament_flush_temp.get_at(fi);
if (flush_temps[idx] == 0) if (flush_temps[idx] == 0)
flush_temps[idx] = m_print->config().nozzle_temperature_range_high.get_at(idx); flush_temps[idx] = m_print->config().nozzle_temperature_range_high.get_at(fi);
filament_cooling_before_tower[idx] = m_print->config().filament_cooling_before_tower.get_at(fi); filament_cooling_before_tower[idx] = m_print->config().filament_cooling_before_tower.get_at(fi);
} }
std::fill(filament_cooling_before_tower.begin(), filament_cooling_before_tower.end(), 0); std::fill(filament_cooling_before_tower.begin(), filament_cooling_before_tower.end(), 0);
+3
View File
@@ -296,6 +296,9 @@ public:
// resolver keys filament-indexed arrays, the nozzle resolver keys (extruder x volume-type) // resolver keys filament-indexed arrays, the nozzle resolver keys (extruder x volume-type)
// slot arrays. Both degenerate to filament_id / extruder index on single-volume printers. // slot arrays. Both degenerate to filament_id / extruder index on single-volume printers.
size_t get_filament_config_index(int filament_id) const; size_t get_filament_config_index(int filament_id) const;
// The filament resolver for a given layer, for the export pipeline stages after the generator,
// which run behind the current layer and concurrently with the generator.
size_t get_filament_config_index(int filament_id, size_t layer_id) const;
size_t get_nozzle_config_index(int filament_id) const; size_t get_nozzle_config_index(int filament_id) const;
// Object and support extrusions of the same PrintObject at the same print_z. // Object and support extrusions of the same PrintObject at the same print_z.
+6 -4
View File
@@ -18,7 +18,7 @@
namespace Slic3r { namespace Slic3r {
CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_config(gcodegen.config()), m_toolchange_prefix(gcodegen.writer().toolchange_prefix()), m_current_extruder(0), m_current_nozzle(0) CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_config(gcodegen.config()), m_gcodegen(gcodegen), m_toolchange_prefix(gcodegen.writer().toolchange_prefix()), m_current_extruder(0), m_current_nozzle(0)
{ {
this->reset(gcodegen.writer().get_position()); this->reset(gcodegen.writer().get_position());
@@ -737,10 +737,12 @@ std::string CoolingBuffer::apply_layer_cooldown(
&ironing_fan_control, &ironing_fan_speed &ironing_fan_control, &ironing_fan_speed
](bool immediately_apply) { ](bool immediately_apply) {
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder) #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder)
float fan_min_speed = EXTRUDER_CONFIG(fan_min_speed); // The per-variant options take the extruder variant the filament prints with on this layer
const size_t config_index = m_gcodegen.get_filament_config_index(m_current_extruder, layer_id);
float fan_min_speed = m_config.fan_min_speed.get_at(config_index);
float fan_speed_new = EXTRUDER_CONFIG(reduce_fan_stop_start_freq) ? fan_min_speed : 0; float fan_speed_new = EXTRUDER_CONFIG(reduce_fan_stop_start_freq) ? fan_min_speed : 0;
//BBS //BBS
int additional_fan_speed_new = EXTRUDER_CONFIG(additional_cooling_fan_speed); int additional_fan_speed_new = m_config.additional_cooling_fan_speed.get_at(config_index);
int close_fan_the_first_x_layers = EXTRUDER_CONFIG(close_fan_the_first_x_layers); int close_fan_the_first_x_layers = EXTRUDER_CONFIG(close_fan_the_first_x_layers);
// Is the fan speed ramp enabled? // Is the fan speed ramp enabled?
int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer); int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer);
@@ -776,7 +778,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
// additional_fan_speed_new is left at its configured value (auxiliary fan is independent of the // additional_fan_speed_new is left at its configured value (auxiliary fan is independent of the
// part-cooling override). // part-cooling override).
} else if (int(layer_id) >= close_fan_the_first_x_layers) { } else if (int(layer_id) >= close_fan_the_first_x_layers) {
float fan_max_speed = EXTRUDER_CONFIG(fan_max_speed); float fan_max_speed = m_config.fan_max_speed.get_at(config_index);
float slow_down_layer_time = float(EXTRUDER_CONFIG(slow_down_layer_time)); float slow_down_layer_time = float(EXTRUDER_CONFIG(slow_down_layer_time));
float fan_cooling_layer_time = float(EXTRUDER_CONFIG(fan_cooling_layer_time)); float fan_cooling_layer_time = float(EXTRUDER_CONFIG(fan_cooling_layer_time));
//BBS: always enable the fan speed interpolation according to layer time //BBS: always enable the fan speed interpolation according to layer time
+2
View File
@@ -54,6 +54,8 @@ private:
// Referencs GCode::m_config, which is FullPrintConfig. While the PrintObjectConfig slice of FullPrintConfig is being modified, // Referencs GCode::m_config, which is FullPrintConfig. While the PrintObjectConfig slice of FullPrintConfig is being modified,
// the PrintConfig slice of FullPrintConfig is constant, thus no thread synchronization is required. // the PrintConfig slice of FullPrintConfig is constant, thus no thread synchronization is required.
const PrintConfig &m_config; const PrintConfig &m_config;
// Resolves the filament config index of the per-variant options.
const GCode &m_gcodegen;
unsigned int m_current_extruder; unsigned int m_current_extruder;
unsigned int m_current_nozzle; unsigned int m_current_nozzle;
//BBS: current fan speed //BBS: current fan speed
+14 -8
View File
@@ -1230,15 +1230,21 @@ void ToolOrdering::cal_most_used_extruder(const PrintConfig &config)
float ToolOrdering::cal_max_additional_fan(const PrintConfig &config) float ToolOrdering::cal_max_additional_fan(const PrintConfig &config)
{ {
// record std::set<unsigned int> used_filaments;
for (const LayerTools &layer_tools : m_layer_tools)
used_filaments.insert(layer_tools.extruders.begin(), layer_tools.extruders.end());
if (used_filaments.empty())
return 0;
// Orca: additional_cooling_fan_speed can hold one value per extruder variant a filament prints with;
// filament_self_index maps such a column to its filament.
const std::vector<int> &self_index = config.filament_self_index.values;
const size_t columns = std::max(config.additional_cooling_fan_speed.size(), size_t(*used_filaments.rbegin()) + 1);
float max_fan = 0; float max_fan = 0;
for (LayerTools &layer_tools : m_layer_tools) { for (size_t column = 0; column < columns; ++column) {
std::vector<unsigned int> filaments = layer_tools.extruders; const unsigned int filament_id = self_index.size() == columns ? self_index[column] - 1 : column;
std::set<int> layer_extruder_count; if (used_filaments.count(filament_id) && max_fan < config.additional_cooling_fan_speed.get_at(column))
// count once only max_fan = config.additional_cooling_fan_speed.get_at(column);
for (unsigned int &filament : filaments)
if (max_fan < config.additional_cooling_fan_speed.get_at(filament))
max_fan = config.additional_cooling_fan_speed.get_at(filament);
} }
return max_fan; return max_fan;
} }
+11 -8
View File
@@ -4329,9 +4329,9 @@ bool Print::is_dynamic_group_reorder() const
return true; return true;
} }
int Print::get_filament_config_indx(int filament_id, int layer_id) int Print::get_filament_config_indx(int filament_id, int layer_id, bool use_cache)
{ {
return get_config_index(filament_id, layer_id, m_config.filament_extruder_variant.values, m_filament_self_index, m_filament_index_map); return get_config_index(filament_id, layer_id, m_config.filament_extruder_variant.values, m_filament_self_index, use_cache ? &m_filament_index_map : nullptr);
} }
void Print::update_filament_self_index_cache() void Print::update_filament_self_index_cache()
@@ -4374,7 +4374,7 @@ int Print::get_nozzle_config_index(int filament_id, int layer_id)
return get_config_index(filament_id, layer_id, m_default_region_config.print_extruder_variant.values, m_default_region_config.print_extruder_id.values, m_nozzle_index_map); return get_config_index(filament_id, layer_id, m_default_region_config.print_extruder_variant.values, m_default_region_config.print_extruder_id.values, m_nozzle_index_map);
} }
int Print::get_config_index(int filament_id, int layer_id, const std::vector<std::string> &variant_list, const std::vector<int>& self_index_list, FilamentIndexMap &index_map) int Print::get_config_index(int filament_id, int layer_id, const std::vector<std::string> &variant_list, const std::vector<int>& self_index_list, FilamentIndexMap *index_map)
{ {
auto group_result = get_layered_nozzle_group_result(); auto group_result = get_layered_nozzle_group_result();
// Orca: defensive — when no grouping producer has published a result yet, fall back to the // Orca: defensive — when no grouping producer has published a result yet, fall back to the
@@ -4385,7 +4385,8 @@ int Print::get_config_index(int filament_id, int layer_id, const std::vector<std
if (!nozzle_info.has_value()) { if (!nozzle_info.has_value()) {
// Orca: this fallback runs per-filament/per-layer in the g-code hot path — log once per filament // Orca: this fallback runs per-filament/per-layer in the g-code hot path — log once per filament
// (reset each slice) instead of flooding thousands of identical lines that bury the real error. // (reset each slice) instead of flooding thousands of identical lines that bury the real error.
if (m_missing_nozzle_group_logged.insert(filament_id).second) // Without the cache, the log set is left alone too; the cached caller reports the same filament.
if (index_map && m_missing_nozzle_group_logged.insert(filament_id).second)
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ BOOST_LOG_TRIVIAL(error) << __FUNCTION__
<< boost::format(", Line %1%: could not found group_nozzle_info corresponding to filament_id %2%, layer_id %3% (further occurrences for this filament suppressed)") % __LINE__ % filament_id % << boost::format(", Line %1%: could not found group_nozzle_info corresponding to filament_id %2%, layer_id %3% (further occurrences for this filament suppressed)") % __LINE__ % filament_id %
layer_id; layer_id;
@@ -4394,15 +4395,17 @@ int Print::get_config_index(int filament_id, int layer_id, const std::vector<std
ExtruderType extruder_type = ExtruderType(m_config.extruder_type.get_at(nozzle_info->extruder_id)); ExtruderType extruder_type = ExtruderType(m_config.extruder_type.get_at(nozzle_info->extruder_id));
NozzleVolumeType nozzle_volume_type = nozzle_info->volume_type; NozzleVolumeType nozzle_volume_type = nozzle_info->volume_type;
if (!index_map)
return get_config_index_base(nozzle_volume_type, extruder_type, filament_id + 1, variant_list, self_index_list);
FilamentIndexKey key{filament_id, extruder_type, nozzle_volume_type}; FilamentIndexKey key{filament_id, extruder_type, nozzle_volume_type};
auto iter = index_map.find(key); auto iter = index_map->find(key);
if (iter == index_map.end()) { if (iter == index_map->end()) {
int index = get_config_index_base(nozzle_volume_type, extruder_type, filament_id + 1, variant_list, self_index_list); int index = get_config_index_base(nozzle_volume_type, extruder_type, filament_id + 1, variant_list, self_index_list);
index_map[key] = index; (*index_map)[key] = index;
return index; return index;
} else { } else {
return index_map[key]; return iter->second;
} }
} }
+4 -2
View File
@@ -1222,7 +1222,9 @@ public:
// Post-slicing config-slot resolvers: map a (filament, layer) pair to the index of its // Post-slicing config-slot resolvers: map a (filament, layer) pair to the index of its
// per-(extruder x volume type) column in the expanded variant arrays, cached by grouping context. // per-(extruder x volume type) column in the expanded variant arrays, cached by grouping context.
int get_filament_config_indx(int filament_id, int layer_id); // Orca: without use_cache, the filament resolver leaves the cache alone, for the G-code export
// pipeline's cooling stage, which runs concurrently with the generator stage filling it.
int get_filament_config_indx(int filament_id, int layer_id, bool use_cache = true);
int get_nozzle_config_index(int filament_id, int layer_id); int get_nozzle_config_index(int filament_id, int layer_id);
// Orca: Implement prusa's filament shrink compensation approach // Orca: Implement prusa's filament shrink compensation approach
@@ -1282,7 +1284,7 @@ protected:
}; };
using FilamentIndexMap = std::unordered_map<FilamentIndexKey, int, FilamentIndexKeyHash>; using FilamentIndexMap = std::unordered_map<FilamentIndexKey, int, FilamentIndexKeyHash>;
using PrintIndexMap = std::unordered_map<PrintIndexKey, int, PrintIndexKeyHash>; using PrintIndexMap = std::unordered_map<PrintIndexKey, int, PrintIndexKeyHash>;
int get_config_index(int filament_id, int layer_id, const std::vector<std::string> &variant_list, const std::vector<int>& self_index_list, FilamentIndexMap &index_map); int get_config_index(int filament_id, int layer_id, const std::vector<std::string> &variant_list, const std::vector<int>& self_index_list, FilamentIndexMap *index_map);
int get_config_index(int filament_id, int layer_id, const std::vector<std::string> &variant_list, const std::vector<int>& self_index_list, PrintIndexMap &index_map); int get_config_index(int filament_id, int layer_id, const std::vector<std::string> &variant_list, const std::vector<int>& self_index_list, PrintIndexMap &index_map);
// Invalidates the step, and its depending steps in Print. // Invalidates the step, and its depending steps in Print.
+10 -11
View File
@@ -9463,17 +9463,6 @@ std::set<std::string> print_options_with_variant = {
std::set<std::string> filament_options_with_variant = { std::set<std::string> filament_options_with_variant = {
"filament_flow_ratio", "filament_flow_ratio",
"filament_max_volumetric_speed", "filament_max_volumetric_speed",
"enable_pressure_advance",
"pressure_advance",
"fan_min_speed",
"fan_max_speed",
"additional_cooling_fan_speed",
"filament_minimal_purge_on_wipe_tower",
"filament_multitool_ramming",
"filament_multitool_ramming_volume",
"filament_multitool_ramming_flow",
"nozzle_temperature_range_low",
"nozzle_temperature_range_high",
// Per-variant ramming / pre-cooling / nozzle-change filament overrides // Per-variant ramming / pre-cooling / nozzle-change filament overrides
"filament_ramming_volumetric_speed", "filament_ramming_volumetric_speed",
"filament_pre_cooling_temperature", "filament_pre_cooling_temperature",
@@ -9527,6 +9516,16 @@ std::set<std::string> filament_options_with_variant = {
"adaptive_pressure_advance_model", "adaptive_pressure_advance_model",
"adaptive_pressure_advance_overhangs", "adaptive_pressure_advance_overhangs",
"adaptive_pressure_advance_bridges", "adaptive_pressure_advance_bridges",
// Orca: cooling fans, multi-tool ramming and recommended nozzle temperature range
"fan_min_speed",
"fan_max_speed",
"additional_cooling_fan_speed",
"filament_minimal_purge_on_wipe_tower",
"filament_multitool_ramming",
"filament_multitool_ramming_volume",
"filament_multitool_ramming_flow",
"nozzle_temperature_range_low",
"nozzle_temperature_range_high",
"activate_air_filtration", "activate_air_filtration",
"activate_air_filtration_during_print", "activate_air_filtration_during_print",
"activate_air_filtration_on_completion", "activate_air_filtration_on_completion",
+62
View File
@@ -893,3 +893,65 @@ TEST_CASE("Each filament sets the pressure advance of its extruder variant on a
CHECK(gcode.find("; start pressure advance " + pressure_advance + "\n") != std::string::npos); CHECK(gcode.find("; start pressure advance " + pressure_advance + "\n") != std::string::npos);
} }
} }
// The speeds, in percent, a G-code turns a fan on at: the part cooling fan for `M106 S`, the auxiliary
// fan for `M106 P2 S`.
static std::set<int> fan_speeds(const std::string &gcode, const std::string &command)
{
std::set<int> speeds;
std::istringstream stream(gcode);
for (std::string line; std::getline(stream, line);)
if (line.rfind(command, 0) == 0)
if (const int pwm = std::stoi(line.substr(command.size())); pwm > 0)
speeds.insert(int(std::lround(pwm * 100. / 255.)));
return speeds;
}
// The fan speeds and the recommended nozzle temperature range are tuned per extruder variant like the
// other filament variant settings.
TEST_CASE("Each filament cools with the fan speeds of its extruder variant", "[MultiFilament]")
{
auto [nozzle_volume_type, filament, fan_min_speed, fan_max_speed, additional_fan_speed, range_high] = GENERATE(table<NozzleVolumeType, int, int, int, int, int>({
{ nvtStandard, 1, 15, 25, 10, 240 },
{ nvtHighFlow, 1, 35, 45, 20, 260 },
{ nvtHighFlow, 2, 55, 65, 40, 280 }, // filament 2 defines no High Flow variant
}));
// Layers printed faster than slow_down_layer_time run the fan at its maximum speed, layers slower than
// fan_cooling_layer_time at its minimum.
const bool fast_layers = GENERATE(false, true);
DYNAMIC_SECTION(get_nozzle_volume_type_string(nozzle_volume_type) << " nozzle, filament " << filament << (fast_layers ? ", fast layers" : ", slow layers")) {
DynamicPrintConfig config = multifilament_config(2, {
{ "extruder_variant_list", "Direct Drive Standard,Direct Drive High Flow" },
// filament 1 defines Standard and High Flow, filament 2 Standard
{ "filament_extruder_variant", "Direct Drive Standard;Direct Drive High Flow;Direct Drive Standard" },
{ "filament_self_index", "1,1,2" },
{ "fan_min_speed", "15,35,55" },
{ "fan_max_speed", "25,45,65" },
{ "additional_cooling_fan_speed", "10,20,40" },
{ "nozzle_temperature_range_high", "240,260,280" },
{ "auxiliary_fan", 1 },
{ "reduce_fan_stop_start_freq", "1,1" },
{ "slow_down_layer_time", fast_layers ? "1000,1000" : "0,0" },
{ "fan_cooling_layer_time", fast_layers ? "1000,1000" : "0,0" },
{ "slow_down_for_layer_cooling", "0,0" },
{ "enable_overhang_bridge_fan", "0,0" },
{ "sparse_infill_filament_id", filament },
{ "internal_solid_filament_id", filament },
{ "top_surface_filament_id", filament },
{ "bottom_surface_filament_id", filament },
{ "outer_wall_filament_id", filament },
{ "inner_wall_filament_id", filament },
{ "enable_prime_tower", 0 },
{ "skirt_loops", 0 },
{ "brim_type", "no_brim" },
// custom G-code indexes the per-filament arrays by filament
{ "machine_start_gcode", "; start range high {nozzle_temperature_range_high[initial_extruder]}" },
});
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = { nozzle_volume_type };
const std::string gcode = slice({ cube(20) }, config);
CHECK(fan_speeds(gcode, "M106 S") == std::set<int>{ fast_layers ? fan_max_speed : fan_min_speed });
CHECK(fan_speeds(gcode, "M106 P2 S") == std::set<int>{ additional_fan_speed });
CHECK(gcode.find("; start range high " + std::to_string(range_high) + "\n") != std::string::npos);
}
}
@@ -52,20 +52,18 @@ TEST_CASE("Filament cooling and pressure settings follow the selected nozzle var
{"nozzle_temperature_range_high", "255", "280"}, {"nozzle_temperature_range_high", "255", "280"},
}; };
const int variant_index = GENERATE(0, 1, 2, 3); const int variant_index = GENERATE(0, 1, 2, 3);
const int value_count = 4;
const bool load_preset = GENERATE(false, true); const bool load_preset = GENERATE(false, true);
const bool high_flow = variant_index % 2 != 0; const bool high_flow = variant_index % 2 != 0;
for (const Setting &setting : settings) { for (const Setting &setting : settings) {
DYNAMIC_SECTION(setting.key << " variant=" << variant_index << " values=" << value_count << " loaded=" << load_preset) { DYNAMIC_SECTION(setting.key << " variant=" << variant_index << " loaded=" << load_preset) {
DynamicPrintConfig printer = make_hybrid_printer_config(); DynamicPrintConfig printer = make_hybrid_printer_config();
if (variant_index >= 2) if (variant_index >= 2)
printer.option<ConfigOptionEnumsGeneric>("extruder_type")->values[1] = etBowden; printer.option<ConfigOptionEnumsGeneric>("extruder_type")->values[1] = etBowden;
DynamicPrintConfig filament; DynamicPrintConfig filament;
filament.option<ConfigOptionStrings>("filament_extruder_variant", true)->values = filament.option<ConfigOptionStrings>("filament_extruder_variant", true)->values =
{"Direct Drive Standard", "Direct Drive High Flow", "Bowden Standard", "Bowden High Flow"}; {"Direct Drive Standard", "Direct Drive High Flow", "Bowden Standard", "Bowden High Flow"};
std::string values = setting.standard; // one value per variant of filament_extruder_variant
for (int i = 1; i < value_count; ++i) const std::string values = std::string(setting.standard) + "," + setting.high_flow + "," + setting.standard + "," + setting.high_flow;
values += std::string(",") + (i % 2 ? setting.high_flow : setting.standard);
REQUIRE(filament.option(setting.key, true)->deserialize(values)); REQUIRE(filament.option(setting.key, true)->deserialize(values));
if (load_preset) { if (load_preset) {
@@ -88,35 +86,6 @@ TEST_CASE("Filament cooling and pressure settings follow the selected nozzle var
} }
} }
TEST_CASE("Filaments on the same hybrid tool keep their own cooling and pressure variants", "[Config][FilamentVariants]")
{
DynamicPrintConfig config = make_hybrid_printer_config();
config.option<ConfigOptionInts>("filament_self_index", true)->values = {1, 1, 2, 2};
config.option<ConfigOptionStrings>("filament_extruder_variant", true)->values =
{"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard", "Direct Drive High Flow"};
config.option<ConfigOptionInts>("filament_map", true)->values = {2, 2};
config.option<ConfigOptionInts>("filament_volume_map", true)->values = {nvtHighFlow, nvtStandard};
config.option<ConfigOptionFloats>("fan_max_speed", true)->values = {15., 60., 20., 80.};
config.option<ConfigOptionFloats>("pressure_advance", true)->values = {0.04, 0.02, 0.05, 0.03};
config.option<ConfigOptionBools>("filament_multitool_ramming", true)->values = {false, true, false, true};
std::vector<std::vector<NozzleVolumeType>> nozzle_types;
const int count = config.get_extruder_nozzle_volume_count(2, nozzle_types);
std::set<std::string> keys = filament_options_with_variant;
keys.insert("filament_self_index");
config.update_values_to_printer_extruders_for_multiple_filaments(config, 2, count, keys,
"filament_self_index", "filament_extruder_variant");
const auto &fan = config.option<ConfigOptionFloats>("fan_max_speed")->values;
REQUIRE(fan.size() == 2);
REQUIRE_THAT(fan[0], Catch::Matchers::WithinAbs(60., 1e-9));
REQUIRE_THAT(fan[1], Catch::Matchers::WithinAbs(20., 1e-9));
const auto &pa = config.option<ConfigOptionFloats>("pressure_advance")->values;
REQUIRE(pa.size() == 2);
REQUIRE_THAT(pa[0], Catch::Matchers::WithinAbs(0.02, 1e-9));
REQUIRE_THAT(pa[1], Catch::Matchers::WithinAbs(0.05, 1e-9));
REQUIRE(config.option<ConfigOptionBools>("filament_multitool_ramming")->get_at(0));
REQUIRE_FALSE(config.option<ConfigOptionBools>("filament_multitool_ramming")->get_at(1));
}
TEST_CASE("apply_override fills nil entries from the 0-based default index", "[Config]") TEST_CASE("apply_override fills nil entries from the 0-based default index", "[Config]")
{ {
ConfigOptionFloats machine({10., 20., 30.}); ConfigOptionFloats machine({10., 20., 30.});
@@ -495,6 +464,8 @@ TEST_CASE("update_values_to_printer_extruders_for_multiple_filaments resolves pe
config.option<ConfigOptionStrings>("filament_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow", config.option<ConfigOptionStrings>("filament_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow",
"Direct Drive Standard", "Direct Drive High Flow"}; "Direct Drive Standard", "Direct Drive High Flow"};
config.option<ConfigOptionFloats>("filament_max_volumetric_speed", true)->values = {12., 20., 13., 21.}; config.option<ConfigOptionFloats>("filament_max_volumetric_speed", true)->values = {12., 20., 13., 21.};
config.option<ConfigOptionFloats>("fan_max_speed", true)->values = {15., 60., 20., 80.};
config.option<ConfigOptionBools>("filament_multitool_ramming", true)->values = {false, true, false, true};
}; };
std::set<std::string> filament_keys = filament_options_with_variant; std::set<std::string> filament_keys = filament_options_with_variant;
@@ -514,6 +485,8 @@ TEST_CASE("update_values_to_printer_extruders_for_multiple_filaments resolves pe
"filament_self_index", "filament_extruder_variant"); "filament_self_index", "filament_extruder_variant");
REQUIRE(config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->values == std::vector<double>({12., 21.})); REQUIRE(config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->values == std::vector<double>({12., 21.}));
REQUIRE(config.option<ConfigOptionFloats>("fan_max_speed")->values == std::vector<double>({15., 80.}));
REQUIRE(config.option<ConfigOptionBools>("filament_multitool_ramming")->values == std::vector<unsigned char>({0, 1}));
REQUIRE(config.option<ConfigOptionStrings>("filament_extruder_variant")->values == REQUIRE(config.option<ConfigOptionStrings>("filament_extruder_variant")->values ==
std::vector<std::string>({"Direct Drive Standard", "Direct Drive High Flow"})); std::vector<std::string>({"Direct Drive Standard", "Direct Drive High Flow"}));
REQUIRE(config.option<ConfigOptionInts>("filament_self_index")->values == std::vector<int>({1, 2})); REQUIRE(config.option<ConfigOptionInts>("filament_self_index")->values == std::vector<int>({1, 2}));
@@ -948,6 +948,9 @@ TEST_CASE("Filaments ordered after a migrator shift columns and the resolver tra
config.option<ConfigOptionInts>("nozzle_temperature", true)->values = {200, 210, 220, 230, 240, 250}; config.option<ConfigOptionInts>("nozzle_temperature", true)->values = {200, 210, 220, 230, 240, 250};
config.option<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = {0.5, 0.5, 0.7, 0.9, 1.4, 1.4}; config.option<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = {0.5, 0.5, 0.7, 0.9, 1.4, 1.4};
config.option<ConfigOptionFloats>("retraction_length", true)->values = {0.8, 0.9, 1.0, 1.1}; config.option<ConfigOptionFloats>("retraction_length", true)->values = {0.8, 0.9, 1.0, 1.1};
config.option<ConfigOptionFloats>("fan_max_speed", true)->values = {10, 10, 20, 60, 30, 30};
config.option<ConfigOptionInts>("additional_cooling_fan_speed", true)->values = {1, 1, 2, 6, 3, 3};
config.option<ConfigOptionInts>("nozzle_temperature_range_high", true)->values = {230, 230, 240, 280, 250, 250};
Model model; Model model;
model.add_object("cube", "", make_cube(20, 20, 20))->add_instance(); model.add_object("cube", "", make_cube(20, 20, 20))->add_instance();
@@ -988,6 +991,21 @@ TEST_CASE("Filaments ordered after a migrator shift columns and the resolver tra
REQUIRE(merged.size() == 4); REQUIRE(merged.size() == 4);
REQUIRE_THAT(merged[3], Catch::Matchers::WithinAbs(1.4, 1e-9)); REQUIRE_THAT(merged[3], Catch::Matchers::WithinAbs(1.4, 1e-9));
REQUIRE_THAT(merged[2], Catch::Matchers::WithinAbs(0.9, 1e-9)); REQUIRE_THAT(merged[2], Catch::Matchers::WithinAbs(0.9, 1e-9));
// The cooling and temperature range options follow the variant as well.
const PrintConfig &resolved = print.config();
REQUIRE(resolved.fan_max_speed.values == std::vector<double>{10, 20, 60, 30});
CHECK(resolved.fan_max_speed.get_at(print.get_filament_config_indx(1, 0)) == 20);
CHECK(resolved.fan_max_speed.get_at(print.get_filament_config_indx(1, 1)) == 60);
CHECK(resolved.additional_cooling_fan_speed.get_at(print.get_filament_config_indx(2, 1)) == 3);
CHECK(resolved.nozzle_temperature_range_high.get_at(print.get_filament_config_indx(2, 1)) == 250);
// The auxiliary fan maximum takes every variant of the filaments used, and only theirs.
ToolOrdering ordering;
ordering.layer_tools().emplace_back(0.2);
ordering.layer_tools().back().extruders = {0, 2};
CHECK(ordering.cal_max_additional_fan(resolved) == 3);
ordering.layer_tools().back().extruders = {1};
CHECK(ordering.cal_max_additional_fan(resolved) == 6);
} }
TEST_CASE("Selector slicing keeps the result valid across re-apply", "[Print][H2C][Dynamic]") TEST_CASE("Selector slicing keeps the result valid across re-apply", "[Print][H2C][Dynamic]")