diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 5cb66ba5e5..ee03d6e959 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -187,6 +187,10 @@ static void apply_first_layer_order(const DynamicPrintConfig* config, std::vecto void ToolOrdering::handle_dontcare_extruder(const std::vector& tool_order_layer0) { + const PrintConfig* print_config = m_print_config_ptr; + if (!print_config && m_print_object_ptr) + print_config = &m_print_object_ptr->print()->config(); + if(m_layer_tools.empty() || tool_order_layer0.empty()) return; @@ -221,6 +225,8 @@ void ToolOrdering::handle_dontcare_extruder(const std::vector& too for (int i = 1; i < m_layer_tools.size(); i++) { LayerTools& lt = m_layer_tools[i]; + // Extruders in lt.extruders are already sorted. + if (lt.extruders.empty()) continue; if (lt.extruders.size() == 1 && lt.extruders.front() == 0) @@ -229,14 +235,23 @@ void ToolOrdering::handle_dontcare_extruder(const std::vector& too if (lt.extruders.front() == 0) // Pop the "don't care" extruder, the "don't care" region will be merged with the next one. lt.extruders.erase(lt.extruders.begin()); - // Reorder the extruders to start with the last one. - for (size_t i = 1; i < lt.extruders.size(); ++i) - if (lt.extruders[i] == last_extruder_id) { - // Move the last extruder to the front. - memmove(lt.extruders.data() + 1, lt.extruders.data(), i * sizeof(unsigned int)); - lt.extruders.front() = last_extruder_id; - break; + + if (print_config == nullptr + || print_config->toolchange_ordering == ToolChangeOrderingType::Default) + { + // Reorder the extruders to start with the last one. + for (size_t i = 1; i < lt.extruders.size(); ++i) { + if (lt.extruders[i] == last_extruder_id) { + // Move the last extruder to the front. + std::rotate( + lt.extruders.begin(), + lt.extruders.begin() + i, + lt.extruders.begin() + i + 1 + ); + break; + } } + } } last_extruder_id = lt.extruders.back(); } @@ -252,6 +267,10 @@ void ToolOrdering::handle_dontcare_extruder(const std::vector& too void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id) { + const PrintConfig* print_config = m_print_config_ptr; + if (!print_config && m_print_object_ptr) + print_config = &m_print_object_ptr->print()->config(); + if(m_layer_tools.empty()) return; if(last_extruder_id == (unsigned int)-1){ @@ -275,6 +294,8 @@ void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id) } for (LayerTools < : m_layer_tools) { + // Extruders in lt.extruders are already sorted. + if (lt.extruders.empty()) continue; if (lt.extruders.size() == 1 && lt.extruders.front() == 0) @@ -283,21 +304,30 @@ void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id) if (lt.extruders.front() == 0) // Pop the "don't care" extruder, the "don't care" region will be merged with the next one. lt.extruders.erase(lt.extruders.begin()); - // Reorder the extruders to start with the last one. - for (size_t i = 1; i < lt.extruders.size(); ++ i) - if (lt.extruders[i] == last_extruder_id) { - // Move the last extruder to the front. - memmove(lt.extruders.data() + 1, lt.extruders.data(), i * sizeof(unsigned int)); - lt.extruders.front() = last_extruder_id; - break; + + if (print_config == nullptr + || print_config->toolchange_ordering == ToolChangeOrderingType::Default) + { + // Reorder the extruders to start with the last one. + for (size_t i = 1; i < lt.extruders.size(); ++i) { + if (lt.extruders[i] == last_extruder_id) { + // Move the last extruder to the front. + std::rotate( + lt.extruders.begin(), + lt.extruders.begin() + i, + lt.extruders.begin() + i + 1 + ); + break; + } } + } if (lt == m_layer_tools[0]) { // On first layer with wipe tower, prefer a soluble extruder // at the beginning, so it is not wiped on the first layer. - if (m_print_config_ptr && m_print_config_ptr->enable_prime_tower) { + if (print_config && print_config->enable_prime_tower) { for (size_t i = 0; ifilament_soluble.get_at(lt.extruders[i]-1)) { // 1-based... + if (print_config->filament_soluble.get_at(lt.extruders[i]-1)) { // 1-based... std::swap(lt.extruders[i], lt.extruders.front()); break; } @@ -396,6 +426,7 @@ void ToolOrdering::sort_and_build_data(const PrintObject& object , unsigned int ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material) { m_print_full_config = &object.print()->full_print_config(); + m_print_config_ptr = &object.print()->config(); m_print_object_ptr = &object; m_print = const_cast(object.print()); if (object.layers().empty()) @@ -1329,8 +1360,11 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first if (!m_layer_tools.empty()) first_layer_filaments = m_layer_tools[0].extruders; + const bool use_cyclic_ordering = + (print_config->toolchange_ordering == ToolChangeOrderingType::Cyclic); + // other_layers_seq: the layer_idx and extruder_idx are base on 1 - auto get_custom_seq = [&other_layers_seqs, &reorder_first_layer, &first_layer_filaments](int layer_idx, std::vector& out_seq) -> bool { + auto get_custom_seq = [&other_layers_seqs, &reorder_first_layer, &first_layer_filaments, &layer_filaments, use_cyclic_ordering](int layer_idx, std::vector& out_seq) -> bool { if (!reorder_first_layer && layer_idx == 0) { out_seq.resize(first_layer_filaments.size()); std::transform(first_layer_filaments.begin(), first_layer_filaments.end(), out_seq.begin(), [](auto item) {return item + 1; }); @@ -1343,6 +1377,15 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first return true; } } + + if (use_cyclic_ordering && layer_idx >= 0 && size_t(layer_idx) < layer_filaments.size()) { + std::vector ordered = layer_filaments[size_t(layer_idx)]; + std::sort(ordered.begin(), ordered.end()); + out_seq.resize(ordered.size()); + std::transform(ordered.begin(), ordered.end(), out_seq.begin(), [](auto item) { return int(item) + 1; }); + return true; + } + return false; }; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 98a5a4fcb6..cf8469567f 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1272,6 +1272,7 @@ static std::vector s_Preset_print_options{ "wipe_tower_bridging", "wipe_tower_extra_flow", "single_extruder_multi_material_priming", + "toolchange_ordering", "wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a676d24cc0..257bbd9c31 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -330,6 +330,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "first_layer_print_sequence" || opt_key == "other_layers_print_sequence" || opt_key == "other_layers_print_sequence_nums" + || opt_key == "toolchange_ordering" || opt_key == "extruder_ams_count" || opt_key == "filament_map_mode" || opt_key == "filament_map" diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index dda24e4e5b..d8887b19e2 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -522,6 +522,12 @@ static t_config_enum_values s_keys_map_PerimeterGeneratorType{ }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PerimeterGeneratorType) +static t_config_enum_values s_keys_map_ToolChangeOrderingType { + { "default", int(ToolChangeOrderingType::Default) }, + { "cyclic", int(ToolChangeOrderingType::Cyclic) } +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ToolChangeOrderingType) + static const t_config_enum_values s_keys_map_ZHopType = { { "Auto Lift", zhtAuto }, { "Normal Lift", zhtNormal }, @@ -6076,6 +6082,22 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(false)); + def = this->add("toolchange_ordering", coEnum); + def->label = L("Toolchange ordering"); + def->category = L("Advanced"); + def->tooltip = L( + "Determines the order of tool changes on each layer.\n" + "- Default: Starts with the last used extruder to minimize tool changes.\n" + "- Cyclic: Uses a fixed tool sequence each layer. This sacrifices speed for better surface quality, as the extra toolchanges allow layers more time to cool." + ); + def->mode = comAdvanced; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.emplace_back("default"); + def->enum_values.emplace_back("cyclic"); + def->enum_labels.emplace_back(L("Default")); + def->enum_labels.emplace_back(L("Cyclic")); + def->set_default_value(new ConfigOptionEnum(ToolChangeOrderingType::Default)); + def = this->add("slice_closing_radius", coFloat); def->label = L("Slice gap closing radius"); def->category = L("Quality"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 3b8b809c78..fbeb9f0574 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -300,6 +300,12 @@ enum class PerimeterGeneratorType Arachne }; +enum class ToolChangeOrderingType +{ + Default, + Cyclic, +}; + // BBS enum OverhangFanThreshold { Overhang_threshold_none = 0, @@ -561,6 +567,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrintHostType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(AuthorizationType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(WipeTowerWallType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType) +CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ToolChangeOrderingType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PowerLossRecoveryMode) #undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS @@ -1413,6 +1420,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, single_extruder_multi_material)) ((ConfigOptionBool, manual_filament_change)) ((ConfigOptionBool, single_extruder_multi_material_priming)) + ((ConfigOptionEnum, toolchange_ordering)) ((ConfigOptionBool, wipe_tower_no_sparse_layers)) ((ConfigOptionString, change_filament_gcode)) ((ConfigOptionString, change_extrusion_role_gcode)) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index f1eb41b0b4..b3fc51c245 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2976,6 +2976,7 @@ void TabPrint::build() optgroup->append_single_option_line("flush_into_support", "multimaterial_settings_flush_options#flush-into-objects-support"); optgroup = page->new_optgroup(L("Advanced"), L"advanced"); optgroup->append_single_option_line("interlocking_beam", "multimaterial_settings_advanced#interlocking-beam"); + optgroup->append_single_option_line("toolchange_ordering", "multimaterial_settings_advanced#toolchange-ordering"); optgroup->append_single_option_line("interface_shells", "multimaterial_settings_advanced#interface-shells"); optgroup->append_single_option_line("mmu_segmented_region_max_width", "multimaterial_settings_advanced#maximum-width-of-segmented-region"); optgroup->append_single_option_line("mmu_segmented_region_interlocking_depth", "multimaterial_settings_advanced#interlocking-depth-of-segmented-region");