fix(imex): skip primary in IMEX is_extruder_used marking

a38b95bf45 translated physical -> logical for IMEX active tools but used
resolve_filament_for_head for *all* active tools including the primary.
For the primary, that falls back to first_filament_for_physical_head —
which returns the FIRST logical slot routed to the primary's physical
extruder, not the slot the user actually assigned to the printing object.

On the user's Neo XP 0.6 (pem [0,0,0,0,1,2,3,3]) printing in copy mode
with the object on slot 2 (PLA dark grey):
  - tool_ordering correctly marks slot 2 (object's filament).
  - The IMEX-marking loop then *also* marked slot 0 (ABS) as the primary's
    "first-routed" slot — wrong: slot 0 isn't loaded, slot 2 is.
  - Start-gcode template emitted EXTRUDER=260 EXTRUDER2=235 EXTRUDER4=235;
    the ABS-temp emission for slot 0 was harmless noise in this macro
    design but conceptually bogus.

The primary's filament is already correctly covered by
tool_ordering.all_extruders() — that lists the slots the objects on the
plate are assigned to.  Skip the primary in the IMEX-marking loop using
the same pattern as the IMEX PA emission path at GCode.cpp:3265
(translate initial_extruder_id -> physical via pem, skip that physical).

Result on the user's setup post-fix:
  - is_extruder_used: slot 2 (object), slot 4 (T1's filament in copy).
  - Start-gcode emits EXTRUDER2=235 EXTRUDER4=235 — exactly two temps,
    one per active heater, with no spurious ABS bookkeeping.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Clifford Garwood
2026-04-25 05:07:34 -04:00
co-authored by Claude Opus 4.7
parent 2b7f438887
commit d41e0d09b4
+23 -10
View File
@@ -2869,22 +2869,35 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
for (unsigned int extruder : tool_ordering.all_extruders())
is_extruder_used[extruder] = true;
// Orca IMEX: also mark the LOGICAL filament slot that each active secondary
// carriage will load during the print. is_extruder_used is logical-slot indexed
// Orca IMEX: mark the LOGICAL filament slot each active SECONDARY carriage
// will load during the print. is_extruder_used is logical-slot indexed
// (consumed as `is_extruder_used[N]` in machine_start_gcode templates), but
// get_imex_active_tools returns PHYSICAL extruder indices. On any printer with
// physical_extruder_map size > 1 (MMU/AFC), writing the physical index into a
// logical-indexed array marks the wrong slot — start-gcode templates would emit
// the wrong filament's temp / not emit the correct one.
// get_imex_active_tools returns PHYSICAL extruder indices. On any printer
// with physical_extruder_map size > 1 (MMU/AFC), writing the physical index
// into a logical-indexed array marks the wrong slot.
//
// Translate physical -> logical via the per-plate imex_head_filament_map (set by
// the IMEX ghost picker), with first_filament_for_physical_head as the fallback
// when no override is set.
if (print.config().is_imex.value && !print.objects().empty()) {
// The primary is intentionally SKIPPED here — its filament is already
// covered by tool_ordering.all_extruders() above (which lists the slots the
// objects on the plate are actually assigned to). Marking the primary
// again via the pem `first_filament_for_physical_head` fallback would
// pollute is_extruder_used with the *first* slot routed to the primary's
// physical extruder, which is generally not the slot the user assigned to
// the printing object. Same skip-primary pattern as the IMEX PA emission
// path (GCode.cpp ~3265).
//
// For secondaries: translate physical -> logical via the per-plate
// imex_head_filament_map (set by the IMEX ghost picker), with
// first_filament_for_physical_head as the fallback when no override is set.
if (print.config().is_imex.value && !print.objects().empty()
&& initial_extruder_id != (unsigned int)-1) {
const auto plate_head_map = parse_imex_head_filament_map(
print.objects().front()->config().imex_head_filament_map.value);
const ConfigOptionInts& pem = print.config().physical_extruder_map;
const int primary_physical = pem.values.empty()
? -1
: pem.get_at((int)initial_extruder_id);
for (int phys_tool : get_imex_active_tools(print)) {
if (phys_tool == primary_physical) continue;
const int logical = resolve_filament_for_head(plate_head_map, pem, phys_tool);
if (logical >= 0 && logical < (int)is_extruder_used.size())
is_extruder_used[logical] = true;