From 62074b409d6620ed5963eb59ff09be6bde9368ec Mon Sep 17 00:00:00 2001 From: Clifford Garwood Date: Mon, 8 Jun 2026 02:22:41 -0400 Subject: [PATCH] fix(gcode): skip ooze prevention for same-physical filament changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/libslic3r/GCode.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 744484765b..db958482c7 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -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