From 0db99d21dd47549b825f3c429c3d4a9f82ff5df1 Mon Sep 17 00:00:00 2001 From: Clifford Garwood Date: Sat, 25 Apr 2026 02:43:24 -0400 Subject: [PATCH] =?UTF-8?q?fix(imex):=20translate=20physical=E2=86=92logic?= =?UTF-8?q?al=20when=20looking=20up=20filament=20info=20in=20pre-slice=20w?= =?UTF-8?q?arnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit collect_imex_warnings parses imex_mode_active_tools to get the active tool indices. Those are PHYSICAL extruder indices (one per carriage). It then used those same indices to look up filament_presets[tool_idx] and bed_temps[tool_idx] — but both arrays are indexed by LOGICAL filament slot. For MMU/AFC layouts where multiple logical slots feed one physical extruder (e.g. AFC manifold: 4 lanes on physical T0), the warning would report the wrong filament: a secondary on physical T1 would be named with filament_presets[1] (= AFC lane 2) instead of the actual filament on T1. Symptom: in IMEX parallel mode on the user's IQEX-AFC printer (pem [0,0,0,0,1,2,3]), the multi-extruder warning called the secondary tool "T1" but reported the filament type for logical slot 1 (an AFC lane), not the actual filament 4 routed to physical T1. Translate physical → logical via effective_physical_extruder_map (with per-plate imex_head_filament_map override) before indexing into filament_presets and bed_temps. The displayed "T%d" still shows the PHYSICAL extruder number — that's the carriage identity the user sees on hardware. Only the filament-info lookup is changed. Co-Authored-By: Claude Opus 4.7 --- src/slic3r/GUI/Plater.cpp | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e84df6e905..c462428a87 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -10018,12 +10018,28 @@ static std::vector collect_imex_warnings(PartPlate* plate) const auto* bed_temps = bed_temp_key.empty() ? nullptr : full_cfg.option(bed_temp_key); const auto& filament_presets = bundle->filament_presets; - // Primary tool's bed temp and filament type - const int primary_bed_temp = (bed_temps && primary_tool < (int)bed_temps->values.size()) - ? bed_temps->values[primary_tool] : 0; + // active_tools and primary_tool are PHYSICAL extruder indices (parsed from + // imex_mode_active_tools). filament_presets and bed_temps are indexed by LOGICAL + // filament slot. For MMU/AFC layouts where multiple logical slots feed one + // physical extruder, looking up filament_presets[physical_idx] returns the wrong + // filament. Translate physical -> logical via the effective pem (with per-plate + // imex_head_filament_map override) before indexing. + const ConfigOptionInts pem = effective_physical_extruder_map(*bundle); + std::map plate_head_map; + if (auto* hfm = plate->config()->option("imex_head_filament_map")) + plate_head_map = parse_imex_head_filament_map(hfm->value); + auto logical_for = [&](int physical_idx) -> int { + const int logical = resolve_filament_for_head(plate_head_map, pem, physical_idx); + return logical >= 0 ? logical : physical_idx; // fallback: identity if no routing found + }; + + // Primary tool's bed temp and filament type (looked up by logical slot) + const int primary_logical = logical_for(primary_tool); + const int primary_bed_temp = (bed_temps && primary_logical < (int)bed_temps->values.size()) + ? bed_temps->values[primary_logical] : 0; std::string primary_display_type; - if (primary_tool < (int)filament_presets.size()) { - Preset* p = bundle->filaments.find_preset(filament_presets[primary_tool]); + if (primary_logical < (int)filament_presets.size()) { + Preset* p = bundle->filaments.find_preset(filament_presets[primary_logical]); if (p) p->get_filament_type(primary_display_type); } if (primary_display_type.empty()) primary_display_type = "unknown"; @@ -10032,16 +10048,18 @@ static std::vector collect_imex_warnings(PartPlate* plate) const int tool_idx = active_tools[i]; if (tool_idx == primary_tool) continue; + const int secondary_logical = logical_for(tool_idx); + std::string secondary_display_type; - if (tool_idx < (int)filament_presets.size()) { - Preset* p = bundle->filaments.find_preset(filament_presets[tool_idx]); + if (secondary_logical < (int)filament_presets.size()) { + Preset* p = bundle->filaments.find_preset(filament_presets[secondary_logical]); if (p) p->get_filament_type(secondary_display_type); } if (secondary_display_type.empty()) secondary_display_type = "unknown"; // Check 2: bed temperature — primary wins, secondary may not get what it needs - if (bed_temps && primary_bed_temp > 0 && tool_idx < (int)bed_temps->values.size()) { - const int secondary_bed_temp = bed_temps->values[tool_idx]; + if (bed_temps && primary_bed_temp > 0 && secondary_logical < (int)bed_temps->values.size()) { + const int secondary_bed_temp = bed_temps->values[secondary_logical]; if (secondary_bed_temp > 0 && std::abs(secondary_bed_temp - primary_bed_temp) > 5) { warnings.push_back(wxString::Format( _L("Bed temperature conflict: T%d (%s) sets the bed to %d\u00B0C \u2014 "