Merge upstream main: multi-variant printers, X-Ray view, preview and startup work

The two conflicts are both places where upstream landed on top of IDEX/IQEX
code. In GCode.cpp the relocated file header block meets the IMEX placeholder
block, and the placeholders are set first: file_start_gcode is processed through
the placeholder parser now, which throws on a name it does not know, so a script
naming {imex_mode} would abort the export if the header ran first. In
test_multifilament.cpp both sides appended a case at the end of the file.
This commit is contained in:
Clifford Garwood
2026-09-28 17:12:53 -04:00
5610 changed files with 235531 additions and 279338 deletions
+79 -9
View File
@@ -627,7 +627,9 @@ static const t_config_enum_values s_keys_map_NozzleVolumeType = {
{ "Standard", nvtStandard },
{ "High Flow", nvtHighFlow },
{ "TPU High Flow", nvtTPUHighFlow },
{ "Hybrid", nvtHybrid }
{ "Hybrid", nvtHybrid },
{ "E3D High Flow", nvtE3DHighFlow },
{ "Extra High Flow", nvtExtraHighFlow }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(NozzleVolumeType)
@@ -695,9 +697,34 @@ int get_config_index_base(NozzleVolumeType volume_type, ExtruderType extruder_ty
for (int index = 0; index < int(variant_list.size()); ++index) {
if (extruder_variant == variant_list[index] && variant_ids_1based[index] == variant_id_1based) { return index; }
}
// Without this variant, use the id's own first variant (usually Standard), not variant index 0,
// which belongs to the first filament or extruder.
for (int index = 0; index < int(variant_list.size()); ++index) {
if (variant_ids_1based[index] == variant_id_1based) { return index; }
}
return 0;
}
std::set<NozzleVolumeType> get_extruder_supported_nozzle_volume_types(const DynamicPrintConfig &printer_config, int extruder_id)
{
std::set<NozzleVolumeType> supported_types;
auto *variant_list = printer_config.option<ConfigOptionStrings>("extruder_variant_list");
auto *extruder_types = printer_config.option<ConfigOptionEnumsGeneric>("extruder_type");
if (!variant_list || !extruder_types || extruder_id < 0 ||
extruder_id >= (int) variant_list->values.size() || extruder_id >= (int) extruder_types->values.size())
return supported_types;
const ExtruderType extruder_type = ExtruderType(extruder_types->values[extruder_id]);
for (NozzleVolumeType volume_type : get_valid_nozzle_volume_type()) {
// An unsupported extruder type yields an empty name, which would match any list.
const std::string variant = get_extruder_variant_string(extruder_type, volume_type);
if (!variant.empty() && variant_list->values[extruder_id].find(variant) != std::string::npos)
supported_types.insert(volume_type);
}
return supported_types;
}
std::string get_nozzle_volume_type_string(NozzleVolumeType nozzle_volume_type)
{
if (nozzle_volume_type > nvtMaxNozzleVolumeType) {
@@ -1541,7 +1568,7 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1));
def = this->add("top_solid_infill_flow_ratio", coFloat);
def = this->add("top_solid_infill_flow_ratio", coFloats);
def->label = L("Top surface flow ratio");
def->category = L("Advanced");
def->tooltip = L("This factor affects the amount of material for top solid infill. "
@@ -1550,7 +1577,8 @@ void PrintConfigDef::init_fff_params()
def->min = 0;
def->max = 2;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1));
def->nullable = true;
def->set_default_value(new ConfigOptionFloatsNullable{1});
def = this->add("bottom_solid_infill_flow_ratio", coFloat);
def->label = L("Bottom surface flow ratio");
@@ -5941,15 +5969,20 @@ 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).
// Listed in display order. A position is not the enum value (E3D High Flow is 5, after the reserved 4),
// so map a position to its NozzleVolumeType through enum_keys_map.
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_values.push_back(L("E3D High Flow"));
def->enum_values.push_back(L("Extra 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->enum_labels.push_back(L("E3D High Flow"));
def->enum_labels.push_back(L("Extra High Flow"));
def->mode = comSimple;
def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard });
@@ -5962,10 +5995,14 @@ void PrintConfigDef::init_fff_params()
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_values.push_back(L("E3D High Flow"));
def->enum_values.push_back(L("Extra 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->enum_labels.push_back(L("E3D High Flow"));
def->enum_labels.push_back(L("Extra High Flow"));
def->mode = comDevelop;
def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard });
@@ -6004,7 +6041,7 @@ void PrintConfigDef::init_fff_params()
// 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).
// substitution warning. Registers the physical types only (no Hybrid).
// Internal use only, no translation.
def = this->add("extruder_nozzle_volume_type", coEnums);
def->label = "Extruder nozzle volume type";
@@ -6013,9 +6050,13 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("Standard");
def->enum_values.push_back("High Flow");
def->enum_values.push_back("TPU High Flow");
def->enum_values.push_back("E3D High Flow");
def->enum_values.push_back("Extra High Flow");
def->enum_labels.push_back("Standard");
def->enum_labels.push_back("High Flow");
def->enum_labels.push_back("TPU High Flow");
def->enum_labels.push_back("E3D High Flow");
def->enum_labels.push_back("Extra High Flow");
def->mode = comDevelop;
def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard });
@@ -6856,6 +6897,20 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("wipe_tower_sparse_layers_combination", coBool);
def->label = L("Combine sparse layers");
def->tooltip = L("If enabled, consecutive layers on which the prime tower has no filament change are printed as a single "
"thicker tower layer instead of one thin layer each, the same way infill combination merges sparse infill. "
"The merged layer is printed at the top of the run, at the height of everything it covers.\n\n"
"Only whole layers are merged, and never past the maximum layer height of the nozzle printing the tower "
"(three quarters of the nozzle diameter when that is left at 0). Two or more layers therefore have to fit "
"under that limit before anything changes at all: at a 0.2 mm layer height under a 0.3 mm maximum nothing "
"is merged, while at 0.1 mm three layers become one.\n\n"
"Unlike \"No sparse layers\" the tower keeps following the model, so the toolhead never has to reach down to it. "
"Has no effect with \"No sparse layers\", smooth timelapse or clumping detection, which need a tower on every layer.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("single_extruder_multi_material_priming", coBool);
def->label = L("Prime all printing extruders");
def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print.");
@@ -9279,6 +9334,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
value = "tree(auto)";
} else if (opt_key == "support_base_pattern" && value == "none") {
value = "hollow";
} else if (opt_key == "tree_support_wall_count" && value == "-1") {
value = "0";
} else if (opt_key == "different_settings_to_system") {
std::string copy_value = value;
copy_value.erase(std::remove(copy_value.begin(), copy_value.end(), '\"'), copy_value.end()); // remove '"' in string
@@ -9565,7 +9622,8 @@ std::set<std::string> print_options_with_variant = {
"initial_layer_travel_jerk",
"default_junction_deviation",
"print_extruder_id", //coInts
"print_extruder_variant" //coStrings
"print_extruder_variant", //coStrings
"top_solid_infill_flow_ratio"
};
std::set<std::string> filament_options_with_variant = {
@@ -10222,6 +10280,13 @@ bool DynamicPrintConfig::is_using_different_extruders()
return ret;
}
bool DynamicPrintConfig::has_multi_variant_filament() const
{
auto variants = dynamic_cast<const ConfigOptionStrings*>(this->option("filament_extruder_variant"));
auto diameters = dynamic_cast<const ConfigOptionFloats*>(this->option("filament_diameter"));
return variants && diameters && variants->size() > diameters->size();
}
bool DynamicPrintConfig::support_different_extruders(int& extruder_count) const
{
std::set<std::string> variant_set;
@@ -11222,6 +11287,11 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
return;
}
std::vector<int> filament_maps = opt_filament_map->values;
auto opt_ids = id_name.empty()? nullptr: dynamic_cast<const ConfigOptionInts*>(this->option(id_name));
// Orca: a map shorter than the filament count must not drop the filaments past its end;
// they take the first extruder.
if (opt_ids && !opt_ids->values.empty())
filament_maps.resize(std::max<size_t>(filament_maps.size(), *std::max_element(opt_ids->values.begin(), opt_ids->values.end())), 1);
size_t filament_count = filament_maps.size();
//apply process settings
auto opt_extruder_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(printer_config.option("extruder_type"));
@@ -11240,7 +11310,6 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
// indexed out of bounds.
if (opt_filament_volume_maps && opt_filament_volume_maps->values.size() == filament_count)
filament_volume_maps = opt_filament_volume_maps->values;
auto opt_ids = id_name.empty()? nullptr: dynamic_cast<const ConfigOptionInts*>(this->option(id_name));
std::vector<int> variant_index;
variant_index.resize(filament_count, -1);
@@ -11257,9 +11326,10 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
//variant index
variant_index[f_index] = get_index_for_extruder(f_index+1, id_name, extruder_type, nozzle_volume_type, variant_name);
if (variant_index[f_index] < 0) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: could not found extruder_type %2%, nozzle_volume_type %3%, filament_index %4%, extruder index %5%")
// Orca: a filament need not define every extruder variant (a Direct Drive filament on a
// Bowden printer), so this is not an invalid state: the filament's first variant is used.
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: could not found extruder_type %2%, nozzle_volume_type %3%, filament_index %4%, extruder index %5%")
%__LINE__ %s_keys_names_ExtruderType[extruder_type] % s_keys_names_NozzleVolumeType[nozzle_volume_type] % (f_index+1) %filament_maps[f_index];
assert(false);
//for some updates happens in a invalid state(caused by popup window)
//we need to avoid crash
variant_index[f_index] = 0;