From 5cf7e27c239b794f84884ffda2ddf46d6369dfe9 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Thu, 16 Jul 2026 16:35:11 +0800 Subject: [PATCH] Show combined nozzle mapping when a filament switch is installed --- src/slic3r/GUI/AmsMappingPopup.cpp | 97 ++++++++++++--- src/slic3r/GUI/AmsMappingPopup.hpp | 3 +- src/slic3r/GUI/SelectMachine.cpp | 177 ++++++++++++++++++--------- src/slic3r/GUI/SelectMachine.hpp | 4 +- src/slic3r/GUI/SyncAmsInfoDialog.cpp | 33 ----- src/slic3r/GUI/SyncAmsInfoDialog.hpp | 1 - 6 files changed, 206 insertions(+), 109 deletions(-) diff --git a/src/slic3r/GUI/AmsMappingPopup.cpp b/src/slic3r/GUI/AmsMappingPopup.cpp index af5df1f091..9baca095e1 100644 --- a/src/slic3r/GUI/AmsMappingPopup.cpp +++ b/src/slic3r/GUI/AmsMappingPopup.cpp @@ -976,7 +976,8 @@ void AmsMapingPopup::on_left_down(wxMouseEvent &evt) if (pos.x > p_rect.x && pos.y > p_rect.y && pos.x < (p_rect.x + item->GetSize().x) && pos.y < (p_rect.y + item->GetSize().y)) { // Orca: the external spool is un-pickable while a Filament Track Switch is installed (Apple hit-tests here). - if (m_fila_switch_installed && (item->m_ams_id == VIRTUAL_TRAY_MAIN_ID || item->m_ams_id == VIRTUAL_TRAY_DEPUTY_ID)) { return; } + // The combined (dynamic) view also routes filament through the switch, so its virtual slots stay un-pickable too. + if ((m_fila_switch_installed || m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) && (item->m_ams_id == VIRTUAL_TRAY_MAIN_ID || item->m_ams_id == VIRTUAL_TRAY_DEPUTY_ID)) { return; } if (item->m_tray_data.type == TrayType::NORMAL) { if (!m_ext_mapping_filatype_check && (item->m_ams_id == VIRTUAL_TRAY_MAIN_ID || item->m_ams_id == VIRTUAL_TRAY_DEPUTY_ID)) { // Do nothing @@ -988,7 +989,8 @@ void AmsMapingPopup::on_left_down(wxMouseEvent &evt) if (item->m_tray_data.type == TrayType::EMPTY) return; if ((m_show_type == ShowType::LEFT && item->GetParent()->GetName() == "left") || (m_show_type == ShowType::RIGHT && item->GetParent()->GetName() == "right") || - m_show_type == ShowType::LEFT_AND_RIGHT) { + m_show_type == ShowType::LEFT_AND_RIGHT || + m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) { item->send_event(m_current_filament_id); Dismiss(); break; @@ -1249,6 +1251,14 @@ void AmsMapingPopup::update(MachineObject* obj, const std::vector& } m_right_extra_slot->Show(); } + else if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) + { + // Orca: kept inside the monolithic update() instead of a separate update TU. + // A filament switch feeds both extruders, so every AMS and both external spools render + // in one combined panel (the right/main area). The left panel and the ext slots stay + // hidden (set above); the ext spools are added as containers in the AMS block below. + m_right_marea_panel->Show(); + } } for (int i = 0; i < obj->vt_slot.size(); i++) { @@ -1282,13 +1292,17 @@ void AmsMapingPopup::update(MachineObject* obj, const std::vector& } } - if (obj->vt_slot[i].id == std::to_string(VIRTUAL_TRAY_MAIN_ID)) { - m_right_extra_slot->send_win = send_win; - add_ext_ams_mapping(td, m_right_extra_slot); - } - else if (obj->vt_slot[i].id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID)) { - m_left_extra_slot->send_win = send_win; - add_ext_ams_mapping(td, m_left_extra_slot); + // Orca: in the combined (switch) view the external spools render as containers inside the + // single panel (handled in the AMS block below), not in the separate left/right ext slots. + if (m_show_type != ShowType::LEFT_AND_RIGHT_DYNAMIC) { + if (obj->vt_slot[i].id == std::to_string(VIRTUAL_TRAY_MAIN_ID)) { + m_right_extra_slot->send_win = send_win; + add_ext_ams_mapping(td, m_right_extra_slot); + } + else if (obj->vt_slot[i].id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID)) { + m_left_extra_slot->send_win = send_win; + add_ext_ams_mapping(td, m_left_extra_slot); + } } } @@ -1305,11 +1319,22 @@ void AmsMapingPopup::update(MachineObject* obj, const std::vector& int ams_indx = atoi(ams_iter->first.c_str()); int nozzle_id = ams_iter->second->GetExtruderId(); + // Orca: with a filament switch an AMS feeds both extruders and is shown in the single + // combined panel (the right/main area); iterate the binding set instead of the single + // extruder id and skip AMS with no usable binding. Without a switch this resolves to the + // original left/right pin (a non-switch AMS carries exactly its one bound extruder). + wxPanel* target_panel = nullptr; + if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) { + if (ams_iter->second->GetBindedExtruderSet().empty()) { continue; } + target_panel = m_right_marea_panel; + } else { + target_panel = (nozzle_id == 0) ? m_right_marea_panel : m_left_marea_panel; + } auto sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL); - auto ams_mapping_item_container = new MappingContainer(nozzle_id == 0 ? m_right_marea_panel : m_left_marea_panel, ams_iter->second->GetDisplayName(), + auto ams_mapping_item_container = new MappingContainer(target_panel, ams_iter->second->GetDisplayName(), ams_iter->second->GetSlotCount()); - ams_mapping_item_container->SetName(nozzle_id == 0 ? m_right_marea_panel->GetName() : m_left_marea_panel->GetName()); + ams_mapping_item_container->SetName(target_panel->GetName()); ams_mapping_item_container->SetSizer(sizer_mapping_list); ams_mapping_item_container->Layout(); @@ -1357,14 +1382,14 @@ void AmsMapingPopup::update(MachineObject* obj, const std::vector& m_amsmapping_container_sizer_list.push_back(sizer_mapping_list); m_amsmapping_container_list.push_back(ams_mapping_item_container); - if (nozzle_id == 0) { + if (target_panel == m_right_marea_panel) { has_right_ams = true; if (ams_mapping_item_container->get_slots_num() == 1) { right_one_slot_containers.push_back(ams_mapping_item_container); } else { right_four_slot_containers.push_back(ams_mapping_item_container); } - } else if (nozzle_id == 1) { + } else if (target_panel == m_left_marea_panel) { has_left_ams = true; if (ams_mapping_item_container->get_slots_num() == 1) { left_one_slot_containers.push_back(ams_mapping_item_container); @@ -1374,10 +1399,52 @@ void AmsMapingPopup::update(MachineObject* obj, const std::vector& } } + // Orca: with a filament switch, render the external spools as containers inside the single + // combined panel (mirroring the AMS containers) instead of the separate left/right ext slots. + if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) { + for (int i = obj->vt_slot.size() - 1; i >= 0; i--) { + DevAmsTray* tray_data = &obj->vt_slot[i]; + TrayData td; + td.id = std::stoi(tray_data->id); + td.ams_id = std::stoi(tray_data->id); + td.slot_id = 0; + if (!tray_data->is_tray_info_ready()) { + td.type = THIRD; + } else { + td.type = NORMAL; + td.remain = tray_data->remain; + td.colour = DevAmsTray::decode_color(tray_data->color); + td.name = tray_data->get_display_filament_type(); + td.filament_type = tray_data->get_filament_type(); + td.ctype = tray_data->ctype; + for (auto col : tray_data->cols) { td.material_cols.push_back(DevAmsTray::decode_color(col)); } + } + + auto sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL); + wxString shown_name = (td.ams_id == VIRTUAL_TRAY_MAIN_ID) ? wxString("Ext-R") : wxString("Ext-L"); + auto ams_mapping_item_container = new MappingContainer(m_right_marea_panel, shown_name, 1); + ams_mapping_item_container->SetName(m_right_marea_panel->GetName()); + ams_mapping_item_container->SetSizer(sizer_mapping_list); + ams_mapping_item_container->Layout(); + + std::vector tray_datas{td}; + add_ams_mapping(tray_datas, false, ams_mapping_item_container, sizer_mapping_list); + m_amsmapping_container_sizer_list.push_back(sizer_mapping_list); + m_amsmapping_container_list.push_back(ams_mapping_item_container); + right_one_slot_containers.push_back(ams_mapping_item_container); + has_right_ams = true; + } + } + _add_containers(this, left_one_slot_containers, left_four_slots_containers, m_sizer_ams_basket_left); _add_containers(this, right_one_slot_containers, right_four_slot_containers, m_sizer_ams_basket_right); - m_left_split_ams_sizer->Show(has_left_ams); - m_right_split_ams_sizer->Show(has_right_ams); + if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) { + m_left_split_ams_sizer->Show(false); + m_right_split_ams_sizer->Show(false); + } else { + m_left_split_ams_sizer->Show(has_left_ams); + m_right_split_ams_sizer->Show(has_right_ams); + } update_items_check_state(ams_mapping_result); } else { m_right_split_ams_sizer->Show(false); diff --git a/src/slic3r/GUI/AmsMappingPopup.hpp b/src/slic3r/GUI/AmsMappingPopup.hpp index 0850c0e728..5c692251b0 100644 --- a/src/slic3r/GUI/AmsMappingPopup.hpp +++ b/src/slic3r/GUI/AmsMappingPopup.hpp @@ -64,7 +64,8 @@ enum TrayType { enum ShowType { LEFT, // only show left ams and left ext RIGHT, //only show right ams and right ext - LEFT_AND_RIGHT //show left and right ams at the same time + LEFT_AND_RIGHT, //show left and right ams at the same time + LEFT_AND_RIGHT_DYNAMIC //show all left and right at one panel when use_dynamic_switch }; struct TrayData diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index 0fdeb93bf4..fa48195d86 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -1367,38 +1367,31 @@ bool SelectMachineDialog::build_nozzles_info(std::string& nozzles_info) return true; } -bool SelectMachineDialog::can_hybrid_mapping(DevExtderSystem data) { - // Mixed mappings are not allowed - return false; +bool SelectMachineDialog::can_hybrid_mapping(MachineObject* obj_) const { + return obj_ && obj_->GetFilaSwitch()->IsInstalled(); +} - if (data.GetTotalExtderCount() <= 1 || !wxGetApp().preset_bundle) - return false; +ShowType SelectMachineDialog::get_filament_mapping_show_type(MachineObject* obj_, int fila_logic_id) const +{ + try { + const auto& full_config = wxGetApp().preset_bundle->full_config(); + size_t total_ext_count = full_config.option("nozzle_diameter")->values.size(); + if (total_ext_count < 2) { + return ShowType::RIGHT; + } - //The default two extruders are left, right, but the order of the extruders on the machine is right, left. - //Therefore, some adjustments need to be made. - std::vectorflow_type_of_machine; - for (const auto& ext : data.GetExtruders()) - { - std::string type_str = ext.GetNozzleFlowType() == NozzleFlowType::H_FLOW ? "High Flow" : "Standard"; - flow_type_of_machine.push_back(type_str); + if (can_hybrid_mapping(obj_)) { + return ShowType::LEFT_AND_RIGHT_DYNAMIC; + } else if (m_filaments_map.at(fila_logic_id) == 1) { + return ShowType::LEFT; + } else if (m_filaments_map.at(fila_logic_id) == 2) { + return ShowType::RIGHT; + } + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": exception: " << e.what(); } - //get the nozzle type of preset --> flow_types - const Preset& current_printer = wxGetApp().preset_bundle->printers.get_selected_preset(); - const Preset* base_printer = wxGetApp().preset_bundle->printers.get_preset_base(current_printer); - std::string base_name = base_printer->name; - auto flow_data = wxGetApp().app_config->get_nozzle_volume_types_from_config(base_name); - std::vector flow_types; - boost::split(flow_types, flow_data, boost::is_any_of(",")); - if (flow_types.size() <= 1 || flow_types.size() != flow_type_of_machine.size()) return false; - - //Only when all preset nozzle types and machine nozzle types are exactly the same, return true. - auto type = flow_types[0]; - for (int i = 0; i < flow_types.size(); i++){ - if (flow_types[i] != type || flow_type_of_machine[i] != type) - return false; - } - return true; + return ShowType::LEFT_AND_RIGHT; } //When filaments cannot be matched automatically, whether to use ext for automatic supply @@ -4761,25 +4754,7 @@ void SelectMachineDialog::reset_and_sync_ams_list() DeviceManager *dev_manager = Slic3r::GUI::wxGetApp().getDeviceManager(); if (!dev_manager) return; MachineObject *obj_ = dev_manager->get_selected_machine(); - const auto& full_config = wxGetApp().preset_bundle->full_config(); - size_t nozzle_nums = full_config.option("nozzle_diameter")->values.size(); - if (nozzle_nums > 1) - { - if (obj_ && can_hybrid_mapping(*obj_->GetExtderSystem())) - { - m_mapping_popup.set_show_type(ShowType::LEFT_AND_RIGHT); - } - else if (m_filaments_map[extruder] == 1) - { - m_mapping_popup.set_show_type(ShowType::LEFT); - } - else if(m_filaments_map[extruder] == 2) - { - m_mapping_popup.set_show_type(ShowType::RIGHT); - } - } else { - m_mapping_popup.set_show_type(ShowType::RIGHT); - } + m_mapping_popup.set_show_type(get_filament_mapping_show_type(obj_, extruder)); if (obj_) { if (m_mapping_popup.IsShown()) return; wxPoint pos = item->ClientToScreen(wxPoint(0, 0)); @@ -4846,9 +4821,105 @@ void SelectMachineDialog::reset_and_sync_ams_list() m_filament_panel_sizer->Layout(); } + // Orca: a filament switch feeds both extruders, so the per-nozzle material items collapse into + // the single panel. Reposition once the selected machine's switch state is known (no-op otherwise). + DeviceManager* dev = wxGetApp().getDeviceManager(); + update_material_item_pos(dev ? dev->get_selected_machine() : nullptr); + // reset_ams_material();//show "-" } +// Orca: collapse the per-nozzle material items into the single panel when the printer has one +// extruder or a filament switch (both feed a single logical mapping surface); otherwise keep the +// left/right split. Early-returns unless an item is actually in the wrong panel. +void SelectMachineDialog::update_material_item_pos(MachineObject* obj_) +{ + if (!obj_) { + return; + } + + const bool is_single_head = obj_->GetExtderSystem()->GetTotalExtderCount() < 2; + const bool has_switcher = obj_->GetFilaSwitch()->IsInstalled(); + const bool use_single_panel = is_single_head || has_switcher; + + bool to_change_pos = false; + for (const auto& iter : m_materialList) { + const auto& material_id = iter.second->id; + const auto& material_item = iter.second->item; + if (use_single_panel) { + if (!m_sizer_ams_mapping->IsShown(material_item)) { + to_change_pos = true; + } + } else { + if (m_filaments_map[material_id] == 1 && !m_sizer_ams_mapping_left->IsShown(material_item)) { + to_change_pos = true; + } else if (m_filaments_map[material_id] == 2 && !m_sizer_ams_mapping_right->IsShown(material_item)) { + to_change_pos = true; + } + } + + if (to_change_pos) break; + } + + if (!to_change_pos) { + return; + } + + m_sizer_ams_mapping->Clear(false); + m_sizer_ams_mapping_left->Clear(false); + m_sizer_ams_mapping_right->Clear(false); + + int sizer_count = 0; + int left_sizer_count = 0; + int right_sizer_count = 0; + for (const auto& iter : m_materialList) { + const auto& material_id = iter.second->id; + const auto& material_item = iter.second->item; + if (use_single_panel) { + if (!m_sizer_ams_mapping->IsShown(material_item)) { + material_item->Reparent(m_filament_panel); + m_sizer_ams_mapping->Add(material_item, 0, wxALL, FromDIP(5)); + sizer_count++; + } + } else { + if (m_filaments_map[material_id] == 1) { + material_item->Reparent(m_filament_left_panel); + m_sizer_ams_mapping_left->Add(material_item, 0, wxALL, FromDIP(5)); + left_sizer_count++; + } else if(m_filaments_map[material_id] == 2){ + material_item->Reparent(m_filament_right_panel); + m_sizer_ams_mapping_right->Add(material_item, 0, wxALL, FromDIP(5)); + right_sizer_count++; + } + } + } + + if (sizer_count > 0) { + m_sizer_ams_mapping->SetCols(8); + m_sizer_ams_mapping->Layout(); + m_filament_panel_sizer->Layout(); + } + + if (left_sizer_count > 0) { + m_sizer_ams_mapping_left->SetCols(4); + m_sizer_ams_mapping_left->Layout(); + m_filament_panel_left_sizer->Layout(); + m_filament_left_panel->Layout(); + } + + if (right_sizer_count > 0) { + m_sizer_ams_mapping_right->SetCols(4); + m_sizer_ams_mapping_right->Layout(); + m_filament_panel_right_sizer->Layout(); + m_filament_right_panel->Layout(); + } + + m_filament_panel->Show(sizer_count > 0); + m_filament_left_panel->Show(left_sizer_count > 0 || right_sizer_count > 0); + m_filament_right_panel->Show(left_sizer_count > 0 || right_sizer_count > 0); + Layout(); +} + void SelectMachineDialog::clone_thumbnail_data() { //record preview_colors MaterialHash::iterator iter = m_materialList.begin(); @@ -5297,17 +5368,7 @@ void SelectMachineDialog::set_default_from_sdcard() pos.y += item->GetRect().height; m_mapping_popup.Move(pos); - if (diameters_count > 1) { - if (obj_ && can_hybrid_mapping(*obj_->GetExtderSystem())) { - m_mapping_popup.set_show_type(ShowType::LEFT_AND_RIGHT); - } else if (m_filaments_map[m_current_filament_id] == 1) { - m_mapping_popup.set_show_type(ShowType::LEFT); - } else if (m_filaments_map[m_current_filament_id] == 2) { - m_mapping_popup.set_show_type(ShowType::RIGHT); - } - } else { - m_mapping_popup.set_show_type(ShowType::RIGHT); - } + m_mapping_popup.set_show_type(get_filament_mapping_show_type(obj_, m_current_filament_id)); if (obj_ && obj_->get_dev_id() == m_printer_last_select) { diff --git a/src/slic3r/GUI/SelectMachine.hpp b/src/slic3r/GUI/SelectMachine.hpp index 354cde9fc7..85d30be633 100644 --- a/src/slic3r/GUI/SelectMachine.hpp +++ b/src/slic3r/GUI/SelectMachine.hpp @@ -510,7 +510,9 @@ public: bool do_ams_mapping(MachineObject *obj_,bool use_ams); bool get_ams_mapping_result(std::string& mapping_array_str, std::string& mapping_array_str2, std::string& ams_mapping_info) const; bool build_nozzles_info(std::string& nozzles_info); - bool can_hybrid_mapping(DevExtderSystem data); + bool can_hybrid_mapping(MachineObject* obj_) const; + ShowType get_filament_mapping_show_type(MachineObject* obj_, int fila_logic_id) const; + void update_material_item_pos(MachineObject* obj_); void auto_supply_with_ext(std::vector slots); int convert_filament_map_nozzle_id_to_task_nozzle_id(int nozzle_id) const; diff --git a/src/slic3r/GUI/SyncAmsInfoDialog.cpp b/src/slic3r/GUI/SyncAmsInfoDialog.cpp index 704fa9ec2a..70758cc2ba 100644 --- a/src/slic3r/GUI/SyncAmsInfoDialog.cpp +++ b/src/slic3r/GUI/SyncAmsInfoDialog.cpp @@ -1447,39 +1447,6 @@ bool SyncAmsInfoDialog::build_nozzles_info(std::string &nozzles_info) return true; } -bool SyncAmsInfoDialog::can_hybrid_mapping(DevExtderSystem data) -{ - // Mixed mappings are not allowed - return false; - - if (data.GetTotalExtderCount() <= 1 || !wxGetApp().preset_bundle) return false; - - // The default two extruders are left, right, but the order of the extruders on the machine is right, left. - // Therefore, some adjustments need to be made. - std::vector flow_type_of_machine; - for (auto it = data.GetExtruders().rbegin(); it != data.GetExtruders().rend(); it++) { - // exist field is not updated, wait add - // if (it->exist < 3) return false; - std::string type_str = it->GetNozzleFlowType() ? "High Flow" : "Standard"; - flow_type_of_machine.push_back(type_str); - } - // get the nozzle type of preset --> flow_types - const Preset & current_printer = wxGetApp().preset_bundle->printers.get_selected_preset(); - const Preset * base_printer = wxGetApp().preset_bundle->printers.get_preset_base(current_printer); - std::string base_name = base_printer->name; - auto flow_data = wxGetApp().app_config->get_nozzle_volume_types_from_config(base_name); - std::vector flow_types; - boost::split(flow_types, flow_data, boost::is_any_of(",")); - if (flow_types.size() <= 1 || flow_types.size() != flow_type_of_machine.size()) return false; - - // Only when all preset nozzle types and machine nozzle types are exactly the same, return true. - auto type = flow_types[0]; - for (int i = 0; i < flow_types.size(); i++) { - if (flow_types[i] != type || flow_type_of_machine[i] != type) return false; - } - return true; -} - // When filaments cannot be matched automatically, whether to use ext for automatic supply void SyncAmsInfoDialog::auto_supply_with_ext(std::vector slots) { diff --git a/src/slic3r/GUI/SyncAmsInfoDialog.hpp b/src/slic3r/GUI/SyncAmsInfoDialog.hpp index 6cacff0af1..6a21ca6bfc 100644 --- a/src/slic3r/GUI/SyncAmsInfoDialog.hpp +++ b/src/slic3r/GUI/SyncAmsInfoDialog.hpp @@ -212,7 +212,6 @@ public: void show_thumbnail_page(); bool get_ams_mapping_result(std::string &mapping_array_str, std::string &mapping_array_str2, std::string &ams_mapping_info); bool build_nozzles_info(std::string &nozzles_info); - bool can_hybrid_mapping(DevExtderSystem data); void auto_supply_with_ext(std::vector slots); bool is_nozzle_type_match(DevExtderSystem data, wxString &error_message) const; int convert_filament_map_nozzle_id_to_task_nozzle_id(int nozzle_id);