From 7d44b60ae4042b4de8c3844eb9dd2671c2d2d873 Mon Sep 17 00:00:00 2001 From: LixNix <105106115+LixNix@users.noreply.github.com> Date: Thu, 8 Oct 2026 14:28:32 +0200 Subject: [PATCH] H2D X2D Multi Nozzle Size Printing Support (#14125) --- src/libslic3r/Fill/Fill.cpp | 4 +-- src/libslic3r/Flow.cpp | 8 ++--- src/libslic3r/LayerRegion.cpp | 2 +- src/libslic3r/Print.cpp | 17 ++++----- src/libslic3r/PrintConfig.cpp | 9 +++++ src/libslic3r/PrintConfig.hpp | 2 ++ src/libslic3r/PrintObject.cpp | 4 +-- src/libslic3r/PrintRegion.cpp | 2 +- src/libslic3r/Support/SupportMaterial.cpp | 2 +- src/libslic3r/Support/TreeSupport.cpp | 2 +- src/slic3r/GUI/AMSMaterialsSetting.cpp | 10 ++++-- src/slic3r/GUI/Plater.cpp | 42 +++++++++++------------ src/slic3r/GUI/Tab.cpp | 3 +- 13 files changed, 63 insertions(+), 44 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index e95fe2741d..b1d8ff2b13 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -1073,7 +1073,7 @@ std::vector group_fills(const Layer &layer, LockRegionParam &lock_p //get locked region param if (params.pattern == ipLockedZag){ const PrintObject *object = layerm.layer()->object(); - auto nozzle_diameter = float(object->print()->config().nozzle_diameter.get_at(layerm.region().extruder(extrusion_role) - 1)); + auto nozzle_diameter = float(nozzle_diameter_for_filament(object->print()->config(), layerm.region().extruder(extrusion_role), object->print()->is_BBL_printer())); Flow skin_flow = params.bridge ? params.flow : Flow::new_from_config_width(extrusion_role, region_config.skin_infill_line_width, nozzle_diameter, float((surface.thickness == -1) ? layer.height : surface.thickness)); //add skin flow append_flow_param(lock_param.skin_flow_params, skin_flow, surface.expolygon); @@ -1788,7 +1788,7 @@ void Layer::make_ironing() // Create the ironing extrusions for regions object()->print()->config().nozzle_diameter.get_at(ironing_params.extruder - 1); + double nozzle_dmr = nozzle_diameter_for_filament(this->object()->print()->config(), ironing_params.extruder, this->object()->print()->is_BBL_printer()); if (ironing_params.just_infill) { //TODO just_infill is currently not used. // Just infill. diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 96cbb86949..7f07c63157 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -241,14 +241,14 @@ Flow support_material_flow(const PrintObject *object, float layer_height) // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (object->config().support_line_width.value > 0) ? object->config().support_line_width : object->config().line_width, // if object->config().support_filament == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(object->print()->config().nozzle_diameter.get_at(object->config().support_filament-1)), + float(nozzle_diameter_for_filament(object->print()->config(), object->config().support_filament, object->print()->is_BBL_printer())), (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value)); } //BBS Flow support_transition_flow(const PrintObject* object) { //BBS: support transition of tree support is bridge flow - float dmr = float(object->print()->config().nozzle_diameter.get_at(object->config().support_filament - 1)); + float dmr = float(nozzle_diameter_for_filament(object->print()->config(), object->config().support_filament, object->print()->is_BBL_printer())); return Flow::bridging_flow(dmr, dmr); } @@ -260,7 +260,7 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig frSupportMaterial, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (width.value > 0) ? width : object->config().line_width, - float(print_config.nozzle_diameter.get_at(object->config().support_filament-1)), + float(nozzle_diameter_for_filament(print_config, object->config().support_filament, object->print()->is_BBL_printer())), (layer_height > 0.f) ? layer_height : float(print_config.initial_layer_print_height.value)); } @@ -271,7 +271,7 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (object->config().support_line_width > 0) ? object->config().support_line_width : object->config().line_width, // if object->config().support_interface_filament == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(object->print()->config().nozzle_diameter.get_at(object->config().support_interface_filament-1)), + float(nozzle_diameter_for_filament(object->print()->config(), object->config().support_interface_filament, object->print()->is_BBL_printer())), (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value)); } diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 340f9a590d..c20cebc641 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -62,7 +62,7 @@ Flow LayerRegion::bridging_flow(FlowRole role, bool thick_bridge) const const PrintObject &print_object = *this->layer()->object(); Flow bridge_flow; // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will fall back to zero'th element, so everything is all right. - auto nozzle_diameter = float(print_object.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1)); + auto nozzle_diameter = float(nozzle_diameter_for_filament(print_object.print()->config(), region.extruder(role), print_object.print()->is_BBL_printer())); const ConfigOptionFloatOrPercent& bridge_width_opt = region_config.bridge_line_width; const double bridge_width = bridge_width_opt.get_abs_value(nozzle_diameter); const bool has_bridge_width = bridge_width > 0.; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4e6ec96ee9..46de2715eb 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2229,7 +2229,7 @@ StringObjectException Print::validate(std::vector *warnin for (const PrintRegion ®ion : object->all_regions()) { const auto &bridge_width_opt = region.config().bridge_line_width; for (FlowRole bridge_role : { frPerimeter, frInfill, frSolidInfill, frTopSolidInfill }) { - const double nozzle_diameter = m_config.nozzle_diameter.get_at(region.extruder(bridge_role) - 1); + const double nozzle_diameter = nozzle_diameter_for_filament(m_config, region.extruder(bridge_role), this->is_BBL_printer()); const double bridge_width = bridge_width_opt.get_abs_value(nozzle_diameter); if (bridge_width <= 0.) continue; @@ -2606,7 +2606,7 @@ Flow Print::brim_flow() const frPerimeter, // Flow::new_from_config_width takes care of the percent to value substitution width, - (float)m_config.nozzle_diameter.get_at(m_print_regions.front()->config().outer_wall_filament_id-1), + (float)nozzle_diameter_for_filament(m_config, m_print_regions.front()->config().outer_wall_filament_id, this->is_BBL_printer()), (float)this->skirt_first_layer_height()); } @@ -2623,12 +2623,13 @@ Flow Print::skirt_flow() const extruders and take the one with, say, the smallest index; The same logic should be applied to the code that selects the extruder during G-code generation as well. */ - return Flow::new_from_config_width(frPerimeter, - // Flow::new_from_config_width takes care of the percent to value substitution - width, - (float) m_config.nozzle_diameter.get_at( - m_objects.empty() ? 0 : m_objects.front()->config().support_filament - 1), - (float) this->skirt_first_layer_height()); + return Flow::new_from_config_width( + frPerimeter, + // Flow::new_from_config_width takes care of the percent to value substitution + width, + // ORCA: resolve the actual nozzle the support filament is printed with (dual-nozzle printers). + (float)nozzle_diameter_for_filament(m_config, m_objects.empty() ? 0 : m_objects.front()->config().support_filament, this->is_BBL_printer()), + (float)this->skirt_first_layer_height()); } bool Print::has_support_material() const diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index fc534c9686..774bc28762 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -133,6 +133,15 @@ size_t get_extruder_index(const GCodeConfig& config, unsigned int filament_id) return 0; } +double nozzle_diameter_for_filament(const PrintConfig& config, int filament_id, bool is_bbl_printer) +{ + int extruder = filament_id; + if (is_bbl_printer && config.nozzle_diameter.size() > 1 && + filament_id >= 1 && static_cast(filament_id - 1) < config.filament_map.size()) + extruder = config.filament_map.get_at(filament_id - 1); + return config.nozzle_diameter.get_at(extruder - 1); +} + // Orca: input shaping values types by flavor std::vector get_shaper_type_values_for_flavor(GCodeFlavor flavor) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 0bad9eea86..a7affb50b4 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -2566,6 +2566,8 @@ static bool has_zero_flush_volume_for_used_filaments(const std::vector &fv_ma size_t get_extruder_index(const GCodeConfig& config, unsigned int filament_id); +double nozzle_diameter_for_filament(const PrintConfig& config, int filament_id, bool is_bbl_printer); + } // namespace Slic3r // Serialization through the Cereal library diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 4229ceca73..12b17fa2ed 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -4467,8 +4467,8 @@ void PrintObject::combine_infill() // Limit the number of combined layers to the maximum height allowed by this regions' nozzle. //FIXME limit the layer height to max_layer_height double nozzle_diameter = std::min( - this->print()->config().nozzle_diameter.get_at(region.config().sparse_infill_filament_id.value - 1), - this->print()->config().nozzle_diameter.get_at(region.config().internal_solid_filament_id.value - 1)); + nozzle_diameter_for_filament(this->print()->config(), region.config().sparse_infill_filament_id.value, this->print()->is_BBL_printer()), + nozzle_diameter_for_filament(this->print()->config(), region.config().internal_solid_filament_id.value, this->print()->is_BBL_printer())); //Orca: Limit combination of infill to up to infill_combination_max_layer_height const double infill_combination_max_layer_height = region.config().infill_combination_max_layer_height.get_abs_value(nozzle_diameter); diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp index 52018223ca..e1a9797f01 100644 --- a/src/libslic3r/PrintRegion.cpp +++ b/src/libslic3r/PrintRegion.cpp @@ -57,7 +57,7 @@ Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_he // Get the configured nozzle_diameter for the extruder associated to the flow role requested. // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right. - auto nozzle_diameter = float(print_config.nozzle_diameter.get_at(this->extruder(role) - 1)); + auto nozzle_diameter = float(nozzle_diameter_for_filament(print_config, this->extruder(role), object.print()->is_BBL_printer())); return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height)); } diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index 2f4665e9e2..cfd259501f 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -2180,7 +2180,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( // check if the sharp tails should be extended higher bool detect_first_sharp_tail_only = false; - const coordf_t extrusion_width = m_object_config->line_width.get_abs_value(object.print()->config().nozzle_diameter.get_at(object.config().support_interface_filament-1)); + const coordf_t extrusion_width = m_object_config->line_width.get_abs_value(nozzle_diameter_for_filament(object.print()->config(), object.config().support_interface_filament, object.print()->is_BBL_printer())); const coordf_t extrusion_width_scaled = scale_(extrusion_width); if (is_auto(m_object_config->support_type.value) && g_config_support_sharp_tails && !detect_first_sharp_tail_only) { for (size_t layer_nr = layer_id_start; layer_nr < num_layers; layer_nr++) { diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index a92b3196cc..ada9310dda 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1399,7 +1399,7 @@ void TreeSupport::generate_toolpaths() { const PrintObjectConfig &object_config = m_object->config(); coordf_t support_extrusion_width = m_support_params.support_extrusion_width; - coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at(object_config.support_filament - 1); + coordf_t nozzle_diameter = nozzle_diameter_for_filament(*m_print_config, object_config.support_filament, m_object->print()->is_BBL_printer()); coordf_t layer_height = object_config.layer_height.value; const size_t wall_count = object_config.tree_support_wall_count.value; diff --git a/src/slic3r/GUI/AMSMaterialsSetting.cpp b/src/slic3r/GUI/AMSMaterialsSetting.cpp index 83dd6a2d07..3859daf8e9 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.cpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.cpp @@ -1012,7 +1012,10 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi float machine_diameter = obj->GetExtderSystem()->GetNozzleDiameter(0); if (machine_diameter == 0.0f && preset_bundle) { const ConfigOption *opt = preset_bundle->printers.get_selected_preset().config.option("nozzle_diameter"); - if (opt) machine_diameter = static_cast(opt)->values[0]; + if (opt) { + const auto &nd = static_cast(opt)->values; + if (!nd.empty()) machine_diameter = nd.size() > 1 ? nd[1] : nd[0]; + } } stream << std::fixed << std::setprecision(1) << machine_diameter; std::string nozzle_diameter_str = stream.str(); @@ -1291,7 +1294,10 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) float machine_diameter = obj->GetExtderSystem()->GetNozzleDiameter(0); if (machine_diameter == 0.0f) { const ConfigOption *opt = preset_bundle->printers.get_selected_preset().config.option("nozzle_diameter"); - if (opt) machine_diameter = static_cast(opt)->values[0]; + if (opt) { + const auto &nd = static_cast(opt)->values; + if (!nd.empty()) machine_diameter = nd.size() > 1 ? nd[1] : nd[0]; + } } stream << std::fixed << std::setprecision(1) << machine_diameter; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 76ccac2838..15c80770f1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1862,27 +1862,22 @@ bool Sidebar::priv::switch_diameter(bool single) auto diameter_left = left_extruder->combo_diameter->GetValue(); auto diameter_right = right_extruder->combo_diameter->GetValue(); if (diameter_left != diameter_right) { - std::string printer_type = wxGetApp().preset_bundle->printers.get_edited_preset().get_printer_type(wxGetApp().preset_bundle); - auto left_name = _L(DevPrinterConfigUtil::get_toolhead_display_name(printer_type, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::SentenceCase)); - auto right_name = _L(DevPrinterConfigUtil::get_toolhead_display_name(printer_type, MAIN_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::SentenceCase)); - MessageDialog dlg(this->plater, - _L("The software does not support using different diameter of nozzles for one print. " - "If the left and right nozzles are inconsistent, we can only proceed with single-head printing. " - "Please confirm which nozzle you would like to use for this project."), - _L("Switch diameter"), wxYES_NO | wxNO_DEFAULT); - dlg.SetButtonLabel(wxID_YES, wxString::Format("%s: %smm", left_name, diameter_left)); - dlg.SetButtonLabel(wxID_NO, wxString::Format("%s: %smm", right_name, diameter_right)); - int result = dlg.ShowModal(); - if (result == wxID_YES) - diameter = diameter_left; - else if (result == wxID_NO) - diameter = diameter_right; - else + double left_value = 0.0, right_value = 0.0; + if (!diameter_left.ToCDouble(&left_value) || !diameter_right.ToCDouble(&right_value)) return false; + + Tab* printer_tab = wxGetApp().get_tab(Preset::TYPE_PRINTER); + DynamicPrintConfig new_conf = wxGetApp().preset_bundle->printers.get_edited_preset().config; + auto* nozzle_diameter_opt = new_conf.option("nozzle_diameter"); + if (printer_tab == nullptr || nozzle_diameter_opt == nullptr || nozzle_diameter_opt->size() < 2) + return false; + + nozzle_diameter_opt->values[0] = left_value; + nozzle_diameter_opt->values[1] = right_value; + printer_tab->load_config(new_conf); + return true; } - else { - diameter = diameter_left; - } + diameter = diameter_left; } return switch_diameter_to(diameter); @@ -1892,15 +1887,20 @@ bool Sidebar::priv::switch_diameter_to(const wxString &diameter) { // ORCA: Check if the selected diameter matches the current nozzle diameter in the config Preset& printer_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); + auto* nozzle_diameter = dynamic_cast(printer_preset.config.option("nozzle_diameter")); + // ORCA: the left/right combos of a BBL multi-nozzle printer set the nozzles apart without leaving + // the preset (see switch_diameter), so there the preset is only kept while every nozzle matches. + const bool nozzles_apart = nozzle_diameter && nozzle_diameter->size() > 1 && wxGetApp().preset_bundle->is_bbl_vendor() && + std::any_of(nozzle_diameter->values.begin(), nozzle_diameter->values.end(), + [&diameter](double value) { return get_diameter_string(value) != diameter.ToStdString(); }); // The combo lists printer variants, and the variant of a mixed-nozzle machine ("0.4+0.6") is no // single extruder's diameter, so the preset's own variant answers first. const std::string &printer_variant = printer_preset.config.opt_string("printer_variant"); - if (printer_variant == diameter.ToStdString()) { + if (printer_variant == diameter.ToStdString() && !nozzles_apart) { return true; } // A named variant ("0.4 High Flow") shares its diameter with the standard profile, which selecting // the plain diameter switches back to, so only a preset naming no variant is kept by its diameter. - auto* nozzle_diameter = dynamic_cast(printer_preset.config.option("nozzle_diameter")); if (printer_variant.empty() && nozzle_diameter && nozzle_diameter->size() > 0) { auto current_nozzle_dia = get_diameter_string(nozzle_diameter->values[0]); // If the selected diameter is the same as current nozzle, don't switch profiles diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 4b94d84543..55f405a28f 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -5833,7 +5833,8 @@ if (is_marlin_flavor) // The page edits the extruder selected on the variant switch. const size_t extruder_idx = size_t(get_current_active_extruder()); bool is_SEMM = m_config->opt_bool("single_extruder_multi_material"); - if (is_SEMM && m_extruders_count > 1 && boost::starts_with(opt_key, "nozzle_diameter")) + bool independent_nozzles = m_preset_bundle && m_preset_bundle->is_bbl_vendor() && m_extruders_count > 1; + if (is_SEMM && m_extruders_count > 1 && !independent_nozzles && boost::starts_with(opt_key, "nozzle_diameter")) { SuppressBackgroundProcessingUpdate sbpu; const double new_nd = boost::any_cast(value);