feat(libslic3r): multi-nozzle slicing engine for H2C/A2L

Port BambuStudio's dual-nozzle slicing core: H2C-era config keys, filament-to-nozzle grouping with per-layer dynamic regrouping, filament/nozzle/hotend gcode placeholder vocabulary, multi-nozzle wipe tower pre-heat/pre-cool, the two-pass pre-cooling injector, and corexy farthest-point timelapse.
This commit is contained in:
SoftFever
2026-07-09 00:06:26 +08:00
parent 781ecdc2c1
commit 237ef41b06
36 changed files with 7622 additions and 708 deletions

View File

@@ -83,6 +83,15 @@ const std::vector<std::string> filament_extruder_override_keys = {
"filament_retraction_distances_when_cut"
};
// Some filament override parameters are generated from filament_extruder_override_keys,
// while filament_retract_length_nc is defined separately. Keep the generator list
// unchanged and use this helper for behavior checks that need the full override set.
bool is_filament_extruder_override_key(const std::string &opt_key)
{
return std::find(filament_extruder_override_keys.begin(), filament_extruder_override_keys.end(), opt_key) != filament_extruder_override_keys.end() ||
opt_key == "filament_retract_length_nc";
}
size_t get_extruder_index(const GCodeConfig& config, unsigned int filament_id)
{
if (filament_id < config.filament_map.size()) {
@@ -569,17 +578,28 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ExtruderType)
static const t_config_enum_values s_keys_map_NozzleVolumeType = {
{ "Standard", nvtStandard },
{ "High Flow", nvtHighFlow }
{ "High Flow", nvtHighFlow },
{ "TPU High Flow", nvtTPUHighFlow },
{ "Hybrid", nvtHybrid }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(NozzleVolumeType)
static const t_config_enum_values s_keys_map_FilamentMapMode = {
{ "Auto For Flush", fmmAutoForFlush },
{ "Auto For Match", fmmAutoForMatch },
{ "Manual", fmmManual }
{ "Manual", fmmManual },
{ "Nozzle Manual", fmmNozzleManual }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(FilamentMapMode)
// PrimeVolumeMode. Serialized string keys must stay stable; they round-trip through .3mf.
static const t_config_enum_values s_keys_map_PrimeVolumeMode = {
{ "Default", pvmDefault },
{ "Saving", pvmSaving },
{ "Fast", pvmFast }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrimeVolumeMode)
//BBS
std::string get_extruder_variant_string(ExtruderType extruder_type, NozzleVolumeType nozzle_volume_type)
@@ -650,6 +670,60 @@ std::vector<std::string> save_extruder_ams_count_to_string(const std::vector<std
return extruder_ams_count_str;
}
// Parses the extruder_nozzle_stats option: one "|"-separated token list per
// extruder, each token
// "<volume_type_name>#<count>". Empty string => no per-type stats for that extruder.
std::vector<std::map<NozzleVolumeType, int>> get_extruder_nozzle_stats(const std::vector<std::string> &strs)
{
std::vector<std::map<NozzleVolumeType, int>> extruder_nozzle_counts;
for (const std::string &str : strs) {
std::map<NozzleVolumeType, int> nozzle_count_map;
if (str.empty()) {
extruder_nozzle_counts.emplace_back(nozzle_count_map);
continue;
}
std::vector<std::string> nozzle_infos;
boost::algorithm::split(nozzle_infos, str, boost::algorithm::is_any_of("|"));
for (auto &nozzle_info : nozzle_infos) {
std::vector<std::string> attr;
boost::algorithm::split(attr, nozzle_info, boost::algorithm::is_any_of("#"));
// Guard against malformed device/project data: skip tokens without a "<type>#<count>"
// shape or with an unknown volume-type key instead of throwing (.at) or reading attr[1]
// out of bounds. Corrupt tokens are dropped and logged; the rest still parse.
if (attr.size() < 2) {
BOOST_LOG_TRIVIAL(warning) << "get_extruder_nozzle_stats: skipping malformed token '" << nozzle_info << "'";
continue;
}
auto it = s_keys_map_NozzleVolumeType.find(attr[0]);
if (it == s_keys_map_NozzleVolumeType.end()) {
BOOST_LOG_TRIVIAL(warning) << "get_extruder_nozzle_stats: skipping unknown nozzle volume type '" << attr[0] << "'";
continue;
}
NozzleVolumeType volume_type = NozzleVolumeType(it->second);
int nozzle_count = std::atoi(attr[1].c_str());
nozzle_count_map[volume_type] = nozzle_count;
}
extruder_nozzle_counts.emplace_back(nozzle_count_map);
}
return extruder_nozzle_counts;
}
std::vector<std::string> save_extruder_nozzle_stats_to_string(const std::vector<std::map<NozzleVolumeType, int>> &extruder_nozzle_stats)
{
std::vector<std::string> extruder_nozzle_count_str;
for (size_t idx = 0; idx < extruder_nozzle_stats.size(); ++idx) {
std::ostringstream oss;
const auto &item = extruder_nozzle_stats[idx];
for (auto it = item.begin(); it != item.end(); ++it) {
oss << get_nozzle_volume_type_string(it->first) << "#" << it->second;
if (std::next(it) != item.end())
oss << "|";
}
extruder_nozzle_count_str.emplace_back(oss.str());
}
return extruder_nozzle_count_str;
}
static void assign_printer_technology_to_unknown(t_optiondef_map &options, PrinterTechnology printer_technology)
{
for (std::pair<const t_config_option_key, ConfigOptionDef> &kvp : options)
@@ -2548,6 +2622,25 @@ void PrintConfigDef::init_fff_params()
def->mode = comDevelop;
def->set_default_value(new ConfigOptionInts{1});
// Multi-nozzle: per-filament map to the config slot identified by (extruder, nozzle_volume_type).
// Forward-compat-only registration with no consumer yet.
// Orca: resolves per-variant slots via get_index_for_extruder at PrintApply time rather than a
// read-time filament-config-index lookup, so nothing in src/ reads this key. Kept registered so an
// H2C project/config that carries it loads without an unknown-option substitution warning.
// Internal use only, no translation.
def = this->add("filament_map_2", coInts);
def->label = "Filament map plus for multi nozzle";
def->tooltip = "Filament map to the index identified by extruder and nozzle_volume_type";
def->mode = comDevelop;
def->set_default_value(new ConfigOptionInts{1});
// Per-filament nozzle-volume-type override (multi-volume/Hybrid). Registered so the value
// round-trips through .3mf plate metadata (filament_volume_maps); load/save-only for now and
// inert for existing printers (unread by slicing until the multi-volume pipeline lands).
def = this->add("filament_volume_map", coInts);
def->mode = comDevelop;
def->set_default_value(new ConfigOptionInts{(int)(NozzleVolumeType::nvtStandard)});
def = this->add("physical_extruder_map",coInts);
// internal use only, don't need translation
def->label = "Map the logical extruder to physical extruder";
@@ -2563,14 +2656,22 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("Auto For Flush");
def->enum_values.push_back("Auto For Match");
def->enum_values.push_back("Manual");
def->enum_values.push_back("Nozzle Manual");
def->enum_values.push_back("Default");
def->enum_labels.push_back(L("Auto For Flush"));
def->enum_labels.push_back(L("Auto For Match"));
def->enum_labels.push_back(L("Manual"));
def->enum_labels.push_back(L("Nozzle Manual"));
def->enum_labels.push_back(L("Default"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<FilamentMapMode>(fmmAutoForFlush));
// Fully-manual mode (fmmNozzleManual): per-filament -> physical-nozzle assignment.
// Inert until the nozzle-assignment engine consumes it. Internal use only, no translation.
def = this->add("filament_nozzle_map", coInts);
def->mode = comDevelop;
def->set_default_value(new ConfigOptionInts{1});
def = this->add("filament_flush_temp", coInts);
def->label = L("Flush temperature");
def->tooltip = L("Temperature when flushing filament. 0 indicates the upper bound of the recommended nozzle temperature range.");
@@ -2581,6 +2682,19 @@ void PrintConfigDef::init_fff_params()
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
def->set_default_value(new ConfigOptionIntsNullable{0});
// Fast-purge flush temperature: used only when prime_volume_mode==Fast.
// Default 0 (fall back to the recommended-range upper bound, identical to filament_flush_temp),
// so the key is inert for the shipping fleet. Kept out of the g-code config block (banned_keys).
def = this->add("filament_flush_temp_fast", coInts);
def->label = L("Flush temperature");
def->tooltip = L("Flush temperature used in fast purge mode.");
def->mode = comAdvanced;
def->nullable = true;
def->min = 0;
def->max = max_temp;
def->sidetext = L(u8"" /* °C */); // degrees Celsius, CIS languages need translation
def->set_default_value(new ConfigOptionIntsNullable{0});
def = this->add("filament_flush_volumetric_speed", coFloats);
def->label = L("Flush volumetric speed");
def->tooltip = L("Volumetric speed when flushing filament. 0 indicates the max volumetric speed.");
@@ -2954,6 +3068,18 @@ void PrintConfigDef::init_fff_params()
def->mode = comDevelop;
def->set_default_value(new ConfigOptionInts{3});
// A single 32-bit int encodes the compatibility level of a filament across all extruders (up to 10).
// Every 3 bits represent one extruder: bits [3*i, 3*i+2] -> extruder i.
// Compatibility levels: 0 = printable, 1 = error, 2 = critical warning, 3 = warning (4-7 reserved).
// Default 0 (printable) keeps the key inert for filaments/printers that do not declare it.
def = this->add("filament_extruder_compatibility", coInts);
def->label = L("Filament-extruder compatibility");
def->tooltip = L("A single 32-bit int encoding the compatibility level of a filament across all extruders (up to 10). "
"Every 3 bits represent one extruder (bits [3*i, 3*i+2] for extruder i). "
"0: printable, 1: error, 2: critical warning, 3: warning, 4-7: reserved.");
def->mode = comDevelop;
def->set_default_value(new ConfigOptionInts{0});
// BBS
def = this->add("temperature_vitrification", coInts);
def->label = L("Softening temperature");
@@ -3857,6 +3983,12 @@ void PrintConfigDef::init_fff_params()
def->mode=comDevelop;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("cooling_filter_enabled", coBool);
def->label = L("Use cooling filter");
def->tooltip = L("Enable this if printer support cooling filter");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("gcode_flavor", coEnum);
def->label = L("G-code flavor");
def->tooltip = L("What kind of G-code the printer is compatible with.");
@@ -4659,6 +4791,39 @@ void PrintConfigDef::init_fff_params()
def->mode = comDevelop;
def->set_default_value(new ConfigOptionFloats{ 0., 0. });
// Bedslinger mass/force limits.
// Consumed by GCode::mass_load_limited_machine_acceleration (curr_y_acceleration_limit)
// and the printed-mass check. Default 0 keeps them inactive for existing printers.
def = this->add("machine_max_force_Y", coFloat);
def->full_label = L("Maximum force of the Y axis");
def->category = L("Machine limits");
def->readonly = false;
def->tooltip = L("The allowed maximum output force of Y axis");
def->sidetext = L("N");
def->min = 0;
def->mode = comDevelop;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("machine_bed_mass_Y", coFloat);
def->full_label = L("Bed mass of the Y axis");
def->category = L("Machine limits");
def->readonly = false;
def->tooltip = L("The machine bed mass load of Y axis");
def->sidetext = L("g");
def->min = 0;
def->mode = comDevelop;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("machine_max_printed_mass", coFloat);
def->full_label = L("The allowed max printed mass");
def->category = L("Machine limits");
def->readonly = false;
def->tooltip = L("The allowed max printed mass on a plate");
def->sidetext = L("g");
def->min = 0;
def->mode = comDevelop;
def->set_default_value(new ConfigOptionFloat(0));
// M204 P... [mm/sec^2]
def = this->add("machine_max_acceleration_extruding", coFloats);
def->full_label = L("Maximum acceleration for extruding");
@@ -5404,10 +5569,15 @@ void PrintConfigDef::init_fff_params()
def->label = "Nozzle Volume Type";
def->tooltip = "Nozzle volume type for extruders.";
def->enum_keys_map = &ConfigOptionEnum<NozzleVolumeType>::get_enum_values();
// Order must match the NozzleVolumeType enum values (Standard=0, High Flow=1, Hybrid=2, TPU High Flow=3).
def->enum_values.push_back(L("Standard"));
def->enum_values.push_back(L("High Flow"));
def->enum_values.push_back(L("Hybrid"));
def->enum_values.push_back(L("TPU High Flow"));
def->enum_labels.push_back(L("Standard"));
def->enum_labels.push_back(L("High Flow"));
def->enum_labels.push_back(L("Hybrid"));
def->enum_labels.push_back(L("TPU High Flow"));
def->mode = comSimple;
def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard });
@@ -5418,8 +5588,12 @@ void PrintConfigDef::init_fff_params()
def->enum_keys_map = &ConfigOptionEnum<NozzleVolumeType>::get_enum_values();
def->enum_values.push_back(L("Standard"));
def->enum_values.push_back(L("High Flow"));
def->enum_values.push_back(L("Hybrid"));
def->enum_values.push_back(L("TPU High Flow"));
def->enum_labels.push_back(L("Standard"));
def->enum_labels.push_back(L("High Flow"));
def->enum_labels.push_back(L("Hybrid"));
def->enum_labels.push_back(L("TPU High Flow"));
def->mode = comDevelop;
def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard });
@@ -5436,6 +5610,43 @@ void PrintConfigDef::init_fff_params()
def->tooltip = "AMS counts per extruder.";
def->set_default_value(new ConfigOptionStrings { });
// Multi-nozzle: per-extruder physical nozzle inventory by volume type,
// "<volume_type>#<count>" tokens joined by "|". Internal use only, no translation.
def = this->add("extruder_nozzle_stats", coStrings);
def->label = "Extruder nozzle stats";
def->tooltip = "Physical nozzle counts per extruder, keyed by nozzle volume type.";
def->set_default_value(new ConfigOptionStrings { });
// Multi-nozzle: number of physical nozzles per extruder. Forward-compat-only registration
// with no slicing consumer: the nozzle-assignment engine derives its per-extruder physical nozzle
// inventory from the richer `extruder_nozzle_stats` key, not from this scalar count, so nothing in
// src/ reads it. Kept registered so an H2C project/config that carries the key loads without an
// unknown-option substitution warning. Default {1} == today's single-nozzle-per-extruder assumption,
// so absent/default is a no-op for every shipping printer. Internal use only, no translation.
def = this->add("extruder_nozzle_count", coInts);
def->label = "extruder nozzle count";
def->tooltip = "extruder nozzle count";
def->mode = comDevelop;
def->set_default_value(new ConfigOptionInts{1});
// Per-nozzle volume type. Forward-compat-only registration with no slicing consumer — nothing in
// src/ reads it; the engine resolves per-nozzle volume types from `extruder_nozzle_stats` tokens
// instead. Kept registered so a project/config carrying it loads without an unknown-option
// substitution warning. Registers Standard/High Flow/TPU High Flow only (no Hybrid).
// Internal use only, no translation.
def = this->add("extruder_nozzle_volume_type", coEnums);
def->label = "Extruder nozzle volume type";
def->tooltip = "Nozzle volume type per physical nozzle of an extruder.";
def->enum_keys_map = &ConfigOptionEnum<NozzleVolumeType>::get_enum_values();
def->enum_values.push_back("Standard");
def->enum_values.push_back("High Flow");
def->enum_values.push_back("TPU High Flow");
def->enum_labels.push_back("Standard");
def->enum_labels.push_back("High Flow");
def->enum_labels.push_back("TPU High Flow");
def->mode = comDevelop;
def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard });
def = this->add("enable_filament_dynamic_map", coBool);
def->label = L("Enable filament dynamic map");
def->tooltip = L("Enable dynamic filament mapping during print.");
@@ -5534,6 +5745,19 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0. });
// Per-(extruder,variant) deretraction speed at an extruder change. This value
// ships in multi-extruder machine profiles but has no slicer consumer — it is a device/firmware-facing
// profile key. Registered here (ConfigDef-only, no FullPrintConfig static member) so X2D/P2S profiles
// that already carry it — and the H2D/A2L leaves — validate as a known key; unread by the slicer.
// nullable so absent leaves stay nil (excluded from the g-code config block).
def = this->add("deretract_speed_extruder_change", coFloats);
def->label = L("Deretraction speed (extruder change)");
def->tooltip = L("Speed for reloading filament into the nozzle when switching extruder.");
def->sidetext = L("mm/s"); // millimeters per second, CIS languages need translation
def->mode = comDevelop;
def->nullable = true;
def->set_default_value(new ConfigOptionFloatsNullable { 0. });
def = this->add("use_firmware_retraction", coBool);
def->label = L("Use firmware retraction");
def->tooltip = L("This experimental setting uses G10 and G11 commands to have the firmware "
@@ -5952,6 +6176,20 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple;
def->set_default_value(new ConfigOptionEnum<TimelapseType>(tlTraditional));
// Farthest-point timelapse. Corexy-only refinement layered on top of the existing
// timelapse_type: when enabled the traditional-mode snapshot is
// taken at the extrusion point farthest from the camera (0,0) instead of traveling to a safe/chute
// position. Gated in GCode::process_layer by timelapse_type==tlTraditional && printer_structure!=psI3,
// so it is inert for i3 (A1/A2L) printers and whenever the toggle is off (default false → every
// existing printer's g-code is byte-identical). Only H2C/H2D machine profiles set it =1.
def = this->add("farthest_point_timelapse", coBool);
def->label = L("Farthest point timelapse");
def->tooltip = L("When enabled, the timelapse snapshot is taken at the farthest point from camera "
"instead of traveling to the wipe tower or excess chute. "
"Only effective in traditional timelapse mode on non-I3 printers.");
def->mode = comSimple;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("standby_temperature_delta", coInt);
def->label = L("Temperature variation");
// TRN PrintSettings : "Ooze prevention" > "Temperature variation"
@@ -6875,6 +7113,14 @@ void PrintConfigDef::init_fff_params()
//def->sidetext = "";
def->set_default_value(new ConfigOptionFloats{0.3});
// Fast-purge mode: the flush multiplier used when prime_volume_mode==Fast.
// Only consumed on the pvmFast branch (default prime_volume_mode==Default reads flush_multiplier),
// so this key is inert for the shipping fleet. Kept out of the g-code config block (banned_keys).
def = this->add("flush_multiplier_fast", coFloats);
def->label = L("Flush multiplier (Fast mode)");
def->tooltip = L("The flush multiplier used in fast purge mode.");
def->set_default_value(new ConfigOptionFloats{1.2});
// BBS
def = this->add("prime_volume", coFloat);
def->label = L("Prime volume");
@@ -6884,6 +7130,22 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple;
def->set_default_value(new ConfigOptionFloat(45.));
// Dual-extruder purge control: Default reproduces current behaviour
// (per-extruder flush_multiplier + filament_prime_volume); Saving reduces the prime volume,
// Fast selects flush_multiplier_fast + filament_flush_temp_fast. Default pvmDefault = inert.
def = this->add("prime_volume_mode", coEnum);
def->label = L("Prime volume mode");
def->tooltip = L("Selects how the wipe-tower prime and flush volumes are computed on multi-extruder printers.");
def->enum_keys_map = &ConfigOptionEnum<PrimeVolumeMode>::get_enum_values();
def->enum_values.push_back("Default");
def->enum_values.push_back("Saving");
def->enum_values.push_back("Fast");
def->enum_labels.push_back(L("Default"));
def->enum_labels.push_back(L("Saving"));
def->enum_labels.push_back(L("Fast"));
def->mode = comDevelop;
def->set_default_value(new ConfigOptionEnum<PrimeVolumeMode>(pvmDefault));
def = this->add("wipe_tower_x", coFloats);
//def->label = L("Position X");
//def->tooltip = L("X coordinate of the left front corner of a wipe tower.");
@@ -7340,6 +7602,162 @@ void PrintConfigDef::init_fff_params()
}
}
// ---- Multi-nozzle + pre-heat + nozzle-change (nc) config keys ----
// These back 6-nozzle cluster grouping, the pre-heat/pre-cool time model and wipe-tower
// nozzle-change handling. Defaults are no-ops for existing single-nozzle printers (all new
// readers gate on extruder_max_nozzle_count > 1).
def = this->add("machine_hotend_change_time", coFloat);
def->label = L("Hotend change time");
def->tooltip = L("Time to change hotend.");
def->sidetext = L("s");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.0));
// Printer-owned toggle selecting the time-aware grouping objective (flush + change/print
// time) over the flush-only objective. Default false == today's flush-only grouping, so
// absent/default is inert. Consumed by the grouping solver (SpeedInfo).
// Orca: pinned to comDevelop (rather than the default comSimple) to match the sibling multi-nozzle
// dev keys and keep it out of user selectors.
def = this->add("group_algo_with_time", coBool);
def->mode = comDevelop;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("machine_prepare_compensation_time", coFloat);
def->mode = comDevelop;
def->set_default_value(new ConfigOptionFloat(260));
def = this->add("enable_pre_heating", coBool);
def->set_default_value(new ConfigOptionBool(false));
// Pre-heat injector context key. handle_hotend_as_extruder is read defensively by the injector;
// registered here (comDevelop, default off) as a known inert key.
def = this->add("handle_hotend_as_extruder", coBool);
def->mode = comDevelop;
def->set_default_value(new ConfigOptionBool(false));
// filament_max_temperature_drop_when_ec: inert (default {0}, passed into the injector context but
// never consumed). Registered inert-only (comDevelop); not wired into any estimator read, and not
// added to any per-variant expansion list.
def = this->add("filament_max_temperature_drop_when_ec", coFloats);
def->mode = comDevelop;
def->set_default_value(new ConfigOptionFloats{0});
def = this->add("hotend_cooling_rate", coFloats);
def->nullable = true;
def->set_default_value(new ConfigOptionFloatsNullable{2});
def = this->add("hotend_heating_rate", coFloats);
def->nullable = true;
def->set_default_value(new ConfigOptionFloatsNullable{2});
def = this->add("filament_change_length_nc", coFloats);
def->label = L("Hotend change");
def->tooltip = L("When changing the hotend, it is recommended to extrude a certain length of filament from the original nozzle. This helps minimize nozzle oozing.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats{10});
def = this->add("filament_ramming_travel_time", coFloats);
def->label = L("Extruder change");
def->tooltip = L("To prevent oozing, the nozzle will perform a reverse travel movement for a certain period after "
"the ramming is complete. The setting define the travel time.");
def->mode = comAdvanced;
def->sidetext = "s";
def->min = 0;
def->nullable = true;
def->set_default_value(new ConfigOptionFloatsNullable{0});
def = this->add("filament_pre_cooling_temperature", coInts);
def->label = L("Extruder change");
def->tooltip = L("To prevent oozing, the nozzle temperature will be cooled during ramming. Therefore, the ramming time must be greater than the cooldown time. 0 means disabled.");
def->mode = comAdvanced;
def->sidetext = "°C";
def->min = 0;
def->nullable = true;
def->set_default_value(new ConfigOptionIntsNullable{0});
def = this->add("filament_ramming_volumetric_speed", coFloats);
def->label = L("Extruder change");
def->tooltip = L("The maximum volumetric speed for ramming before extruder change, where -1 means using the maximum volumetric speed.");
def->sidetext = L("mm³/s");
def->min = -1;
def->max = 200;
def->mode = comAdvanced;
def->nullable = true;
def->set_default_value(new ConfigOptionFloatsNullable{-1});
def = this->add("filament_ramming_travel_time_nc", coFloats);
def->label = L("Hotend change");
def->tooltip = L("To prevent oozing, the nozzle will perform a reverse travel movement for a certain period after "
"the ramming is complete. The setting define the travel time.");
def->mode = comAdvanced;
def->sidetext = "s";
def->min = 0;
def->nullable = true;
def->set_default_value(new ConfigOptionFloatsNullable{0});
def = this->add("filament_pre_cooling_temperature_nc", coInts);
def->label = L("Hotend change");
def->tooltip = L(
"To prevent oozing, the nozzle temperature will be cooled during ramming. Note: only a cooldown command and fan activation are triggered, reaching the target temperature is not guaranteed. 0 means disabled.");
def->mode = comAdvanced;
def->sidetext = "°C";
def->min = 0;
def->nullable = true;
def->set_default_value(new ConfigOptionIntsNullable{0});
def = this->add("filament_ramming_volumetric_speed_nc", coFloats);
def->label = L("Hotend change");
def->tooltip = L("The maximum volumetric speed for ramming before a hotend change, where -1 means using the maximum volumetric speed.");
def->sidetext = L("mm³/s");
def->min = -1;
def->max = 200;
def->mode = comAdvanced;
def->nullable = true;
def->set_default_value(new ConfigOptionFloatsNullable{-1});
def = this->add("filament_retract_length_nc", coFloats);
def->label = L("length when change hotend");
def->tooltip = L("When this retraction value is modified, it will be used as the amount of filament retracted "
"inside the hotend before changing hotends.");
def->sidetext = L("mm");
def->mode = comDevelop;
def->nullable = true;
def->min = 0;
def->max = 18;
def->set_default_value(new ConfigOptionFloatsNullable { 10. });
def = this->add("extruder_max_nozzle_count", coInts);
def->mode = comDevelop;
def->nullable = true;
def->set_default_value(new ConfigOptionIntsNullable{ 1 });
// Printer flag gating the fast-purge mode selector in the Multi-Filament
// UI. comDevelop, default false; no shipping profile sets it, so the UI stays hidden.
def = this->add("support_fast_purge_mode", coBool);
def->label = L("Support fast purge mode");
def->tooltip = L("Whether this printer supports fast purge mode with optimized temperature and multiplier.");
def->mode = comDevelop;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("filament_prime_volume_nc", coFloats);
def->label = L("Hotend change");
def->tooltip = L("The volume of material required to prime the extruder for a hotend change on the tower.");
def->sidetext = L("mm³");
def->min = 1.0;
def->mode = comSimple;
def->set_default_value(new ConfigOptionFloats{60.});
def = this->add("filament_preheat_temperature_delta", coFloats);
def->label = L("Preheat temperature delta");
def->tooltip = L("Temperature delta applied during pre-heating before tool change.");
def->sidetext = "°C";
def->mode = comDevelop;
def->nullable = true;
def->set_default_value(new ConfigOptionFloatsNullable{0});
def = this->add("detect_narrow_internal_solid_infill", coBool);
def->label = L("Detect narrow internal solid infills");
def->category = L("Strength");
@@ -7389,13 +7807,14 @@ void PrintConfigDef::init_filament_option_keys()
m_filament_option_keys = {
"filament_diameter", "min_layer_height", "max_layer_height","volumetric_speed_coefficients",
"retraction_length", "z_hop", "z_hop_types", "retract_lift_above", "retract_lift_below", "retract_lift_enforce", "retraction_speed", "deretraction_speed",
"retract_before_wipe", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance",
"retract_before_wipe", "filament_retract_length_nc", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance",
"retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "filament_colour",
"default_filament_profile","retraction_distances_when_cut","long_retractions_when_cut"/*,"filament_seam_gap"*/
};
m_filament_retract_keys = {
"deretraction_speed",
"filament_retract_length_nc",
"long_retractions_when_cut",
"retract_before_wipe",
"retract_lift_above",
@@ -8414,6 +8833,15 @@ std::set<std::string> print_options_with_variant = {
std::set<std::string> filament_options_with_variant = {
"filament_flow_ratio",
"filament_max_volumetric_speed",
// Per-variant ramming / pre-cooling / nozzle-change filament overrides
"filament_ramming_volumetric_speed",
"filament_pre_cooling_temperature",
"filament_ramming_travel_time",
"filament_ramming_volumetric_speed_nc",
"filament_pre_cooling_temperature_nc",
"filament_ramming_travel_time_nc",
"filament_retract_length_nc",
"filament_preheat_temperature_delta",
//"filament_extruder_id",
"filament_extruder_variant",
"filament_retraction_length",
@@ -8461,7 +8889,8 @@ std::set<std::string> printer_extruder_options = {
"extruder_printable_area",
"extruder_printable_height",
"min_layer_height",
"max_layer_height"
"max_layer_height",
"extruder_max_nozzle_count" // Per-extruder max (sub-)nozzle count
};
std::set<std::string> printer_options_with_variant_1 = {
@@ -8489,6 +8918,9 @@ std::set<std::string> printer_options_with_variant_1 = {
"nozzle_type",
"printer_extruder_id",
"printer_extruder_variant",
// Per-variant hotend heat-up / cool-down rates (pre-heat time model)
"hotend_cooling_rate",
"hotend_heating_rate",
"nozzle_flush_dataset"
};