fix(gcode): skip ooze prevention for same-physical filament changes

Ooze prevention currently treats every toolchange as a transition between
independent hotends — the OLD filament's extruder cools to a standby
temperature (via standby_temperature_delta or the filament's
idle_temperature) before the change, and the NEW filament's extruder
ramps back up afterward. This is correct for IDEX/toolchanger setups
where the parked nozzle would drip otherwise.

For AFC/MMU lane swaps where the SAME physical extruder stays selected
(only the loaded filament changes), the cool-down → re-heat round trip
is pointless: same nozzle, same heater, just a different filament feeding
it. In-print this costs 30+ seconds per lane swap, and the AFC tip-form
sequence ends up running on a cooling extruder.

Gate the pre_toolchange call on physical_extruder_map: when both the old
and new filament index map to the same physical extruder, skip the
standby cool-down. post_toolchange is left untouched — its M109 to the
new filament's print temp is still emitted, so per-lane temperature
differences (e.g. PLA → PETG on the same AFC manifold) are still handled.

Note on pem sizing: the guard requires physical_extruder_map to be sized
to the filament count for the per-filament lookup to succeed. The option's
registered default is a single-entry [0], which is shorter than the filament
count on any multi-filament setup, so the bounds check fails and ooze runs
as before. The fix fires only on profiles that explicitly author pem to
match filament count (the AFC/MMU/toolchanger configs that actually encode
same-physical routing).

Behavior matrix:

| Config | pem | Ooze behavior |
|---|---|---|
| Single-extruder + SEMM | (any) | `init_ooze_prevention` already disables ooze. No change. |
| Single-extruder, no SEMM | (any) | Single filament, no toolchanges. N/A. |
| IDEX / Toolchanger | `[0,1,…]` per-filament | Cross-physical → ooze runs as before. |
| Vanilla AFC, SEMM=true | (any) | `init_ooze_prevention` disables ooze. No change. |
| Vanilla AFC, SEMM=false | `[0,0,…]` per-filament | Same physical → **ooze SKIPPED** (the fix). |
| Toolchanger + AFC | per-filament | AFC swaps skip, cross-physical swaps run. |
| Default pem `[0]` (1 entry) | shorter than filament count | Bounds check fails for filament index ≥ 1 → ooze runs as before. No-op for profiles that haven't authored a per-filament pem. |
| Empty pem | `[]` | Guard returns false → ooze runs as before. |

The guard depends only on physical_extruder_map; no machine-class check.
Any printer whose pem is sized to filament count and indicates multiple
logical slots routed to the same physical hotend benefits.
This commit is contained in:
Clifford Garwood
2026-06-08 03:13:44 -04:00
parent 896adc7071
commit 62074b409d
+17 -1
View File
@@ -7971,7 +7971,23 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
// If ooze prevention is enabled, park current extruder in the nearest
// standby point and set it to the standby temperature.
if (m_ooze_prevention.enable && m_writer.filament() != nullptr)
//
// Same-physical short-circuit: skip the standby cool-down when the old and
// new filament both route to the same physical extruder via
// physical_extruder_map (AFC/MMU lane swaps where the active heater stays
// selected, just a different lane is loaded). The cool-down → re-heat
// round trip is pointless in that case — same nozzle, same heater, just a
// different filament feeding it. post_toolchange below still emits M109
// to the new filament's print temp, so per-lane temperature differences
// (e.g. PLA lane → PETG lane on the same AFC manifold) are still handled.
auto same_physical_extruder = [&](int a, int b) {
const auto& pem = m_config.physical_extruder_map.values;
return !pem.empty() && a >= 0 && b >= 0
&& a < (int)pem.size() && b < (int)pem.size()
&& pem[a] == pem[b];
};
if (m_ooze_prevention.enable && m_writer.filament() != nullptr
&& !same_physical_extruder(m_writer.filament()->id(), (int)new_filament_id))
gcode += m_ooze_prevention.pre_toolchange(*this);
// BBS