mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-06-13 23:43:03 +00:00
Enhance tool change handling in GCode: Introduce support for local-z unplanned tool changes in the WipeTowerIntegration class. Update tool_change method to accommodate an additional parameter for local_z_unplanned, allowing for more flexible tool change management during Local-Z phase-b operations. Adjust related logic in process_layer to ensure proper synchronization with wipe tower planning.
This commit is contained in:
@@ -922,10 +922,79 @@ std::string WipeTowerIntegration::prime(GCode& gcodegen)
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string WipeTowerIntegration::tool_change(GCode& gcodegen, int extruder_id, bool finish_layer)
|
||||
std::string WipeTowerIntegration::tool_change(GCode& gcodegen, int extruder_id, bool finish_layer, bool local_z_unplanned)
|
||||
{
|
||||
std::string gcode;
|
||||
|
||||
auto emit_local_z_unplanned_toolchange = [&]() -> std::string {
|
||||
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<int>(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<int>(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"
|
||||
<< " 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;
|
||||
};
|
||||
|
||||
if (local_z_unplanned)
|
||||
return emit_local_z_unplanned_toolchange();
|
||||
|
||||
assert(m_layer_idx >= 0);
|
||||
if (m_layer_idx >= (int) m_tool_changes.size())
|
||||
return gcode;
|
||||
@@ -4568,7 +4637,9 @@ LayerResult GCode::process_layer(const Print& print,
|
||||
std::vector<LocalZPassBucket> pass_buckets;
|
||||
};
|
||||
|
||||
const bool local_z_perimeter_runtime_supported = !has_wipe_tower && !is_anything_overridden;
|
||||
// Local-Z phase-b can run with wipe tower enabled, but wiping-overrides remain unsupported
|
||||
// because that flow emits a separate purge/print pass that is not Local-Z aware yet.
|
||||
const bool local_z_perimeter_runtime_supported = !is_anything_overridden;
|
||||
bool local_z_phase_b_requested_for_layer = false;
|
||||
std::vector<LocalZLayerContext> local_z_layer_contexts;
|
||||
std::vector<std::unique_ptr<ExtrusionEntityCollection>> local_z_clipped_collections;
|
||||
@@ -4718,7 +4789,7 @@ LayerResult GCode::process_layer(const Print& print,
|
||||
<< " print_z=" << print_z
|
||||
<< " wipe_tower=" << has_wipe_tower
|
||||
<< " wiping_overrides=" << is_anything_overridden;
|
||||
gcode += "; local-z perimeter phase-b disabled for this layer (wipe tower or wiping overrides active)\n";
|
||||
gcode += "; local-z perimeter phase-b disabled for this layer (wiping overrides active)\n";
|
||||
} else if (local_z_phase_b_requested_for_layer && !local_z_perimeter_phase_b_enabled) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Local-Z phase-b requested but no eligible contexts"
|
||||
<< " print_z=" << print_z
|
||||
@@ -5119,6 +5190,9 @@ LayerResult GCode::process_layer(const Print& print,
|
||||
}
|
||||
|
||||
if (!local_z_pass_refs.empty()) {
|
||||
const int local_z_phase_b_start_extruder =
|
||||
(has_wipe_tower && m_writer.extruder() != nullptr) ? int(m_writer.extruder()->id()) : -1;
|
||||
bool local_z_phase_b_changed_extruder = false;
|
||||
BOOST_LOG_TRIVIAL(info) << "Local-Z phase-b emitting"
|
||||
<< " print_z=" << print_z
|
||||
<< " perimeter_passes=" << local_z_pass_refs.size();
|
||||
@@ -5152,7 +5226,13 @@ LayerResult GCode::process_layer(const Print& print,
|
||||
if (objects_by_extruder.empty())
|
||||
continue;
|
||||
|
||||
gcode += this->set_extruder(local_extruder_id, pass_plan.print_z);
|
||||
if (has_wipe_tower && m_writer.need_toolchange(local_extruder_id))
|
||||
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);
|
||||
} else {
|
||||
gcode += this->set_extruder(local_extruder_id, pass_plan.print_z);
|
||||
}
|
||||
if (std::abs(m_writer.get_position().z() - pass_z) > EPSILON) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Local-Z pass z restore"
|
||||
<< " print_z=" << print_z
|
||||
@@ -5200,6 +5280,27 @@ LayerResult GCode::process_layer(const Print& print,
|
||||
m_last_layer_z = saved_last_layer_z;
|
||||
}
|
||||
|
||||
// Keep wipe tower planning synchronized with the normal per-layer extruder loop:
|
||||
// Local-Z may do extra toolchanges, but wipe tower was planned against the layer loop order.
|
||||
// Restore the pre-pass tool so wipe tower tool_change() consumes the expected sequence.
|
||||
if (has_wipe_tower && local_z_phase_b_changed_extruder) {
|
||||
if (local_z_phase_b_start_extruder >= 0 &&
|
||||
m_writer.need_toolchange(static_cast<unsigned int>(local_z_phase_b_start_extruder))) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Local-Z phase-b restoring pre-pass extruder for wipe tower"
|
||||
<< " print_z=" << print_z
|
||||
<< " restore_extruder=" << local_z_phase_b_start_extruder;
|
||||
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);
|
||||
} else {
|
||||
gcode += this->set_extruder(static_cast<unsigned int>(local_z_phase_b_start_extruder), print_z);
|
||||
}
|
||||
} else if (local_z_phase_b_start_extruder < 0) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Local-Z phase-b cannot restore pre-pass extruder (undefined writer state)"
|
||||
<< " print_z=" << print_z;
|
||||
}
|
||||
}
|
||||
|
||||
const double nominal_layer_z = print_z + m_config.z_offset.value;
|
||||
if (std::abs(m_writer.get_position().z() - nominal_layer_z) > EPSILON) {
|
||||
gcode += this->retract(false, false, LiftType::NormalLift);
|
||||
|
||||
@@ -98,7 +98,9 @@ public:
|
||||
|
||||
std::string prime(GCode &gcodegen);
|
||||
void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; }
|
||||
std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer);
|
||||
// If local_z_unplanned is true, emit a wipe/toolchange without consuming the preplanned
|
||||
// per-layer wipe-tower sequence (used by Local-Z phase-b extra toolchanges).
|
||||
std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer, bool local_z_unplanned = false);
|
||||
bool is_empty_wipe_tower_gcode(GCode &gcodegen, int extruder_id, bool finish_layer);
|
||||
std::string finalize(GCode &gcodegen);
|
||||
std::vector<float> used_filament_length() const;
|
||||
|
||||
Reference in New Issue
Block a user