From 3cd587d09e066d6c6a27d466b7f663dfd5d674f3 Mon Sep 17 00:00:00 2001 From: "xun.zhang" Date: Thu, 26 Dec 2024 11:07:22 +0800 Subject: [PATCH] ENH: refine match mode group result 1.When there are identical materials, try to make the quantity of materials for each nozzle as similar as possible after grouping. 2.Fix an encoding error jira:NONE Signed-off-by: xun.zhang Change-Id: Iad77d7a995b9c25d004026f409c7e2ecbb8269db (cherry picked from commit 13d7cd06252678b6b084d17438e99ff808a4191d) --- src/libslic3r/FilamentGroup.cpp | 187 ++++++++++++++++++--------- src/libslic3r/FilamentGroupUtils.cpp | 32 ++++- src/libslic3r/FilamentGroupUtils.hpp | 6 +- 3 files changed, 159 insertions(+), 66 deletions(-) diff --git a/src/libslic3r/FilamentGroup.cpp b/src/libslic3r/FilamentGroup.cpp index 2cd8595ee..675f64a11 100644 --- a/src/libslic3r/FilamentGroup.cpp +++ b/src/libslic3r/FilamentGroup.cpp @@ -487,36 +487,60 @@ namespace Slic3r used_types.emplace_back(ctx.model_info.filament_types[f]); } - std::vector machine_filaments; - - for (size_t eid = 0; eid < ctx.machine_info.machine_filament_info.size(); ++eid) { + std::vector machine_filament_list; + std::map> machine_filament_set; + for (size_t eid = 0; eid < ctx.machine_info.machine_filament_info.size();++eid) { for (auto& filament : ctx.machine_info.machine_filament_info[eid]) { - if (!ctx.group_info.ignore_ext_filament || !filament.is_extended) { - machine_filaments.emplace_back(filament); - } + machine_filament_set[filament].insert(machine_filament_list.size()); + machine_filament_list.emplace_back(filament); } } - if (machine_filaments.empty()) + if (machine_filament_list.empty()) throw FilamentGroupException(FilamentGroupException::EmptyAmsFilaments,"Empty ams filament in For-Match mode."); - std::map unprintable_limits; // key stores filament idx in used_filament, value stores unprintable extruder - extract_unprintable_limit_indices(ctx.model_info.unprintable_filaments, used_filaments, unprintable_limits); + std::map unprintable_limit_indices; // key stores filament idx in used_filament, value stores unprintable extruder + extract_unprintable_limit_indices(ctx.model_info.unprintable_filaments, used_filaments, unprintable_limit_indices); - auto is_extruder_filament_compatible = [&unprintable_limits](int filament_idx, int extruder_id) { - auto iter = unprintable_limits.find(filament_idx); - if (iter != unprintable_limits.end() && iter->second == extruder_id) + std::vector> color_dist_matrix(used_colors.size(), std::vector(machine_filament_list.size())); + for (size_t i = 0; i < used_colors.size(); ++i) { + for (size_t j = 0; j < machine_filament_list.size(); ++j) { + color_dist_matrix[i][j] = calc_color_distance( + RGBColor(used_colors[i].r, used_colors[i].g, used_colors[i].b), + RGBColor(machine_filament_list[j].color.r, machine_filament_list[j].color.g, machine_filament_list[j].color.b) + ); + } + } + + std::vectorl_nodes(used_filaments.size()); + std::iota(l_nodes.begin(), l_nodes.end(), 0); + std::vectorr_nodes(machine_filament_list.size()); + std::iota(r_nodes.begin(), r_nodes.end(), 0); + std::vectormachine_filament_capacity(machine_filament_list.size()); + for (size_t idx = 0; idx < machine_filament_capacity.size(); ++idx) { + if (machine_filament_list[idx].is_extended) { + // extend filaments can at most map one filaments + machine_filament_capacity[idx] = ctx.group_info.ignore_ext_filament ? 0 : 1; + } + else + machine_filament_capacity[idx] = l_nodes.size(); // AMS filaments can map multiple filaments + } + + std::vectorextruder_filament_count(2, 0); + + auto is_extruder_filament_compatible = [&unprintable_limit_indices](int filament_idx, int extruder_id) { + auto iter = unprintable_limit_indices.find(filament_idx); + if (iter != unprintable_limit_indices.end() && iter->second == extruder_id) return false; return true; }; - auto build_unlink_limits = [](const std::vector& l_nodes, const std::vector& r_nodes, const std::function& can_link) { std::unordered_map> unlink_limits; for (size_t i = 0; i < l_nodes.size(); ++i) { std::vector unlink_filaments; for (size_t j = 0; j < r_nodes.size(); ++j) { - if (!can_link(i, j)) + if (!can_link(l_nodes[i], r_nodes[j])) unlink_filaments.emplace_back(j); } if (!unlink_filaments.empty()) @@ -525,76 +549,111 @@ namespace Slic3r return unlink_limits; }; + auto optimize_map_to_machine_filament = [&](const std::vector& map_to_machine_filament, const std::vector& l_nodes, const std::vector& r_nodes, std::vector& filament_map, bool consider_capacity) { + std::vector ungrouped_filaments; + std::vector filaments_to_optimize; - std::vector> color_dist_matrix(used_colors.size(), std::vector(machine_filaments.size())); - for (size_t i = 0; i < used_colors.size(); ++i) { - for (size_t j = 0; j < machine_filaments.size(); ++j) { - color_dist_matrix[i][j] = calc_color_distance( - RGBColor(used_colors[i].r, used_colors[i].g, used_colors[i].b), - RGBColor(machine_filaments[j].color.r, machine_filaments[j].color.g, machine_filaments[j].color.b) - ); + auto map_filament_to_machine_filament = [&](int filament_idx, int machine_filament_idx) { + auto& machine_filament = machine_filament_list[machine_filament_idx]; + machine_filament_capacity[machine_filament_idx] = std::max(0, machine_filament_capacity[machine_filament_idx] - 1); // decrease machine filament capacity + filament_map[used_filaments[filament_idx]] = machine_filament.extruder_id; // set extruder id to filament map + extruder_filament_count[machine_filament.extruder_id] += 1; // increase filament count in extruder + }; + auto unmap_filament_to_machine_filament = [&](int filament_idx, int machine_filament_idx) { + auto& machine_filament = machine_filament_list[machine_filament_idx]; + machine_filament_capacity[machine_filament_idx] += 1; // increase machine filament capacity + extruder_filament_count[machine_filament.extruder_id] -= 1; // increase filament count in extruder + }; + + for (size_t idx = 0; idx < map_to_machine_filament.size(); ++idx) { + if (map_to_machine_filament[idx] == MaxFlowGraph::INVALID_ID) { + ungrouped_filaments.emplace_back(l_nodes[idx]); + continue; + } + int used_filament_idx = l_nodes[idx]; + int machine_filament_idx = r_nodes[map_to_machine_filament[idx]]; + auto& machine_filament = machine_filament_list[machine_filament_idx]; + if (machine_filament_set[machine_filament].size() > 1 && unprintable_limit_indices.count(used_filament_idx) == 0) + filaments_to_optimize.emplace_back(idx); + + map_filament_to_machine_filament(used_filament_idx, machine_filament_idx); } - } + // try to optimize the result + for (auto idx : filaments_to_optimize) { + int filament_idx = l_nodes[idx]; + int old_machine_filament_idx = r_nodes[map_to_machine_filament[idx]]; + auto& old_machine_filament = machine_filament_list[old_machine_filament_idx]; - std::vectorl_nodes(used_filaments.size()); - std::vectorr_nodes(machine_filaments.size()); - std::iota(r_nodes.begin(), r_nodes.end(), 0); - std::vectorr_node_capacity(machine_filaments.size(),l_nodes.size()); + int curr_gap = std::abs(extruder_filament_count[0] - extruder_filament_count[1]); + unmap_filament_to_machine_filament(filament_idx, old_machine_filament_idx); + + auto optional_filaments = machine_filament_set[old_machine_filament]; + auto iter = optional_filaments.begin(); + for (; iter != optional_filaments.end(); ++iter) { + int new_extruder_id = machine_filament_list[*iter].extruder_id; + int new_gap = std::abs(extruder_filament_count[new_extruder_id] + 1 - extruder_filament_count[1 - new_extruder_id]); + if (new_gap < curr_gap && (!consider_capacity || machine_filament_capacity[*iter] > 0)) { + map_filament_to_machine_filament(filament_idx, *iter); + break; + } + } + + if (iter == optional_filaments.end()) + map_filament_to_machine_filament(filament_idx, old_machine_filament_idx); + } + return ungrouped_filaments; + }; std::vector group(ctx.group_info.total_filament_num, ctx.machine_info.master_extruder_id); std::vector ungrouped_filaments; + auto unlink_limits_full = build_unlink_limits(l_nodes, r_nodes, [&used_types, &machine_filament_list, is_extruder_filament_compatible](int used_filament_idx, int machine_filament_idx) { + return used_types[used_filament_idx] == machine_filament_list[machine_filament_idx].type && + is_extruder_filament_compatible(used_filament_idx, machine_filament_list[machine_filament_idx].extruder_id); + }); + { - std::iota(l_nodes.begin(), l_nodes.end(), 0); - auto unlink_limits = build_unlink_limits(l_nodes, r_nodes, [&](int lidx, int ridx) { - return used_types[l_nodes[lidx]] == machine_filaments[r_nodes[ridx]].type && - is_extruder_filament_compatible(l_nodes[lidx], machine_filaments[ridx].extruder_id); - }); - - MatchModeGroupSolver s(color_dist_matrix, l_nodes, r_nodes, r_node_capacity, unlink_limits); - auto ret = s.solve(); - for (size_t idx = 0; idx < ret.size(); ++idx) - if (ret[idx] == MaxFlowGraph::INVALID_ID) - ungrouped_filaments.emplace_back(l_nodes[idx]); - else - group[used_filaments[l_nodes[idx]]] = machine_filaments[r_nodes[ret[idx]]].extruder_id; - - for (size_t idx = 0; idx < std::min(ret.size(), l_nodes.size()); ++idx) - l_nodes[idx] = ret[idx]; + MatchModeGroupSolver s(color_dist_matrix, l_nodes, r_nodes, machine_filament_capacity, unlink_limits_full); + ungrouped_filaments = optimize_map_to_machine_filament(s.solve(), l_nodes, r_nodes,group,true); + if (ungrouped_filaments.empty()) + return group; } - if (ungrouped_filaments.empty()) - return group; + for (size_t idx = 0; idx < machine_filament_capacity.size(); ++idx) + machine_filament_capacity[idx] = l_nodes.size(); + + // remove capacity limits { l_nodes = ungrouped_filaments; - ungrouped_filaments.clear(); - - auto unlink_limits = build_unlink_limits(l_nodes, r_nodes, [&](int lidx, int ridx) { - return is_extruder_filament_compatible(l_nodes[lidx], machine_filaments[ridx].extruder_id); - }); - - MatchModeGroupSolver s(color_dist_matrix, l_nodes, r_nodes, r_node_capacity, unlink_limits); - auto ret = s.solve(); - for (size_t idx = 0; idx < ret.size(); ++idx) { - if (ret[idx] == MaxFlowGraph::INVALID_ID) - ungrouped_filaments.emplace_back(l_nodes[idx]); - else - group[used_filaments[l_nodes[idx]]] = machine_filaments[r_nodes[ret[idx]]].extruder_id; - } + MatchModeGroupSolver s(color_dist_matrix, l_nodes, r_nodes, machine_filament_capacity, unlink_limits_full); + ungrouped_filaments = optimize_map_to_machine_filament(s.solve(), l_nodes, r_nodes, group,false); + if (ungrouped_filaments.empty()) + return group; } - if (ungrouped_filaments.empty()) - return group; + // additionally remove type limits { l_nodes = ungrouped_filaments; - ungrouped_filaments.clear(); - MatchModeGroupSolver s(color_dist_matrix, l_nodes, r_nodes, r_node_capacity, {}); - auto ret = s.solve(); + auto unlink_limits = build_unlink_limits(l_nodes, r_nodes, [&machine_filament_list, is_extruder_filament_compatible](int used_filament_idx, int machine_filament_idx) { + return is_extruder_filament_compatible(used_filament_idx, machine_filament_list[machine_filament_idx].extruder_id); + }); + + MatchModeGroupSolver s(color_dist_matrix, l_nodes, r_nodes, machine_filament_capacity, unlink_limits); + ungrouped_filaments = optimize_map_to_machine_filament(s.solve(), l_nodes, r_nodes, group,false); + if (ungrouped_filaments.empty()) + return group; + } + + // remove all limits + { + l_nodes = ungrouped_filaments; + MatchModeGroupSolver s(color_dist_matrix, l_nodes, r_nodes, machine_filament_capacity, {}); + auto ret = optimize_map_to_machine_filament(s.solve(), l_nodes, r_nodes, group,false); for (size_t idx = 0; idx < ret.size(); ++idx) { if (ret[idx] == MaxFlowGraph::INVALID_ID) assert(false); else - group[used_filaments[l_nodes[idx]]] = machine_filaments[r_nodes[ret[idx]]].extruder_id; + group[used_filaments[l_nodes[idx]]] = machine_filament_list[r_nodes[ret[idx]]].extruder_id; } } diff --git a/src/libslic3r/FilamentGroupUtils.cpp b/src/libslic3r/FilamentGroupUtils.cpp index 261e6908b..7875ad9fb 100644 --- a/src/libslic3r/FilamentGroupUtils.cpp +++ b/src/libslic3r/FilamentGroupUtils.cpp @@ -26,6 +26,32 @@ namespace FilamentGroupUtils a = hexToByte(hexstr.substr(7, 2)); } + bool Color::operator<(const Color& other) const + { + if (r != other.r) return r < other.r; + if (g != other.g) return g < other.g; + if (b != other.b) return b < other.b; + return a < other.a; + } + + bool Color::operator==(const Color& other) const + { + return r == other.r && g == other.g && b == other.b && a == other.a; + + } + + bool Color::operator!=(const Color& other) const + { + return r != other.r || g != other.g || b != other.b || a != other.a; + } + + bool FilamentInfo::operator<(const FilamentInfo& other) const + { + if (color != other.color) return color < other.color; + return type < other.type; + } + + // TODO: add explanation std::vector calc_max_group_size(const std::vector>& ams_counts, bool ignore_ext_filament) { // add default value to 2 @@ -53,7 +79,11 @@ namespace FilamentGroupUtils auto& arr = filament_configs[idx]; for (auto& item : arr) { FilamentInfo temp; - temp.color = Color(item.option("filament_colour")->get_at(0)); + std::string color_str = item.option("filament_colour")->get_at(0); + if (color_str.empty()) + temp.color = Color(); + else + temp.color = Color(color_str); temp.type = item.option("filament_type")->get_at(0); temp.extruder_id = idx; temp.is_extended = item.option("tray_name")->get_at(0) == "Ext"; // hard-coded ext flag diff --git a/src/libslic3r/FilamentGroupUtils.hpp b/src/libslic3r/FilamentGroupUtils.hpp index be3a71601..533613ff2 100644 --- a/src/libslic3r/FilamentGroupUtils.hpp +++ b/src/libslic3r/FilamentGroupUtils.hpp @@ -20,6 +20,9 @@ namespace Slic3r unsigned char a = 255; Color(unsigned char r_ = 0, unsigned char g_ = 0, unsigned char b_ = 0, unsigned a_ = 255) :r(r_), g(g_), b(b_), a(a_) {} Color(const std::string& hexstr); + bool operator<(const Color& other) const; + bool operator==(const Color& other) const; + bool operator!=(const Color& other) const; }; @@ -27,7 +30,8 @@ namespace Slic3r Color color; std::string type; int extruder_id; - bool is_extended; // TODO: rename + bool is_extended; + bool operator<(const FilamentInfo& other) const; };