Add wipe_tower_type printer setting to replace vendor-based wipe tower selection

Previously, wipe tower behavior was determined by checking if the printer
  was a QIDI vendor. This introduces a configurable enum (Type 1 / Type 2)
  so any printer can select its wipe tower implementation. BBL printers
  remain hardcoded to Type 1. Qidi profiles default to Type 1.
This commit is contained in:
SoftFever
2026-03-15 22:19:53 +08:00
parent 494601eea5
commit d58d9be07b
18 changed files with 63 additions and 39 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "Qidi",
"version": "02.03.02.51",
"version": "02.03.02.52",
"force_update": "0",
"description": "Qidi configurations",
"machine_model_list": [

View File

@@ -64,6 +64,7 @@
"scan_first_layer": "0",
"silent_mode": "0",
"single_extruder_multi_material": "1",
"wipe_tower_type": "type1",
"support_air_filtration": ["1"],
"support_box_temp_control": "1",
"support_chamber_temp_control": "1",

View File

@@ -128,6 +128,7 @@
"scan_first_layer": "0",
"silent_mode": "0",
"single_extruder_multi_material": "1",
"wipe_tower_type": "type1",
"support_air_filtration": [
"1"
],

View File

@@ -118,6 +118,7 @@
],
"silent_mode": "0",
"single_extruder_multi_material": "1",
"wipe_tower_type": "type1",
"change_filament_gcode": "",
"machine_pause_gcode": "M25 ;pause print",
"wipe": [

View File

@@ -9,6 +9,7 @@
"change_filament_gcode": "",
"machine_pause_gcode": "M0",
"support_chamber_temp_control": "1",
"wipe_tower_type": "type1",
"filament_tower_interface_pre_extrusion_dist": ["10"],
"filament_tower_interface_pre_extrusion_length": ["0"],
"filament_tower_ironing_area": ["4"],

View File

@@ -1456,7 +1456,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
std::string WipeTowerIntegration::prime(GCode &gcodegen)
{
std::string gcode;
if (!gcodegen.is_BBL_Printer() && !gcodegen.is_QIDI_Printer()) {
if (gcodegen.wipe_tower_type() == WipeTowerType::Type2) {
for (const WipeTower::ToolChangeResult &tcr : m_priming) {
if (!tcr.extrusions.empty())
gcode += append_tcr2(gcodegen, tcr, tcr.new_tool);
@@ -1472,7 +1472,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
assert(m_layer_idx >= 0);
if (m_layer_idx >= (int) m_tool_changes.size())
return gcode;
if (!gcodegen.is_BBL_Printer() && !gcodegen.is_QIDI_Printer()) {
if (gcodegen.wipe_tower_type() == WipeTowerType::Type2) {
if (gcodegen.writer().need_toolchange(extruder_id) || finish_layer) {
if (m_layer_idx < (int) m_tool_changes.size()) {
if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size()))
@@ -1561,7 +1561,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
std::string WipeTowerIntegration::finalize(GCode &gcodegen)
{
std::string gcode;
if (!gcodegen.is_BBL_Printer() && !gcodegen.is_QIDI_Printer()) {
if (gcodegen.wipe_tower_type() == WipeTowerType::Type2) {
if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON)
gcode += gcodegen.change_layer(m_final_purge.print_z);
gcode += append_tcr2(gcodegen, m_final_purge, -1);
@@ -1983,11 +1983,11 @@ bool GCode::is_BBL_Printer()
return false;
}
bool GCode::is_QIDI_Printer()
WipeTowerType GCode::wipe_tower_type()
{
if (m_curr_print)
return m_curr_print->is_QIDI_printer();
return false;
return m_curr_print->wipe_tower_type();
return WipeTowerType::Type2;
}
void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb)
@@ -2400,7 +2400,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// modifies m_silent_time_estimator_enabled
DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled);
const bool is_bbl_printers = print.is_BBL_printer();
const bool is_qidi_printers = print.is_QIDI_printer();
const WipeTowerType wipe_tower_type = print.wipe_tower_type();
m_calib_config.clear();
// resets analyzer's tracking data
m_last_height = 0.f;
@@ -2701,7 +2701,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
throw Slic3r::SlicingError(_(L("No object can be printed. Maybe too small")));
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
// Orca: support all extruder priming
initial_extruder_id = (!is_bbl_printers && has_wipe_tower && !print.config().single_extruder_multi_material_priming && !is_qidi_printers) ?
initial_extruder_id = (wipe_tower_type == WipeTowerType::Type2 && has_wipe_tower && !print.config().single_extruder_multi_material_priming) ?
// The priming towers will be skipped.
tool_ordering.all_extruders().back() :
// Don't skip the priming towers.
@@ -2814,7 +2814,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
this->placeholder_parser().set("current_object_idx", 0);
// For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided.
this->placeholder_parser().set("has_wipe_tower", has_wipe_tower);
this->placeholder_parser().set("has_single_extruder_multi_material_priming", !is_bbl_printers && has_wipe_tower && print.config().single_extruder_multi_material_priming);
this->placeholder_parser().set("has_single_extruder_multi_material_priming", wipe_tower_type == WipeTowerType::Type2 && has_wipe_tower && print.config().single_extruder_multi_material_priming);
this->placeholder_parser().set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change).
this->placeholder_parser().set("num_extruders", int(print.config().nozzle_diameter.values.size()));
this->placeholder_parser().set("retract_length", new ConfigOptionFloats(print.config().retraction_length));
@@ -3134,7 +3134,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
}
// Orca: support extruder priming
if (is_bbl_printers || ! (has_wipe_tower && print.config().single_extruder_multi_material_priming))
if (wipe_tower_type != WipeTowerType::Type2 || ! (has_wipe_tower && print.config().single_extruder_multi_material_priming))
{
// Set initial extruder only after custom start G-code.
// Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
@@ -3290,7 +3290,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
//BBS
file.write(m_writer.travel_to_z(initial_layer_print_height + m_config.z_offset.value, "Move to the first layer height"));
if (!is_bbl_printers && print.config().single_extruder_multi_material_priming) {
if (wipe_tower_type == WipeTowerType::Type2 && print.config().single_extruder_multi_material_priming) {
file.write(m_wipe_tower->prime(*this));
// Verify, whether the print overaps the priming extrusions.
BoundingBoxf bbox_print(get_print_extrusions_extents(print));

View File

@@ -251,7 +251,7 @@ public:
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false, int toolchange_temp_override = -1);
bool is_BBL_Printer();
bool is_QIDI_Printer();
WipeTowerType wipe_tower_type();
// SoftFever
std::string set_object_info(Print* print);

View File

@@ -381,7 +381,7 @@ void ToolOrdering::sort_and_build_data(const PrintObject& object , unsigned int
// (print->config().print_sequence == PrintSequence::ByObject is true).
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material)
{
m_is_BBL_printer = object.print()->is_BBL_printer();
m_wipe_tower_type = object.print()->wipe_tower_type();
m_print_full_config = &object.print()->full_print_config();
m_print_object_ptr = &object;
m_print = const_cast<Print*>(object.print());
@@ -427,7 +427,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
// (print->config().print_sequence == PrintSequence::ByObject is false).
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
{
m_is_BBL_printer = print.is_BBL_printer();
m_wipe_tower_type = print.wipe_tower_type();
m_print_full_config = &print.full_print_config();
m_print = const_cast<Print *>(&print); // for update the context of print
m_print_config_ptr = &print.config();
@@ -1227,7 +1227,7 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) {
std::vector<float> flush_matrix(cast<float>(get_flush_volumes_matrix(print_config->flush_volumes_matrix.values, nozzle_id, nozzle_nums)));
std::vector<std::vector<float>> wipe_volumes;
if ((print_config->purge_in_prime_tower && print_config->single_extruder_multi_material) || m_is_BBL_printer) {
if ((print_config->purge_in_prime_tower && print_config->single_extruder_multi_material) || m_wipe_tower_type == WipeTowerType::Type1) {
for (unsigned int i = 0; i < number_of_extruders; ++i)
wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders));
} else {

View File

@@ -279,7 +279,7 @@ private:
const PrintObject* m_print_object_ptr = nullptr;
Print* m_print;
bool m_sorted = false;
bool m_is_BBL_printer = false;
WipeTowerType m_wipe_tower_type = WipeTowerType::Type1;
FilamentChangeStats m_stats_by_single_extruder;
FilamentChangeStats m_stats_by_multi_extruder_curr;

View File

@@ -1028,7 +1028,7 @@ static std::vector<std::string> s_Preset_printer_options {
"use_relative_e_distances", "extruder_type", "use_firmware_retraction", "printer_notes",
"grab_length", "support_object_skip_flush", "physical_extruder_map",
"cooling_tube_retraction",
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "purge_in_prime_tower", "enable_filament_ramming",
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "wipe_tower_type", "purge_in_prime_tower", "enable_filament_ramming",
"z_offset",
"disable_m73", "preferred_orientation", "emit_machine_limits_to_gcode", "pellet_modded_printer", "support_multi_bed_types", "default_bed_type", "bed_mesh_min","bed_mesh_max","bed_mesh_probe_distance", "adaptive_bed_mesh_margin", "enable_long_retraction_when_cut","long_retractions_when_cut","retraction_distances_when_cut",
"bed_temperature_formula", "nozzle_flush_dataset"

View File

@@ -145,7 +145,6 @@ public:
VendorType get_current_vendor_type();
// Vendor related handy functions
bool is_bbl_vendor() { return get_current_vendor_type() == VendorType::Marlin_BBL; }
bool is_qidi_vendor() { return get_current_vendor_type() == VendorType::Klipper_Qidi; }
// Whether using bbl network for print upload
bool use_bbl_network();

View File

@@ -315,6 +315,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "prime_tower_enable_framework"
|| opt_key == "prime_tower_width"
|| opt_key == "prime_tower_brim_width"
|| opt_key == "wipe_tower_type"
|| opt_key == "prime_tower_skip_points"
|| opt_key == "prime_tower_flat_ironing"
|| opt_key == "enable_tower_interface_features"
@@ -3127,10 +3128,10 @@ void Print::_make_wipe_tower()
// BBS
const unsigned int number_of_extruders = (unsigned int)(m_config.filament_colour.values.size());
const auto bUseWipeTower2 = is_BBL_printer() || is_QIDI_printer() ? false : true;
const bool is_wipe_tower_type2 = this->wipe_tower_type() == WipeTowerType::Type2;
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int) -1, bUseWipeTower2 ? true : false);
m_wipe_tower_data.tool_ordering.sort_and_build_data(*this, (unsigned int)-1, bUseWipeTower2 ? true : false);
m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int) -1, is_wipe_tower_type2);
m_wipe_tower_data.tool_ordering.sort_and_build_data(*this, (unsigned int)-1, is_wipe_tower_type2);
if (!m_wipe_tower_data.tool_ordering.has_wipe_tower())
// Don't generate any wipe tower.
@@ -3173,7 +3174,7 @@ void Print::_make_wipe_tower()
}
this->throw_if_canceled();
if (!bUseWipeTower2) {
if (!is_wipe_tower_type2) {
// in BBL machine, wipe tower is only use to prime extruder. So just use a global wipe volume.
WipeTower wipe_tower(m_config, m_plate_index, m_origin, m_wipe_tower_data.tool_ordering.first_extruder(),
m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z, m_wipe_tower_data.tool_ordering.all_extruders());
@@ -3311,7 +3312,7 @@ void Print::_make_wipe_tower()
wipe_volumes.push_back(std::vector<float>(flush_matrix.begin()+i*number_of_extruders, flush_matrix.begin()+(i+1)*number_of_extruders));
// Orca: itertate over wipe_volumes and change the non-zero values to the prime_volume
if ((!m_config.purge_in_prime_tower || !m_config.single_extruder_multi_material) && !is_BBL_printer() && !is_QIDI_printer()) {
if ((!m_config.purge_in_prime_tower || !m_config.single_extruder_multi_material) && is_wipe_tower_type2) {
for (unsigned int i = 0; i < number_of_extruders; ++i) {
for (unsigned int j = 0; j < number_of_extruders; ++j) {
if (wipe_volumes[i][j] > 0) {

View File

@@ -1066,8 +1066,7 @@ public:
//SoftFever
bool &is_BBL_printer() { return m_isBBLPrinter; }
const bool is_BBL_printer() const { return m_isBBLPrinter; }
bool &is_QIDI_printer() { return m_isQIDIPrinter; }
const bool is_QIDI_printer() const { return m_isQIDIPrinter; }
WipeTowerType wipe_tower_type() const { return is_BBL_printer() ? WipeTowerType::Type1 : m_config.wipe_tower_type.value; }
CalibMode& calib_mode() { return m_calib_params.mode; }
const CalibMode calib_mode() const { return m_calib_params.mode; }
void set_calib_params(const Calib_Params& params);
@@ -1135,7 +1134,6 @@ private:
//SoftFever
bool m_isBBLPrinter;
bool m_isQIDIPrinter;
// Ordered collections of extrusion paths to build skirt loops and brim.
ExtrusionEntityCollection m_skirt;

View File

@@ -187,6 +187,12 @@ static t_config_enum_values s_keys_map_NoiseType {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(NoiseType)
static t_config_enum_values s_keys_map_WipeTowerType {
{ "type1", int(WipeTowerType::Type1) },
{ "type2", int(WipeTowerType::Type2) }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(WipeTowerType)
static t_config_enum_values s_keys_map_FuzzySkinMode {
{ "displacement", int(FuzzySkinMode::Displacement) },
{ "extrusion", int(FuzzySkinMode::Extrusion) },
@@ -5484,6 +5490,17 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("wipe_tower_type", coEnum);
def->label = L("Wipe tower type");
def->tooltip = L("Choose the wipe tower implementation for multi-material prints. Type 1 is recommended for Bambu and Qidi printers with a filament cutter. Type 2 offers better compatibility with multi-tool and MMU printers and provide overall better compatibility.");
def->enum_keys_map = &ConfigOptionEnum<WipeTowerType>::get_enum_values();
def->enum_values.emplace_back("type1");
def->enum_values.emplace_back("type2");
def->enum_labels.emplace_back(L("Type 1"));
def->enum_labels.emplace_back(L("Type 2"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<WipeTowerType>(WipeTowerType::Type2));
def = this->add("purge_in_prime_tower", coBool);
def->label = L("Purge in prime tower");
def->tooltip = L("Purge remaining filament into prime tower.");

View File

@@ -58,6 +58,11 @@ enum class NoiseType {
Voronoi,
};
enum class WipeTowerType {
Type1,
Type2,
};
enum PrintHostType {
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htCrealityPrint, htObico, htFlashforge, htSimplyPrint, htElegooLink
};
@@ -490,6 +495,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrinterTechnology)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeFlavor)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(FuzzySkinType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(FuzzySkinMode)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(WipeTowerType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(NoiseType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(InfillPattern)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(IroningType)
@@ -1388,6 +1394,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloats, filament_multitool_ramming_flow))
((ConfigOptionFloats, filament_stamping_loading_speed))
((ConfigOptionFloats, filament_stamping_distance))
((ConfigOptionEnum<WipeTowerType>, wipe_tower_type))
((ConfigOptionBool, purge_in_prime_tower))
((ConfigOptionBool, enable_filament_ramming))
((ConfigOptionBool, support_multi_bed_types))

View File

@@ -681,7 +681,6 @@ StringObjectException BackgroundSlicingProcess::validate(StringObjectException *
assert(m_print == m_fff_print);
m_fff_print->is_BBL_printer() = wxGetApp().preset_bundle->is_bbl_vendor();
m_fff_print->is_QIDI_printer() = wxGetApp().preset_bundle->is_qidi_vendor();
return m_print->validate(warning, collison_polygons, height_polygons);
}

View File

@@ -807,6 +807,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
toggle_field("single_extruder_multi_material", !is_BBL_Printer);
auto bSEMM = preset_bundle->printers.get_edited_preset().config.opt_bool("single_extruder_multi_material");
const bool supports_wipe_tower_2 = !is_BBL_Printer && preset_bundle->printers.get_edited_preset().config.opt_enum<WipeTowerType>("wipe_tower_type") == WipeTowerType::Type2;
toggle_field("ooze_prevention", !bSEMM);
bool have_ooze_prevention = config->opt_bool("ooze_prevention");
@@ -831,17 +832,17 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
"wipe_tower_extra_spacing", "wipe_tower_max_purge_speed",
"wipe_tower_bridging", "wipe_tower_extra_flow",
"wipe_tower_no_sparse_layers"})
toggle_line(el, have_prime_tower && !is_BBL_Printer);
toggle_line(el, have_prime_tower && supports_wipe_tower_2);
WipeTowerWallType wipe_tower_wall_type = config->opt_enum<WipeTowerWallType>("wipe_tower_wall_type");
bool have_rib_wall = (wipe_tower_wall_type == WipeTowerWallType::wtwRib)&&have_prime_tower;
toggle_line("wipe_tower_cone_angle", have_prime_tower && !is_BBL_Printer && wipe_tower_wall_type == WipeTowerWallType::wtwCone);
toggle_line("wipe_tower_cone_angle", have_prime_tower && supports_wipe_tower_2 && wipe_tower_wall_type == WipeTowerWallType::wtwCone);
toggle_line("wipe_tower_extra_rib_length", have_rib_wall);
toggle_line("wipe_tower_rib_width", have_rib_wall);
toggle_line("wipe_tower_fillet_wall", have_rib_wall);
toggle_field("prime_tower_width", have_prime_tower && !(is_BBL_Printer && have_rib_wall));
toggle_field("prime_tower_width", have_prime_tower && (supports_wipe_tower_2 || have_rib_wall));
toggle_line("single_extruder_multi_material_priming", !bSEMM && have_prime_tower && !is_BBL_Printer);
toggle_line("single_extruder_multi_material_priming", !bSEMM && have_prime_tower && supports_wipe_tower_2);
toggle_line("prime_volume",have_prime_tower && (!purge_in_primetower || !bSEMM));

View File

@@ -4911,6 +4911,7 @@ if (is_marlin_flavor)
optgroup->append_single_option_line("bed_temperature_formula", "printer_basic_information_advanced#bed-temperature-type");
optgroup = page->new_optgroup(L("Wipe tower"), "param_tower");
optgroup->append_single_option_line("wipe_tower_type", "printer_multimaterial_wipe_tower");
optgroup->append_single_option_line("purge_in_prime_tower", "printer_multimaterial_wipe_tower#purge-in-prime-tower");
optgroup->append_single_option_line("enable_filament_ramming", "printer_multimaterial_wipe_tower#enable-filament-ramming");
@@ -5204,11 +5205,6 @@ void TabPrinter::toggle_options()
is_BBL_printer = wxGetApp().preset_bundle->is_bbl_vendor();
}
bool is_QIDI_printer = false;
if (m_preset_bundle) {
is_QIDI_printer = wxGetApp().preset_bundle->is_qidi_vendor();
}
bool have_multiple_extruders = true;
//m_extruders_count > 1;
//if (m_active_page->title() == "Custom G-code") {
@@ -5236,6 +5232,8 @@ void TabPrinter::toggle_options()
}
if (m_active_page->title() == L("Multimaterial")) {
const bool supports_wipe_tower_2 = !is_BBL_printer && m_config->opt_enum<WipeTowerType>("wipe_tower_type") == WipeTowerType::Type2;
toggle_line("wipe_tower_type", !is_BBL_printer);
// SoftFever: hide specific settings for BBL printer
for (auto el : {
"enable_filament_ramming",
@@ -5245,7 +5243,7 @@ void TabPrinter::toggle_options()
"extra_loading_move",
"high_current_on_filament_swap",
})
toggle_option(el, !is_BBL_printer && !is_QIDI_printer);
toggle_option(el, supports_wipe_tower_2);
auto bSEMM = m_config->opt_bool("single_extruder_multi_material");
if (!bSEMM && m_config->opt_bool("manual_filament_change")) {
@@ -5255,7 +5253,7 @@ void TabPrinter::toggle_options()
}
toggle_option("extruders_count", !bSEMM);
toggle_option("manual_filament_change", bSEMM);
toggle_option("purge_in_prime_tower", bSEMM && (!is_BBL_printer && !is_QIDI_printer));
toggle_option("purge_in_prime_tower", bSEMM && supports_wipe_tower_2);
}
wxString extruder_number;
long val = 1;