diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index b1c77d10f..20a0677dc 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -25,7 +25,7 @@ const static bool g_wipe_into_objects = false; // Shortest hamilton path problem -static std::vector solve_extruder_order(const std::vector>& wipe_volumes, std::vector all_extruders, std::optional start_extruder_id) +static std::vector solve_extruder_order(const std::vector>& wipe_volumes, std::vector all_extruders, std::optional start_extruder_id, float* min_cost) { bool add_start_extruder_flag = false; @@ -37,7 +37,7 @@ static std::vector solve_extruder_order(const std::vector solve_extruder_order(const std::vector cache[final_state][dst]) { cost = cache[final_state][dst]; + if (min_cost) + *min_cost = cost; final_dst = dst; } } @@ -90,11 +92,17 @@ static std::vector solve_extruder_order(const std::vector get_extruders_order(const std::vector> &wipe_volumes, std::vector all_extruders, std::optionalstart_extruder_id) +std::vector get_extruders_order(const std::vector> &wipe_volumes, std::vector all_extruders, std::optionalstart_extruder_id, float* cost = nullptr) { + if (all_extruders.size() == 1) { + if (cost) + *cost = 0; + return all_extruders; + } + #define USE_DP_OPTIMIZE #ifdef USE_DP_OPTIMIZE - return solve_extruder_order(wipe_volumes, all_extruders, start_extruder_id); + return solve_extruder_order(wipe_volumes, all_extruders, start_extruder_id, cost); #else if (all_extruders.size() > 1) { int begin_index = 0; @@ -827,14 +835,217 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material) } } -void ToolOrdering::reorder_extruders_for_minimum_flush_volume() +std::set, std::vector>> generate_combinations(const std::vector &extruders) { - bool is_multi_extruder = false; - if (is_multi_extruder) { - reorder_extruders_for_minimum_flush_volume_multi_extruder(); - return; + int n = extruders.size(); + std::vector flags(n); + std::set, std::vector>> unique_combinations; + + if (extruders.empty()) + return unique_combinations; + + for (int i = 1; i <= n / 2; ++i) { + std::fill(flags.begin(), flags.begin() + i, true); + std::fill(flags.begin() + i, flags.end(), false); + + do { + std::vector group1, group2; + for (int j = 0; j < n; ++j) { + if (flags[j]) { + group1.push_back(extruders[j]); + } else { + group2.push_back(extruders[j]); + } + } + + if (group1.size() > group2.size()) { std::swap(group1, group2); } + + unique_combinations.insert({group1, group2}); + + } while (std::prev_permutation(flags.begin(), flags.end())); } + return unique_combinations; +} + +using FlushMatrix = std::vector>; + +float get_flush_volume(const std::vector &filament_maps, const std::vector &extruders, const std::vector &matrix, size_t nozzle_nums) +{ + std::vector> nozzle_filaments; + nozzle_filaments.resize(nozzle_nums); + + for (unsigned int filament_id : extruders) { + nozzle_filaments[filament_maps[filament_id]].emplace_back(filament_id); + } + + float flush_volume = 0; + for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) { + for (size_t i = 0; i + 1 < nozzle_filaments[nozzle_id].size(); ++i) { + flush_volume += matrix[nozzle_id][nozzle_filaments[nozzle_id][i]][nozzle_filaments[nozzle_id][i+1]]; + } + } + + return flush_volume; +} + +std::vector ToolOrdering::get_recommended_filament_maps() +{ + const PrintConfig *print_config = m_print_config_ptr; + if (!print_config && m_print_object_ptr) { + print_config = &(m_print_object_ptr->print()->config()); + } + + if (!print_config || m_layer_tools.empty()) + return std::vector(); + + const unsigned int number_of_extruders = (unsigned int) (print_config->filament_colour.values.size() + EPSILON); + + // get flush matrix + std::vector nozzle_flush_mtx; + size_t nozzle_nums = print_config->nozzle_diameter.values.size(); + for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) { + std::vector flush_matrix(cast(get_flush_volumes_matrix(print_config->flush_volumes_matrix.values, nozzle_id, nozzle_nums))); + std::vector> wipe_volumes; + for (unsigned int i = 0; i < number_of_extruders; ++i) + wipe_volumes.push_back(std::vector(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders)); + + nozzle_flush_mtx.emplace_back(wipe_volumes); + } + + auto extruders_to_hash_key = [](const std::vector &extruders, std::optional initial_extruder_id) -> uint32_t { + uint32_t hash_key = 0; + // high 16 bit define initial extruder ,low 16 bit define extruder set + if (initial_extruder_id) hash_key |= (1 << (16 + *initial_extruder_id)); + for (auto item : extruders) hash_key |= (1 << item); + return hash_key; + }; + + std::vector other_layers_seqs; + const ConfigOptionInts * other_layers_print_sequence_op = print_config->option("other_layers_print_sequence"); + const ConfigOptionInt * other_layers_print_sequence_nums_op = print_config->option("other_layers_print_sequence_nums"); + if (other_layers_print_sequence_op && other_layers_print_sequence_nums_op) { + const std::vector &print_sequence = other_layers_print_sequence_op->values; + int sequence_nums = other_layers_print_sequence_nums_op->value; + other_layers_seqs = get_other_layers_print_sequence(sequence_nums, print_sequence); + } + + // other_layers_seq: the layer_idx and extruder_idx are base on 1 + auto get_custom_seq = [&other_layers_seqs](int layer_idx, std::vector &out_seq) -> bool { + for (size_t idx = other_layers_seqs.size() - 1; idx != size_t(-1); --idx) { + const auto &other_layers_seq = other_layers_seqs[idx]; + if (layer_idx + 1 >= other_layers_seq.first.first && layer_idx + 1 <= other_layers_seq.first.second) { + out_seq = other_layers_seq.second; + return true; + } + } + return false; + }; + + std::set extruders; + for (int i = 0; i < m_layer_tools.size(); ++i) { + LayerTools < = m_layer_tools[i]; + for (unsigned int extruder : lt.extruders) + extruders.insert(extruder); + } + + auto extruder_group = generate_combinations(std::vector(extruders.begin(), extruders.end())); + + std::vector recommended_filament_maps; + float min_flush_volume = std::numeric_limits::max(); + for (auto iter = extruder_group.begin(); iter != extruder_group.end(); ++iter) { + std::vector filament_maps; + filament_maps.resize(number_of_extruders); + for (unsigned int e : iter->first) { + filament_maps[e] = 0; + } + for (unsigned int e : iter->second) { + filament_maps[e] = 1; + } + + std::optional current_extruder_id; + std::vector> nozzle_to_cur_filaments; + nozzle_to_cur_filaments.resize(nozzle_nums); + + float flush_volume_cost = 0; + for (int i = 0; i < m_layer_tools.size(); ++i) { + LayerTools < = m_layer_tools[i]; + if (lt.extruders.empty()) + continue; + + std::vector custom_extruder_seq; + if (get_custom_seq(i, custom_extruder_seq) && !custom_extruder_seq.empty()) { + std::vector unsign_custom_extruder_seq; + for (int extruder : custom_extruder_seq) { + unsigned int unsign_extruder = static_cast(extruder) - 1; + auto it = std::find(lt.extruders.begin(), lt.extruders.end(), unsign_extruder); + if (it != lt.extruders.end()) { + unsign_custom_extruder_seq.emplace_back(unsign_extruder); + nozzle_to_cur_filaments[filament_maps[unsign_extruder]] = unsign_extruder; + } + } + assert(lt.extruders.size() == unsign_custom_extruder_seq.size()); + lt.extruders = unsign_custom_extruder_seq; + current_extruder_id = lt.extruders.back(); + flush_volume_cost += get_flush_volume(filament_maps, lt.extruders, nozzle_flush_mtx, nozzle_nums); + continue; + } + + // The algorithm complexity is O(n2*2^n) + if (i != 0) { + std::vector> nozzle_filaments; + nozzle_filaments.resize(nozzle_nums); + + for (unsigned int filament_id : lt.extruders) { + nozzle_filaments[filament_maps[filament_id]].emplace_back(filament_id); + } + + for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) { + auto hash_key = extruders_to_hash_key(nozzle_filaments[nozzle_id], nozzle_to_cur_filaments[nozzle_id]); + auto iter = m_tool_order_cache.find(hash_key); + // todo : the cache with flush cost + //if (iter == m_tool_order_cache.end()) { + float f_cost = 0; + nozzle_filaments[nozzle_id] = get_extruders_order(nozzle_flush_mtx[nozzle_id], nozzle_filaments[nozzle_id], nozzle_to_cur_filaments[nozzle_id], &f_cost); + std::vector hash_val; + hash_val.reserve(nozzle_filaments[nozzle_id].size()); + for (auto item : nozzle_filaments[nozzle_id]) + hash_val.emplace_back(static_cast(item)); + m_tool_order_cache[hash_key] = hash_val; + flush_volume_cost += f_cost; + //} else { + // std::vector extruder_order; + // extruder_order.reserve(iter->second.size()); + // for (auto item : iter->second) + // extruder_order.emplace_back(static_cast(item)); + // nozzle_filaments[nozzle_id] = std::move(extruder_order); + //} + nozzle_to_cur_filaments[nozzle_id] = nozzle_filaments[nozzle_id].back(); + } + + lt.extruders.clear(); + for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) { + lt.extruders.insert(lt.extruders.end(), nozzle_filaments[nozzle_id].begin(), nozzle_filaments[nozzle_id].end()); + } + } + current_extruder_id = lt.extruders.back(); + } + + if (flush_volume_cost == 0) { + recommended_filament_maps = filament_maps; + break; + } + + if (flush_volume_cost < min_flush_volume) { + min_flush_volume = flush_volume_cost; + recommended_filament_maps = filament_maps; + } + } + return recommended_filament_maps; +} + +void ToolOrdering::reorder_extruders_for_minimum_flush_volume() +{ const PrintConfig *print_config = m_print_config_ptr; if (!print_config && m_print_object_ptr) { print_config = &(m_print_object_ptr->print()->config()); @@ -843,6 +1054,13 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume() if (!print_config || m_layer_tools.empty()) return; + size_t nozzle_nums = print_config->nozzle_diameter.values.size(); + if (nozzle_nums > 1) { + std::vector filament_maps = get_recommended_filament_maps(); + reorder_extruders_for_minimum_flush_volume_multi_extruder(filament_maps); + return; + } + // Get wiping matrix to get number of extruders and convert vector to vector: std::vector flush_matrix(cast(print_config->flush_volumes_matrix.values)); const unsigned int number_of_extruders = (unsigned int) (sqrt(flush_matrix.size()) + EPSILON); @@ -928,7 +1146,7 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume() } } -void ToolOrdering::reorder_extruders_for_minimum_flush_volume_multi_extruder() +void ToolOrdering::reorder_extruders_for_minimum_flush_volume_multi_extruder(const std::vector& filament_maps) { const PrintConfig *print_config = m_print_config_ptr; if (!print_config && m_print_object_ptr) { @@ -939,14 +1157,6 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume_multi_extruder() return; const unsigned int number_of_extruders = (unsigned int) (print_config->filament_colour.values.size() + EPSILON); - // todo multi_extruders: get filament_maps - std::vector filament_maps; - for (int i = 0; i <= number_of_extruders / 2; ++i) { - filament_maps.push_back(1); - } - for (int i = number_of_extruders / 2 + 1; i < number_of_extruders; ++i) { - filament_maps.push_back(2); - } using FlushMatrix = std::vector>; size_t nozzle_nums = print_config->nozzle_diameter.values.size(); @@ -992,6 +1202,7 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume_multi_extruder() std::optional current_extruder_id; std::vector> nozzle_to_cur_filaments; + nozzle_to_cur_filaments.resize(nozzle_nums); for (int i = 0; i < m_layer_tools.size(); ++i) { LayerTools < = m_layer_tools[i]; @@ -1021,7 +1232,7 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume_multi_extruder() nozzle_filaments.resize(nozzle_nums); for (unsigned int filament_id : lt.extruders) { - nozzle_filaments[filament_maps[filament_id] - 1].emplace_back(filament_id); + nozzle_filaments[filament_maps[filament_id]].emplace_back(filament_id); } for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index ae4a329ea..cc10b7ffe 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -196,8 +196,9 @@ private: void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_layer_height); void mark_skirt_layers(const PrintConfig &config, coordf_t max_layer_height); void collect_extruder_statistics(bool prime_multi_material); + std::vector ToolOrdering::get_recommended_filament_maps(); void reorder_extruders_for_minimum_flush_volume(); - void reorder_extruders_for_minimum_flush_volume_multi_extruder(); + void reorder_extruders_for_minimum_flush_volume_multi_extruder(const std::vector &filament_maps); // BBS std::vector generate_first_layer_tool_order(const Print& print); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 3ee054cbe..33c40ec08 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2466,17 +2466,18 @@ void Print::_make_wipe_tower() int nozzle_id = filament_maps[filament_id] - 1; unsigned int pre_filament_id = nozzle_cur_filament_ids[nozzle_id]; - if (pre_filament_id == filament_id) - continue; - float volume_to_purge = multi_extruder_flush[nozzle_id][pre_filament_id][filament_id]; - volume_to_purge *= m_config.flush_multiplier.get_at(nozzle_id); - volume_to_purge = pre_filament_id == -1 ? 0 : + float volume_to_purge = 0; + if (pre_filament_id != unsigned int(-1) && pre_filament_id != filament_id) { + volume_to_purge = multi_extruder_flush[nozzle_id][pre_filament_id][filament_id]; + volume_to_purge *= m_config.flush_multiplier.get_at(nozzle_id); + volume_to_purge = pre_filament_id == -1 ? 0 : layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_filament_id, filament_id, volume_to_purge); + } + wipe_tower.plan_toolchange((float) layer_tools.print_z, (float) layer_tools.wipe_tower_layer_height, current_filament_id, filament_id, m_config.prime_volume, volume_to_purge); current_filament_id = filament_id; - nozzle_cur_filament_ids[nozzle_id] = filament_id; } layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 44bcacf88..b7d86e875 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -13283,10 +13283,12 @@ void Plater::on_config_change(const DynamicPrintConfig &config) //bool print_sequence_changed = false; t_config_option_keys diff_keys = p->config->diff(config); - std::string old_model_id; - auto * printer_model = p->config->opt("printer_model"); - if (printer_model != nullptr && !printer_model->value.empty()) { - old_model_id = printer_model->value; + size_t old_nozzle_size = 1, new_nozzle_size = 1; + auto * opt_old = p->config->option("nozzle_diameter"); + auto * opt_new = config.option("nozzle_diameter"); + if (opt_old && opt_new) { + old_nozzle_size = opt_old->values.size(); + new_nozzle_size = opt_new->values.size(); } for (auto opt_key : diff_keys) { @@ -13347,7 +13349,9 @@ void Plater::on_config_change(const DynamicPrintConfig &config) } else if (opt_key == "printer_model") { p->reset_gcode_toolpaths(); - update_flush_volume_matrix(config, old_model_id); + if (old_nozzle_size != new_nozzle_size) { + update_flush_volume_matrix(old_nozzle_size, new_nozzle_size); + } // update to force bed selection(for texturing) bed_shape_changed = true; @@ -13372,32 +13376,44 @@ void Plater::on_config_change(const DynamicPrintConfig &config) this->p->schedule_background_process(); } -void Plater::update_flush_volume_matrix(const Slic3r::DynamicPrintConfig& config, const std::string& old_model_id) +void Plater::update_flush_volume_matrix(size_t old_nozzle_size, size_t new_nozzle_size) { - auto is_multi_extruder_printer = [](const std::string &model_id) { - return model_id == "Bambu Lab O1 Dual"; - }; + size_t nozzle_nums = wxGetApp().preset_bundle->get_printer_extruder_count(); + Slic3r::DynamicPrintConfig *project_config = &wxGetApp().preset_bundle->project_config; - auto *printer_model = config.opt("printer_model"); - if (printer_model != nullptr && !printer_model->value.empty()) { - size_t nozzle_nums = wxGetApp().preset_bundle->get_printer_extruder_count(); - if (!is_multi_extruder_printer(old_model_id) && is_multi_extruder_printer(printer_model->value)) { - Slic3r::DynamicPrintConfig *project_config = &wxGetApp().preset_bundle->project_config; - std::vector flush_volume_mtx = get_flush_volumes_matrix(project_config->option("flush_volumes_matrix")->values, -1, nozzle_nums); - flush_volume_mtx.insert(flush_volume_mtx.end(), flush_volume_mtx.begin(), flush_volume_mtx.end()); - set_flush_volumes_matrix(project_config->option("flush_volumes_matrix")->values, flush_volume_mtx, -1, nozzle_nums); + // Verify whether it is the first time start Studio + size_t filament_nums = project_config->option("filament_colour")->values.size(); + size_t flush_volume_size = project_config->option("flush_volumes_matrix")->values.size(); + if (filament_nums * filament_nums * new_nozzle_size == flush_volume_size) + return; - double first_value = project_config->option("flush_multiplier")->values.at(0); - project_config->option("flush_multiplier")->values = std::vector({first_value, 1.0}); + assert(nozzle_nums == new_nozzle_size); + if (old_nozzle_size < new_nozzle_size) { + std::vector flush_volume_mtx = get_flush_volumes_matrix(project_config->option("flush_volumes_matrix")->values, -1, old_nozzle_size); + std::vector first_flush_volume_mtx = get_flush_volumes_matrix(project_config->option("flush_volumes_matrix")->values, 0, old_nozzle_size); + + std::vector flush_multipliers = project_config->option("flush_multiplier")->values; + double first_flush_multiplier = project_config->option("flush_multiplier")->values.at(0); + + for (size_t i = old_nozzle_size; i < new_nozzle_size; ++i) { + flush_volume_mtx.insert(flush_volume_mtx.end(), first_flush_volume_mtx.begin(), first_flush_volume_mtx.end()); + flush_multipliers.push_back(first_flush_multiplier); } - else if (is_multi_extruder_printer(old_model_id) && !is_multi_extruder_printer(printer_model->value)) { - Slic3r::DynamicPrintConfig *project_config = &wxGetApp().preset_bundle->project_config; - std::vector flush_volume_mtx = get_flush_volumes_matrix(project_config->option("flush_volumes_matrix")->values, 0, nozzle_nums); - set_flush_volumes_matrix(project_config->option("flush_volumes_matrix")->values, flush_volume_mtx, -1, nozzle_nums); + set_flush_volumes_matrix(project_config->option("flush_volumes_matrix")->values, flush_volume_mtx, -1, new_nozzle_size); + project_config->option("flush_multiplier")->values = flush_multipliers; + } + else if (old_nozzle_size > new_nozzle_size) { + std::vector new_flush_volume_mtx; + std::vector flush_multipliers; + for (size_t i = 0; i < new_nozzle_size; ++i) { + std::vector flush_volume_mtx = get_flush_volumes_matrix(project_config->option("flush_volumes_matrix")->values, i, old_nozzle_size); + new_flush_volume_mtx.insert(new_flush_volume_mtx.end(), flush_volume_mtx.begin(), flush_volume_mtx.end()); - double first_value = project_config->option("flush_multiplier")->values.at(0); - project_config->option("flush_multiplier")->values = std::vector({first_value}); + double multiplier_val = project_config->option("flush_multiplier")->values.at(i); + flush_multipliers.push_back(multiplier_val); } + set_flush_volumes_matrix(project_config->option("flush_volumes_matrix")->values, new_flush_volume_mtx, -1, new_nozzle_size); + project_config->option("flush_multiplier")->values = flush_multipliers; } } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 59e6e2715..6e140044d 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -613,7 +613,7 @@ public: Mouse3DController& get_mouse3d_controller(); //BBS: update when switch muilti_extruder printer - void update_flush_volume_matrix(const Slic3r::DynamicPrintConfig &config, const std::string &old_model_id); + void update_flush_volume_matrix(size_t old_nozzle_size, size_t new_nozzle_size); //BBS: add bed exclude area void set_bed_shape() const; void set_bed_shape(const Pointfs& shape, const Pointfs& exclude_area, const double printable_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 2df09b68e..93f310e9f 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1597,7 +1597,8 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) } update(); - m_active_page->update_visibility(m_mode, true); + if (m_active_page) + m_active_page->update_visibility(m_mode, true); m_page_view->GetParent()->Layout(); } diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index 2de5848ec..a5633b9f0 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -315,7 +315,6 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector& matrix, con m_number_of_extruders = (unsigned int)extruder_colours.size(); generate_display_matrix(); - assert(m_display_matrix.size() == extruders.size() ^ 2); for (const std::string& color : extruder_colours) { //unsigned char rgb[3];