diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 591639685..389a67a9a 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -428,6 +428,8 @@ set(lisbslic3r_sources ClipperZUtils.hpp FlushVolPredictor.hpp FlushVolPredictor.cpp + FilamentGroup.hpp + FilamentGroup.cpp ) if (APPLE) diff --git a/src/libslic3r/FilamentGroup.cpp b/src/libslic3r/FilamentGroup.cpp new file mode 100644 index 000000000..2a2e6cc0d --- /dev/null +++ b/src/libslic3r/FilamentGroup.cpp @@ -0,0 +1,279 @@ +#include "FilamentGroup.hpp" +#include "GCode/ToolOrdering.hpp" + +namespace Slic3r +{ + int FilamentGroup::calc_filament_group(const std::vector>& layer_filaments) + { + std::setused_filaments; + for (const auto& lf : layer_filaments) + for (const auto& extruder : lf) + used_filaments.insert(extruder); + + m_filament_labels.resize(used_filaments.size()); + m_used_filaments = std::vector(used_filaments.begin(), used_filaments.end()); + std::sort(m_used_filaments.begin(), m_used_filaments.end()); + + if (m_filament_num <= 1) + return 0; + if (m_filament_num < 10) + return calc_filament_group_by_enum(layer_filaments); + else + return calc_filament_group_by_pam(layer_filaments,300); + + } + + int FilamentGroup::calc_filament_group_by_enum(const std::vector>& layer_filaments) + { + auto bit_count_one = [](int n) + { + int count = 0; + while (n != 0) + { + n &= n - 1; + count++; + } + return count; + }; + + uint64_t max_group_num = (1 << m_filament_num); + int best_cost = std::numeric_limits::max(); + std::vectorbest_label; + + for (uint64_t i = 0; i < max_group_num; ++i) { + int num_to_group_1 = bit_count_one(i); + if (num_to_group_1 > m_max_group_size[1] || (m_filament_num - num_to_group_1) > m_max_group_size[0]) + continue; + std::setgroup_0, group_1; + for (int j = 0; j < m_filament_num; ++j) { + if (i & (1 << j)) + group_1.insert(m_used_filaments[j]); + else + group_0.insert(m_used_filaments[j]); + } + + if (group_0.size() < m_max_group_size[0] && group_1.size() < m_max_group_size[1]){ + + std::vectorfilament_maps(m_filament_num); + for (int i = 0; i < m_filament_num; ++i) { + if (group_0.find(m_used_filaments[i]) != group_0.end()) + filament_maps[i] = 0; + if (group_1.find(m_used_filaments[i]) != group_1.end()) + filament_maps[i] = 1; + } + + int total_cost = reorder_filaments_for_minimum_flush_volume( + m_used_filaments, + filament_maps, + layer_filaments, + m_flush_matrix, + get_custom_seq, + nullptr + ); + + if (total_cost < best_cost) { + best_cost = total_cost; + best_label = filament_maps; + } + } + } + + m_filament_labels = best_label; + + return best_cost; + } + + int FilamentGroup::calc_filament_group_by_pam(const std::vector>& layer_filaments, int timeout_ms) + { + //calc pair counts + std::vector>count_matrix(m_filament_num,std::vector(m_filament_num)); + for (const auto& lf : layer_filaments) { + for (auto iter = lf.begin(); iter != lf.end(); ++iter) { + auto idx1 = std::find(m_used_filaments.begin(), m_used_filaments.end(), *iter)-m_used_filaments.begin(); + for (auto niter = std::next(iter); niter != lf.end(); ++niter) { + auto idx2 = std::find(m_used_filaments.begin(), m_used_filaments.end(), *niter) - m_used_filaments.begin(); + count_matrix[idx1][idx2] += 1; + count_matrix[idx2][idx1] += 1; + } + } + } + + //calc distance matrix + std::vector>distance_matrix(m_filament_num, std::vector(m_filament_num)); + for (size_t i = 0; i < m_used_filaments.size(); ++i) { + for (size_t j = 0; j < m_used_filaments.size(); ++j) { + if (i == j) + distance_matrix[i][j] = 0; + else { + //TODO: check m_flush_matrix + float max_val = std::max(m_flush_matrix[0][m_used_filaments[i]][m_used_filaments[j]], m_flush_matrix[0][m_used_filaments[j]][m_used_filaments[i]]); + float min_val = std::min(m_flush_matrix[0][m_used_filaments[i]][m_used_filaments[j]], m_flush_matrix[0][m_used_filaments[j]][m_used_filaments[i]]); + + double p = 0; + distance_matrix[i][j] = (max_val * p + min_val * (1 - p)) * count_matrix[i][j]; + } + } + } + + KMediods PAM(distance_matrix, m_filament_num,m_max_group_size); + PAM.fit(timeout_ms); + this->m_filament_labels = PAM.get_filament_labels(); + + int cost = reorder_filaments_for_minimum_flush_volume( + m_used_filaments, + this->m_filament_labels, + layer_filaments, + m_flush_matrix, + get_custom_seq, + nullptr + ); + + return cost; + } + + + void KMediods::fit( int timeout_ms) + { + std::vectorbest_medoids; + std::vectorbest_labels; + int best_cost = std::numeric_limits::max(); + + FlushTimeMachine T; + T.time_machine_start(); + + int count = 0; + while (true) + { + std::vectormedoids; + std::vectorlabels; + if (count == 0) + medoids = initialize(INIT_TYPE::Farthest); + else + medoids = initialize(INIT_TYPE::Random); + + labels = assign_label(medoids); + int cost = calc_cost(labels, medoids); + + for (int i = 0; i < m_filament_num; ++i) { + if (std::find(medoids.begin(), medoids.end(), i) != medoids.end()) + continue; + + for (int j = 0; j < 2; ++j) { + std::vector new_medoids = medoids; + new_medoids[j] = i; + std::vector new_labels = assign_label(new_medoids); + int new_cost = calc_cost(new_labels, new_medoids); + + if (new_cost < cost) + { + labels = new_labels; + cost = new_cost; + medoids = new_medoids; + } + } + } + + + if (cost < best_cost) + { + best_cost = cost; + best_labels = labels; + best_medoids = medoids; + } + count += 1; + + if (T.time_machine_end() > timeout_ms) + break; + } + + this->m_filament_labels = best_labels; + } + + std::vector KMediods::assign_label(const std::vector& medoids) const + { + std::vectorlabels(m_filament_num); + struct Comp { + bool operator()(const std::pair& a, const std::pair& b) { + return a.second > b.second; + } + }; + std::priority_queue, std::vector>,Comp>min_heap; + + for (int i = 0; i < m_filament_num; ++i) { + int distancec_to_0 = m_distance_matrix[i][medoids[0]]; + int distancec_to_1 = m_distance_matrix[i][medoids[1]]; + min_heap.push({ i,distancec_to_0 - distancec_to_1 }); + } + std::set group_0, group_1; + while (!min_heap.empty()) { + auto top = min_heap.top(); + min_heap.pop(); + if (group_0.size() < m_max_group_size[0] && (top.second <= 0 || group_1.size() >= m_max_group_size[1])) + group_0.insert(top.first); + else + group_1.insert(top.first); + + } + for (auto& item : group_0) + labels[item] = 0; + for (auto& item : group_1) + labels[item] = 1; + + return labels; + } + + int KMediods::calc_cost(const std::vector& labels, const std::vector& medoids) const + { + int total_cost = 0; + for (int i = 0; i < m_filament_num; ++i) + total_cost += m_distance_matrix[i][medoids[labels[i]]]; + return total_cost; + } + + std::vector KMediods::initialize(INIT_TYPE type) const + { + auto hash_func = [](int n1, int n2) { + return n1 * 100 + n2; + }; + srand(time(nullptr)); + std::vectorret; + if (type == INIT_TYPE::Farthest) { + //get the farthest items + int target_i=0,target_j=0,target_val=std::numeric_limits::min(); + for(int i=0;itarget_val){ + target_val=m_distance_matrix[i][j]; + target_i=i; + target_j=j; + } + } + } + ret.emplace_back(std::min(target_i, target_j)); + ret.emplace_back(std::max(target_i, target_j)); + } + else if (type == INIT_TYPE::Random) { + while (true) { + std::vectormedoids; + while (medoids.size() < 2) + { + int candidate = rand() % m_filament_num; + if (std::find(medoids.begin(), medoids.end(), candidate) == medoids.end()) + medoids.push_back(candidate); + } + std::sort(medoids.begin(), medoids.end()); + + if (m_medoids_set.find(hash_func(medoids[0], medoids[1])) != m_medoids_set.end() && m_medoids_set.size() != (m_filament_num * (m_filament_num - 1) / 2)) + continue; + else { + ret = medoids; + break; + } + } + } + m_medoids_set.insert(hash_func(ret[0],ret[1])); + return ret; + } +} + + diff --git a/src/libslic3r/FilamentGroup.hpp b/src/libslic3r/FilamentGroup.hpp new file mode 100644 index 000000000..3d14094d5 --- /dev/null +++ b/src/libslic3r/FilamentGroup.hpp @@ -0,0 +1,90 @@ +#ifndef FILAMENT_GROUP_HPP +#define FILAMENT_GROUP_HPP + +#include + +namespace Slic3r +{ + using FlushMatrix = std::vector>; + + struct FlushTimeMachine + { + private: + std::chrono::high_resolution_clock::time_point start; + + public: + void time_machine_start() + { + start = std::chrono::high_resolution_clock::now(); + } + + int time_machine_end() + { + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + return duration.count(); + } + }; + + + class FilamentGroup + { + public: + FilamentGroup(const std::vector& flush_matrix, const int filament_num, const std::vector& max_group_size) : + m_flush_matrix{ flush_matrix }, + m_filament_num{ filament_num }, + m_max_group_size{ max_group_size } + {} + + int calc_filament_group(const std::vector>& layer_filaments); + int calc_filament_group_by_enum(const std::vector>& layer_filaments); + int calc_filament_group_by_pam(const std::vector>& layer_filaments, int timeout_ms = 300); + + std::vector get_filament_map() const {return m_filament_labels;} + + private: + std::vectorm_flush_matrix; + int m_filament_num; + std::vectorm_max_group_size; + std::vectorm_used_filaments; + public: + std::optional&)>> get_custom_seq; + private: + std::vectorm_filament_labels; + std::vector>m_filament_orders; + + }; + + class KMediods + { + enum INIT_TYPE + { + Random = 0, + Farthest + }; + public: + KMediods(const std::vector>& distance_matrix, const int filament_num, const std::vector& max_group_size) : + m_distance_matrix{ distance_matrix }, + m_filament_num{ filament_num }, + m_max_group_size{ max_group_size } {} + + void fit(int timeout_ms = 300); + std::vectorget_filament_labels()const { + return m_filament_labels; + } + + private: + std::vectorinitialize(INIT_TYPE type)const; + std::vectorassign_label(const std::vector& medoids)const; + int calc_cost(const std::vector& labels, const std::vector& medoids)const; + private: + std::vector>m_distance_matrix; + int m_filament_num; + std::vectorm_max_group_size; + std::vectorm_used_filaments; + mutable std::setm_medoids_set; + private: + std::vectorm_filament_labels; + }; +} +#endif // !FILAMENT_GROUP_HPP diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index ea8885491..6beaabe85 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -3,7 +3,6 @@ #include "Layer.hpp" #include "ClipperUtils.hpp" #include "ParameterUtils.hpp" - // #define SLIC3R_DEBUG // Make assert active if SLIC3R_DEBUG @@ -94,7 +93,7 @@ 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, float* cost = nullptr) { - if (all_extruders.size() == 1) { + if (all_extruders.size() <= 1) { if (cost) *cost = 0; return all_extruders; @@ -104,7 +103,7 @@ std::vector get_extruders_order(const std::vector 1) { + if (all_extruders.size() > 1) { int begin_index = 0; auto iter = std::find(all_extruders.begin(), all_extruders.end(), start_extruder_id); if (iter != all_extruders.end()) { @@ -137,6 +136,75 @@ if (all_extruders.size() > 1) { #endif // OPTIMIZE } +int reorder_filaments_for_minimum_flush_volume(const std::vector&filament_lists, + const std::vector&filament_maps, + const std::vector>& layer_filaments, + const std::vector& flush_matrix, + std::optional&)>> get_custom_seq, + std::vector>* filament_sequences) +{ + int cost = 0; + + if (filament_sequences) { + filament_sequences->clear(); + filament_sequences->resize(layer_filaments.size()); + } + + std::vector>groups(2); + for (int i = 0; i < filament_maps.size(); ++i) { + if (filament_maps[i] == 0) + groups[0].insert(filament_lists[i]); + if (filament_maps[i] == 1) + groups[1].insert(filament_lists[i]); + } + + for (size_t idx = 0; idx < groups.size();++idx) { + std::optionalcurrent_extruder_id; + int layer = 0; + for (const auto& lf : layer_filaments) { + + std::vectorcustom_filament_seq; + if (get_custom_seq && (*get_custom_seq)(layer, custom_filament_seq) && !custom_filament_seq.empty()) { + std::vector unsign_custom_extruder_seq; + for (int extruder : custom_filament_seq) { + unsigned int unsign_extruder = static_cast(extruder) - 1; + auto it = std::find(lf.begin(), lf.end(), unsign_extruder); + if (it != lf.end()) { + unsign_custom_extruder_seq.emplace_back(unsign_extruder); + } + } + assert(lf.size() == unsign_custom_extruder_seq.size()); + if (filament_sequences) + (*filament_sequences)[layer] = unsign_custom_extruder_seq; + + current_extruder_id = unsign_custom_extruder_seq.back(); + continue; + } + + std::vectorfilament_used_in_group; + for (const auto& filament : lf) { + if (groups[idx].find(filament) != groups[idx].end()) + filament_used_in_group.emplace_back(filament); + } + float tmp_cost = 0; + auto sequence = get_extruders_order(flush_matrix[idx], filament_used_in_group, current_extruder_id, &tmp_cost); + + assert(sequence.size()==filament_used_in_group.size()); + + if (filament_sequences) + (*filament_sequences)[layer].insert((*filament_sequences)[layer].end(), sequence.begin(), sequence.end()); + + if (!sequence.empty()) + current_extruder_id = sequence.back(); + cost += tmp_cost; + layer += 1; + } + } + + return cost; +} + + // Returns true in case that extruder a comes before b (b does not have to be present). False otherwise. bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const @@ -869,8 +937,6 @@ std::set, std::vector>> genera 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; @@ -914,14 +980,6 @@ std::vector ToolOrdering::get_recommended_filament_maps() 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"); @@ -943,106 +1001,34 @@ std::vector ToolOrdering::get_recommended_filament_maps() 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; + std::vectorused_filaments; + std::vector>layer_filaments; + for (auto& lt : m_layer_tools) { + layer_filaments.emplace_back(lt.extruders); + for (auto& extruder : lt.extruders) { + if (std::find(used_filaments.begin(), used_filaments.end(), extruder) == used_filaments.end()) + used_filaments.emplace_back(extruder); } } - return recommended_filament_maps; + std::sort(used_filaments.begin(), used_filaments.end()); + + FilamentGroup fg( + nozzle_flush_mtx, + used_filaments.size(), + { 16,16 } + ); + fg.get_custom_seq = get_custom_seq; + fg.calc_filament_group(layer_filaments); + + std::vectorret(number_of_extruders); + auto filament_map = fg.get_filament_map(); + for (size_t idx = 0; idx < filament_map.size(); ++idx) { + if (filament_map[idx]) + ret[used_filaments[idx]] = 1; + } + return ret; } // for print by object @@ -1192,119 +1178,6 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume() print_config = &(m_print_object_ptr->print()->config()); } - if (!print_config || m_layer_tools.empty()) - return; - - size_t nozzle_nums = print_config->nozzle_diameter.values.size(); - if (nozzle_nums > 1 && print_config->option>("filament_map_mode")->value == FilamentMapMode::fmmAuto) { - std::vector filament_maps = m_print->get_filament_maps(); - - if (print_config->print_sequence != PrintSequence::ByObject) { - filament_maps = get_recommended_filament_maps(); - if (filament_maps.empty()) // multi-extruder and one-color - return; - - std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value + 1; }); - m_print->update_filament_maps_to_config(filament_maps); - } - - std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value - 1; }); - 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); - // Extract purging volumes for each extruder pair: - 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)); - - auto extruders_to_hash_key = [](const std::vector& extruders, std::optionalinitial_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::optionalcurrent_extruder_id; - for (int i = 0; i < m_layer_tools.size(); ++i) { - LayerTools& lt = 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); - } - } - assert(lt.extruders.size() == unsign_custom_extruder_seq.size()); - lt.extruders = unsign_custom_extruder_seq; - current_extruder_id = lt.extruders.back(); - continue; - } - - // The algorithm complexity is O(n2*2^n) - if (i != 0) { - auto hash_key = extruders_to_hash_key(lt.extruders, current_extruder_id); - auto iter = m_tool_order_cache.find(hash_key); - if (iter == m_tool_order_cache.end()) { - lt.extruders = get_extruders_order(wipe_volumes, lt.extruders, current_extruder_id); - std::vector hash_val; - hash_val.reserve(lt.extruders.size()); - for (auto item : lt.extruders) - hash_val.emplace_back(static_cast(item)); - m_tool_order_cache[hash_key] = hash_val; - } - else { - std::vectorextruder_order; - extruder_order.reserve(iter->second.size()); - for (auto item : iter->second) - extruder_order.emplace_back(static_cast(item)); - lt.extruders = std::move(extruder_order); - } - } - current_extruder_id = lt.extruders.back(); - } -} - -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) { - print_config = &(m_print_object_ptr->print()->config()); - } - if (!print_config || m_layer_tools.empty()) return; @@ -1321,14 +1194,27 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume_multi_extruder(con nozzle_flush_mtx.emplace_back(wipe_volumes); } + + std::vectorfilament_maps(number_of_extruders, 0); + if (nozzle_nums > 1) { + filament_maps = m_print->get_filament_maps(); + if (print_config->print_sequence != PrintSequence::ByObject) { + filament_maps = get_recommended_filament_maps(); + if (filament_maps.empty()) + return; + std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value + 1; }); + m_print->update_filament_maps_to_config(filament_maps); + } + std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value - 1; }); + } - 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>filament_sequences; + std::vectorfilament_lists(number_of_extruders); + std::iota(filament_lists.begin(),filament_lists.end(),0); + std::vector>layer_filaments; + for (auto& lt : m_layer_tools) { + layer_filaments.emplace_back(lt.extruders); + } std::vector other_layers_seqs; const ConfigOptionInts * other_layers_print_sequence_op = print_config->option("other_layers_print_sequence"); @@ -1349,74 +1235,20 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume_multi_extruder(con } } return false; - }; + }; - std::optional current_extruder_id; + reorder_filaments_for_minimum_flush_volume( + filament_lists, + filament_maps, + layer_filaments, + nozzle_flush_mtx, + get_custom_seq, + &filament_sequences + ); - 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]; - 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(); - 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); - if (iter == m_tool_order_cache.end()) { - nozzle_filaments[nozzle_id] = get_extruders_order(nozzle_flush_mtx[nozzle_id], nozzle_filaments[nozzle_id], nozzle_to_cur_filaments[nozzle_id]); - 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; - } 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(); - } + for (size_t i = 0; i < filament_sequences.size(); ++i) + m_layer_tools[i].extruders = std::move(filament_sequences[i]); } - // Layers are marked for infinite skirt aka draft shield. Not all the layers have to be printed. void ToolOrdering::mark_skirt_layers(const PrintConfig &config, coordf_t max_layer_height) { diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index bde425797..f6552d92a 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -8,6 +8,9 @@ #include #include +#include "FilamentGroup.hpp" +#include "ExtrusionEntity.hpp" +#include "PrintConfig.hpp" namespace Slic3r { @@ -17,6 +20,13 @@ class LayerTools; namespace CustomGCode { struct Item; } class PrintRegion; + +int reorder_filaments_for_minimum_flush_volume(const std::vector& filament_lists, + const std::vector& filament_maps, + const std::vector>& layer_filaments, + const std::vector& flush_matrix, + std::optional&)>> get_custom_seq, + std::vector>* filament_sequences); // Object of this class holds information about whether an extrusion is printed immediately // after a toolchange (as part of infill/perimeter wiping) or not. One extrusion can be a part // of several copies - this has to be taken into account. @@ -200,7 +210,6 @@ private: void collect_extruder_statistics(bool prime_multi_material); std::vector get_recommended_filament_maps(); void reorder_extruders_for_minimum_flush_volume(); - 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);