From 0530d6c4dae188bf44692e96dfd9b677557359f3 Mon Sep 17 00:00:00 2001 From: Clifford Garwood Date: Tue, 21 Apr 2026 00:27:40 -0400 Subject: [PATCH] feat: derive physical_extruder_map from printer_extruder_id; use for IMEX PA/temp Auto-populate physical_extruder_map (0-indexed) from printer_extruder_id (1-indexed) in Print::apply(). The guard only runs when the map is still at default size (<= 1 element), so printer profiles that set it explicitly are untouched. All IMEX parallel-mode PA and temperature emission now routes tool slot indices through physical_extruder_map before constructing firmware extruder qualifiers (EXTRUDER=, M104 T, M572 D). This ensures AFC/MMU setups where multiple slots share one physical extruder get the correct qualifier -- e.g. T6 on physical extruder 3 emits EXTRUDER=extruder3 instead of EXTRUDER=extruder6. Profile authoring for MMU/AFC printers: Add physical_extruder_map to the printer profile JSON as a 0-indexed string array, one entry per logical filament slot, whose value is the physical extruder carrying that slot. The array size must be > 1 for the explicit map to override the auto-derive. Example for a 7-slot printer with a 4-lane MMU on extruder 0 and three independent direct drives on extruders 1/2/3: "physical_extruder_map": ["0","0","0","0","1","2","3"] Non-MMU printers need no action -- printer_extruder_id already encodes the 1:1 mapping and the auto-derive handles it. Deeper integration (zone validation, collision detection, filament assignment grouping, Moonraker agent auto-population) is deferred. Co-Authored-By: Claude Sonnet 4.6 --- src/libslic3r/GCode.cpp | 14 ++++++++++---- src/libslic3r/PrintApply.cpp | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a3e8be4c6d..e617e42743 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3232,7 +3232,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato if (tool_idx == (int)initial_extruder_id) continue; if (!print.config().enable_pressure_advance.get_at(tool_idx)) continue; file.write(m_writer.set_pressure_advance( - print.config().pressure_advance.get_at(tool_idx), tool_idx)); + print.config().pressure_advance.get_at(tool_idx), + m_config.physical_extruder_map.get_at(tool_idx))); } } } @@ -4696,7 +4697,8 @@ LayerResult GCode::process_layer( if (tool_idx >= num_nozzles) continue; int temperature = print.config().nozzle_temperature.values[tool_idx]; if (temperature > 0) - gcode += GCodeWriter::set_temperature(temperature, m_writer.config.gcode_flavor, false, tool_idx, "set IMEX tool temperature"); + gcode += GCodeWriter::set_temperature(temperature, m_writer.config.gcode_flavor, false, + m_config.physical_extruder_map.get_at(tool_idx), "set IMEX tool temperature"); } } else { for (const Extruder& extruder : m_writer.extruders()) { @@ -7563,7 +7565,9 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo // In primary mode (single active tool) regular tool changes handle PA // so no qualifier is needed — same as a non-IMEX printer. const bool imex_parallel = !m_imex_parallel_mode.empty() && m_imex_parallel_mode != "primary"; - const int pa_tool = imex_parallel ? (int)new_filament_id : -1; + const int pa_tool = imex_parallel + ? m_config.physical_extruder_map.get_at((int)new_filament_id) + : -1; gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(new_filament_id), pa_tool); // Orca: Adaptive PA // Reset Adaptive PA processor last PA value @@ -7864,7 +7868,9 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo if (m_config.enable_pressure_advance.get_at(new_filament_id)) { const bool imex_parallel = !m_imex_parallel_mode.empty() && m_imex_parallel_mode != "primary"; - const int pa_tool = imex_parallel ? (int)new_filament_id : -1; + const int pa_tool = imex_parallel + ? m_config.physical_extruder_map.get_at((int)new_filament_id) + : -1; gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(new_filament_id), pa_tool); // Orca: Adaptive PA // Reset Adaptive PA processor last PA value diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index ac2d56780f..7e54e202bd 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1161,6 +1161,20 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ //update print config related with variants new_full_config.update_values_to_printer_extruders(new_full_config, print_options_with_variant, "print_extruder_id", "print_extruder_variant"); + // Derive physical_extruder_map (0-indexed) from printer_extruder_id (1-indexed) when the + // map hasn't been explicitly configured in the printer profile (size <= 1 = default). + // This gives all firmware code a consistent slot → physical-extruder translation, + // including AFC/MMU setups where multiple tool slots share one physical extruder. + { + auto* pem = new_full_config.option("physical_extruder_map", true); + const auto* pei = new_full_config.option("printer_extruder_id"); + if (pem && pei && !pei->values.empty() && pem->values.size() <= 1) { + pem->values.clear(); + for (int v : pei->values) + pem->values.push_back(v - 1); // convert 1-indexed extruder IDs to 0-indexed + } + } + m_ori_full_print_config = new_full_config; new_full_config.update_values_to_printer_extruders_for_multiple_filaments(new_full_config, filament_options_with_variant, "filament_self_index", "filament_extruder_variant"); auto opt_filament_map = new_full_config.option("filament_map");