diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index e223f20920..1d9aff376f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -930,66 +930,17 @@ std::string WipeTowerIntegration::tool_change(GCode& gcodegen, int extruder_id, if (extruder_id < 0 || !gcodegen.writer().need_toolchange(extruder_id)) return ""; - auto pick_toolchange_template_from_layer = - [&](int layer_idx) -> const WipeTower::ToolChangeResult* { - if (layer_idx < 0 || layer_idx >= int(m_tool_changes.size())) - return nullptr; - const auto& layer_changes = m_tool_changes[layer_idx]; - for (const WipeTower::ToolChangeResult& candidate : layer_changes) { - if (candidate.initial_tool != candidate.new_tool) - return &candidate; - } - return nullptr; - }; - - // Prefer a real toolchange template (initial_tool != new_tool). Using "empty grid" - // / finish-layer templates here can suppress [change_filament_gcode] injection and - // produce no actual emitted T-change. - const WipeTower::ToolChangeResult* template_tcr = nullptr; - if (m_layer_idx >= 0 && m_layer_idx < int(m_tool_changes.size())) - template_tcr = pick_toolchange_template_from_layer(m_layer_idx); - if (template_tcr == nullptr) { - const int start_back = std::min(m_layer_idx - 1, int(m_tool_changes.size()) - 1); - for (int back = start_back; back >= 0; --back) { - template_tcr = pick_toolchange_template_from_layer(back); - if (template_tcr != nullptr) - break; - } - } - if (template_tcr == nullptr) { - const int start_fwd = std::max(m_layer_idx + 1, 0); - for (int fwd = start_fwd; fwd < int(m_tool_changes.size()); ++fwd) { - template_tcr = pick_toolchange_template_from_layer(fwd); - if (template_tcr != nullptr) - break; - } - } - - if (template_tcr == nullptr) { - BOOST_LOG_TRIVIAL(warning) << "Wipe tower local-z unplanned toolchange fallback unavailable (no real template)," - << " using raw toolchange" - << " layer_idx=" << m_layer_idx - << " extruder_id=" << extruder_id; - return gcodegen.set_extruder(unsigned(extruder_id), - gcodegen.writer().get_position().z() - gcodegen.config().z_offset.value); - } - - WipeTower::ToolChangeResult tcr = *template_tcr; - tcr.initial_tool = gcodegen.writer().extruder() ? int(gcodegen.writer().extruder()->id()) : extruder_id; - tcr.new_tool = extruder_id; - tcr.print_z = float(gcodegen.writer().get_position().z() - gcodegen.config().z_offset.value); - tcr.priming = false; - tcr.force_travel = true; - - BOOST_LOG_TRIVIAL(debug) << "Wipe tower local-z unplanned toolchange emitted" + // Local-Z phase-b may introduce extra intra-layer toolchanges that were not part + // of the preplanned wipe tower sequence. Replaying a full wipe-tower template for + // each of those extra switches overprints the same tower layer at micro-step Zs + // and turns the tower into mush. Keep these extra switches local-Z-only by doing + // a direct toolchange here and leave the normal wipe tower plan untouched. + BOOST_LOG_TRIVIAL(debug) << "Local-Z unplanned toolchange using direct extruder switch" << " layer_idx=" << m_layer_idx << " extruder_id=" << extruder_id << " tool_change_idx=" << m_tool_change_idx; - std::string out; - out += "; local-z unplanned wipe-tower toolchange begin\n"; - out += append_tcr2(gcodegen, tcr, extruder_id); - out += "; local-z unplanned wipe-tower toolchange end\n"; - return out; + return gcodegen.set_extruder(unsigned(extruder_id), + gcodegen.writer().get_position().z() - gcodegen.config().z_offset.value); }; if (local_z_unplanned) @@ -5230,6 +5181,12 @@ LayerResult GCode::process_layer(const Print& print, local_z_phase_b_changed_extruder = true; if (has_wipe_tower && m_wipe_tower) { gcode += m_wipe_tower->tool_change(*this, int(local_extruder_id), false, true); + // Local-Z phase-b uses the wipe tower outside the normal per-layer + // extruder loop, so mirror the usual toolchange bookkeeping here. + // This forces the next object path to refresh WIDTH/HEIGHT tags + // after prime tower G-code, keeping the preview in sync with the + // actual local-Z pass height. + m_last_processor_extrusion_role = erWipeTower; } else { gcode += this->set_extruder(local_extruder_id, pass_plan.print_z); } @@ -5292,6 +5249,7 @@ LayerResult GCode::process_layer(const Print& print, gcode += "; local-z phase-b restore pre-pass extruder for wipe tower\n"; if (m_wipe_tower) { gcode += m_wipe_tower->tool_change(*this, local_z_phase_b_start_extruder, false, true); + m_last_processor_extrusion_role = erWipeTower; } else { gcode += this->set_extruder(static_cast(local_z_phase_b_start_extruder), print_z); } diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 3770a1ed14..df23e8a54e 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -1109,6 +1109,36 @@ static inline void compute_local_z_gradient_component_heights(int mix_b_percent, h_b = lo + pct_b * (hi - lo); } +static bool choose_local_z_start_with_component_a(const std::vector &pass_heights, + double expected_h_a, + double expected_h_b, + size_t cadence_index) +{ + double err_ab = 0.0; + double err_ba = 0.0; + for (size_t pass_i = 0; pass_i < pass_heights.size(); ++pass_i) { + const double expected_ab = (pass_i % 2) == 0 ? expected_h_a : expected_h_b; + const double expected_ba = (pass_i % 2) == 0 ? expected_h_b : expected_h_a; + err_ab += std::abs(pass_heights[pass_i] - expected_ab); + err_ba += std::abs(pass_heights[pass_i] - expected_ba); + } + + if (err_ab + 1e-6 < err_ba) + return true; + if (err_ba + 1e-6 < err_ab) + return false; + + // When the requested component heights are equal (for example 50/50), + // either A/B or B/A is numerically identical. Preserve the existing + // row cadence so equal-split layers keep the normal local-Z A/B/A/B + // sequence instead of flipping AB|BA between nominal layers. + if (std::abs(expected_h_a - expected_h_b) <= 1e-6) { + return (cadence_index % 2) == 0; + } + + return expected_h_a >= expected_h_b; +} + static std::vector build_local_z_alternating_pass_heights(double base_height, double lower_bound, double upper_bound, @@ -2259,16 +2289,10 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector