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; };