ENH: enhance filament group alogrithm

1. Try to merge filaments before grouping
2. Set max match num for machine filamnet in match mode

jira:STUDIO-10392

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: I2451d838e07ee02f493fda4dc702f3d13b2ad37b
This commit is contained in:
xun.zhang 2025-02-13 21:23:47 +08:00 committed by lane.wei
parent f042c817a7
commit d15fc37ff2
6 changed files with 132 additions and 31 deletions

View File

@ -24,6 +24,19 @@ namespace Slic3r
} }
} }
static std::unordered_map<int, int> get_merged_filament_map(const std::unordered_map<int, std::vector<int>>& merged_filaments)
{
std::unordered_map<int, int> filament_merge_map;
for (auto elem : merged_filaments) {
for (auto f : elem.second) {
//traverse filaments in merged group
filament_merge_map[f] = elem.first;
}
}
return filament_merge_map;
}
std::vector<int> calc_filament_group_for_tpu(const std::set<int>& tpu_filaments, const int filament_nums, const int master_extruder_id) std::vector<int> calc_filament_group_for_tpu(const std::set<int>& tpu_filaments, const int filament_nums, const int master_extruder_id)
{ {
std::vector<int> ret(filament_nums); std::vector<int> ret(filament_nums);
@ -129,8 +142,6 @@ namespace Slic3r
{ {
using namespace FlushPredict; using namespace FlushPredict;
const double ams_color_dist_threshold = used_filaments.size() * color_threshold;
const int fail_cost = 9999; const int fail_cost = 9999;
// these code is to make we machine filament info size is 2 // these code is to make we machine filament info size is 2
@ -189,14 +200,16 @@ namespace Slic3r
auto ams_map = mcmf.solve(); auto ams_map = mcmf.solve();
for (size_t idx = 0; idx < ams_map.size(); ++idx) { for (size_t idx = 0; idx < ams_map.size(); ++idx) {
if (ams_map[idx] == MaxFlowGraph::INVALID_ID) if (ams_map[idx] == MaxFlowGraph::INVALID_ID || distance_matrix[idx][ams_map[idx]] > color_threshold) {
group_cost += fail_cost; group_cost += fail_cost;
else }
else {
group_cost += distance_matrix[idx][ams_map[idx]]; group_cost += distance_matrix[idx][ams_map[idx]];
} }
} }
}
if (best_map.empty() || (group_cost < ams_color_dist_threshold && group_cost < best_cost)) { if (best_map.empty() || group_cost < best_cost) {
best_cost = group_cost; best_cost = group_cost;
best_map = map; best_map = map;
} }
@ -479,6 +492,88 @@ namespace Slic3r
return calc_min_flush_group_by_pam2(used_filaments, cost, 500); return calc_min_flush_group_by_pam2(used_filaments, cost, 500);
} }
std::unordered_map<int, std::vector<int>> FilamentGroup::try_merge_filaments()
{
std::unordered_map<int, std::vector<int>>merged_filaments;
std::unordered_map<std::string, std::vector<int>> merge_filament_map;
auto unprintable_stat_to_str = [unprintable_filaments = this->ctx.model_info.unprintable_filaments](int idx) {
std::string str;
for (size_t eid = 0; eid < unprintable_filaments.size(); ++eid) {
if (unprintable_filaments[eid].count(idx)) {
if (eid > 0)
str += ',';
str += std::to_string(idx);
}
}
return str;
};
for (size_t idx = 0; idx < ctx.model_info.filament_ids.size(); ++idx) {
std::string id = ctx.model_info.filament_ids[idx];
Color color = ctx.model_info.filament_info[idx].color;
std::string unprintable_str = unprintable_stat_to_str(idx);
std::string key = id + "," + color.to_hex_str(true) + "," + unprintable_str;
merge_filament_map[key].push_back(idx);
}
for (auto& elem : merge_filament_map) {
if (elem.second.size() > 1) {
merged_filaments[elem.second.front()] = elem.second;
}
}
return merged_filaments;
}
std::vector<int> FilamentGroup::seperate_merged_filaments(const std::vector<int>& filament_map, const std::unordered_map<int, std::vector<int>>& merged_filaments)
{
std::vector<int> ret_map = filament_map;
for (auto& elem : merged_filaments) {
int src = elem.first;
for (auto f : elem.second) {
ret_map[f] = ret_map[src];
}
}
return ret_map;
}
void FilamentGroup::rebuild_context(const std::unordered_map<int, std::vector<int>>& merged_filaments)
{
if (merged_filaments.empty())
return;
FilamentGroupContext new_ctx = ctx;
std::unordered_map<int, int> filament_merge_map = get_merged_filament_map(merged_filaments);
// modify layer filaments
for (auto& layer_filament : new_ctx.model_info.layer_filaments) {
for (auto& f : layer_filament) {
if (auto iter = filament_merge_map.find((int)(f)); iter != filament_merge_map.end()) {
f = iter->second;
}
}
}
for (auto& unprintables : new_ctx.model_info.unprintable_filaments) {
std::set<int> new_unprintables;
for (auto f : unprintables) {
if (auto iter = filament_merge_map.find((int)(f)); iter != filament_merge_map.end()) {
new_unprintables.insert(iter->second);
}
else {
new_unprintables.insert(f);
}
}
}
ctx = new_ctx;
return;
}
std::vector<int> FilamentGroup::calc_filament_group(int* cost) std::vector<int> FilamentGroup::calc_filament_group(int* cost)
{ {
@ -488,7 +583,11 @@ namespace Slic3r
} }
catch (const FilamentGroupException& e) { catch (const FilamentGroupException& e) {
} }
return calc_filament_group_for_flush(cost);
auto merged_map = try_merge_filaments();
rebuild_context(merged_map);
auto filamnet_map = calc_filament_group_for_flush(cost);
return seperate_merged_filaments(filamnet_map, merged_map);
} }
std::vector<int> FilamentGroup::calc_filament_group_for_match(int* cost) std::vector<int> FilamentGroup::calc_filament_group_for_match(int* cost)
@ -529,16 +628,7 @@ namespace Slic3r
std::iota(l_nodes.begin(), l_nodes.end(), 0); std::iota(l_nodes.begin(), l_nodes.end(), 0);
std::vector<int>r_nodes(machine_filament_list.size()); std::vector<int>r_nodes(machine_filament_list.size());
std::iota(r_nodes.begin(), r_nodes.end(), 0); std::iota(r_nodes.begin(), r_nodes.end(), 0);
std::vector<int>machine_filament_capacity(machine_filament_list.size()); std::vector<int>machine_filament_capacity(machine_filament_list.size(),l_nodes.size());
for (size_t idx = 0; idx < machine_filament_capacity.size(); ++idx) {
if (machine_filament_list[idx].is_extended) {
machine_filament_capacity[idx] = 1; // extend filaments can at most map one filaments
}
else {
machine_filament_capacity[idx] = l_nodes.size(); // AMS filaments can map multiple filaments
}
}
std::vector<int>extruder_filament_count(2, 0); std::vector<int>extruder_filament_count(2, 0);
auto is_extruder_filament_compatible = [&unprintable_limit_indices](int filament_idx, int extruder_id) { auto is_extruder_filament_compatible = [&unprintable_limit_indices](int filament_idx, int extruder_id) {
@ -627,18 +717,6 @@ namespace Slic3r
}); });
{ {
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;
}
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;
MatchModeGroupSolver s(color_dist_matrix, l_nodes, r_nodes, machine_filament_capacity, unlink_limits_full); 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); ungrouped_filaments = optimize_map_to_machine_filament(s.solve(), l_nodes, r_nodes,group,false);
if (ungrouped_filaments.empty()) if (ungrouped_filaments.empty())

View File

@ -73,6 +73,7 @@ namespace Slic3r
std::vector<FlushMatrix> flush_matrix; std::vector<FlushMatrix> flush_matrix;
std::vector<std::vector<unsigned int>> layer_filaments; std::vector<std::vector<unsigned int>> layer_filaments;
std::vector<FilamentGroupUtils::FilamentInfo> filament_info; std::vector<FilamentGroupUtils::FilamentInfo> filament_info;
std::vector<std::string> filament_ids;
std::vector<std::set<int>> unprintable_filaments; std::vector<std::set<int>> unprintable_filaments;
} model_info; } model_info;
@ -133,6 +134,11 @@ namespace Slic3r
std::vector<int> calc_min_flush_group(int* cost = nullptr); std::vector<int> calc_min_flush_group(int* cost = nullptr);
std::vector<int> calc_min_flush_group_by_enum(const std::vector<unsigned int>& used_filaments, int* cost = nullptr); std::vector<int> calc_min_flush_group_by_enum(const std::vector<unsigned int>& used_filaments, int* cost = nullptr);
std::vector<int> calc_min_flush_group_by_pam2(const std::vector<unsigned int>& used_filaments, int* cost = nullptr, int timeout_ms = 300); std::vector<int> calc_min_flush_group_by_pam2(const std::vector<unsigned int>& used_filaments, int* cost = nullptr, int timeout_ms = 300);
std::unordered_map<int, std::vector<int>> try_merge_filaments();
void rebuild_context(const std::unordered_map<int, std::vector<int>>& merged_filaments);
std::vector<int> seperate_merged_filaments(const std::vector<int>& filament_map, const std::unordered_map<int,std::vector<int>>& merged_filaments );
private: private:
FilamentGroupContext ctx; FilamentGroupContext ctx;
std::vector<std::vector<int>> m_memoryed_groups; std::vector<std::vector<int>> m_memoryed_groups;

View File

@ -1,5 +1,5 @@
#include "FilamentGroupUtils.hpp" #include "FilamentGroupUtils.hpp"
#include <sstream>
namespace Slic3r namespace Slic3r
{ {
@ -45,6 +45,20 @@ namespace FilamentGroupUtils
return r != other.r || g != other.g || b != other.b || a != other.a; return r != other.r || g != other.g || b != other.b || a != other.a;
} }
std::string Color::to_hex_str(bool include_alpha) const {
std::ostringstream oss;
oss << "#" << std::hex << std::setfill('0')
<< std::setw(2) << static_cast<int>(r)
<< std::setw(2) << static_cast<int>(g)
<< std::setw(2) << static_cast<int>(b);
if (include_alpha) {
oss << std::setw(2) << static_cast<int>(a);
}
return oss.str();
}
bool MachineFilamentInfo::operator<(const MachineFilamentInfo& other) const bool MachineFilamentInfo::operator<(const MachineFilamentInfo& other) const
{ {
if (color != other.color) return color < other.color; if (color != other.color) return color < other.color;

View File

@ -23,6 +23,7 @@ namespace Slic3r
bool operator<(const Color& other) const; bool operator<(const Color& other) const;
bool operator==(const Color& other) const; bool operator==(const Color& other) const;
bool operator!=(const Color& other) const; bool operator!=(const Color& other) const;
std::string to_hex_str(bool include_alpha = false) const;
}; };

View File

@ -1008,7 +1008,7 @@ std::vector<int> ToolOrdering::get_recommended_filament_maps(const std::vector<s
std::vector<std::string> filament_types = print_config.filament_type.values; std::vector<std::string> filament_types = print_config.filament_type.values;
std::vector<std::string> filament_colours = print_config.filament_colour.values; std::vector<std::string> filament_colours = print_config.filament_colour.values;
std::vector<unsigned char> filament_is_support = print_config.filament_is_support.values; std::vector<unsigned char> filament_is_support = print_config.filament_is_support.values;
std::vector<std::string> filament_ids = print_config.filament_ids.values;
// speacially handle tpu filaments // speacially handle tpu filaments
auto used_filaments = collect_sorted_used_filaments(layer_filaments); auto used_filaments = collect_sorted_used_filaments(layer_filaments);
auto tpu_filaments = get_filament_by_type(used_filaments, &print_config, "TPU"); auto tpu_filaments = get_filament_by_type(used_filaments, &print_config, "TPU");
@ -1022,6 +1022,7 @@ std::vector<int> ToolOrdering::get_recommended_filament_maps(const std::vector<s
context.model_info.flush_matrix = std::move(nozzle_flush_mtx); context.model_info.flush_matrix = std::move(nozzle_flush_mtx);
context.model_info.unprintable_filaments = ext_unprintable_filaments; context.model_info.unprintable_filaments = ext_unprintable_filaments;
context.model_info.layer_filaments = layer_filaments; context.model_info.layer_filaments = layer_filaments;
context.model_info.filament_ids = filament_ids;
for (size_t idx = 0; idx < filament_types.size(); ++idx) { for (size_t idx = 0; idx < filament_types.size(); ++idx) {
FilamentGroupUtils::FilamentInfo info; FilamentGroupUtils::FilamentInfo info;

View File

@ -1014,6 +1014,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloats, filament_density)) ((ConfigOptionFloats, filament_density))
((ConfigOptionStrings, filament_type)) ((ConfigOptionStrings, filament_type))
((ConfigOptionBools, filament_soluble)) ((ConfigOptionBools, filament_soluble))
((ConfigOptionStrings, filament_ids))
((ConfigOptionBools, filament_is_support)) ((ConfigOptionBools, filament_is_support))
((ConfigOptionEnumsGeneric, filament_scarf_seam_type)) ((ConfigOptionEnumsGeneric, filament_scarf_seam_type))
((ConfigOptionFloatsOrPercents, filament_scarf_height)) ((ConfigOptionFloatsOrPercents, filament_scarf_height))