OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::FilamentGroup Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/FilamentGroup.hpp>

Collaboration diagram for Slic3r::FilamentGroup:

Public Member Functions

 FilamentGroup (const FilamentGroupContext &ctx_)
void set_clustering_budget (const ClusteringBudget &budget)
std::vector< int > calc_filament_group (int *cost=nullptr)
std::vector< std::vector< int > > get_memoryed_groups () const
std::vector< int > calc_filament_group_for_match (int *cost=nullptr)
std::vector< int > calc_filament_group_for_flush (int *cost=nullptr)
std::vector< int > calc_filament_group_for_tpu (int *cost=nullptr)

Public Attributes

std::optional< std::function< bool(int, std::vector< int > &)> > get_custom_seq

Private Types

using MemoryedGroup = FilamentGroupUtils::MemoryedGroup
using MemoryedGroupHeap = FilamentGroupUtils::MemoryedGroupHeap

Private Member Functions

std::vector< int > calc_min_flush_group (int *cost=nullptr)
std::vector< int > calc_group_by_enum (int k, const std::vector< unsigned int > &used_filaments, const std::unordered_map< int, std::vector< int > > &unplaceable_limits, int *cost=nullptr)
std::vector< int > calc_group_by_kmedoids (int k, const std::vector< unsigned int > &used_filaments, const std::unordered_map< int, std::vector< int > > &unplaceable_limits, int *cost=nullptr)
std::map< int, int > rebuild_unprintables (const std::vector< unsigned int > &used_filaments, const std::map< int, int > &extruder_unprintables)
std::unordered_map< int, std::vector< int > > rebuild_nozzle_unprintables (const std::vector< unsigned int > &used_filaments, const std::unordered_map< int, std::vector< int > > &extruder_unprintables, const std::vector< int > &filament_volume_map)
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 Attributes

FilamentGroupContext ctx
MemoryedGroupHeap m_memoryed_heap
std::vector< std::vector< int > > m_memoryed_groups
ClusteringBudget m_clustering_budget

Detailed Description

Member Typedef Documentation

◆ MemoryedGroup

◆ MemoryedGroupHeap

Constructor & Destructor Documentation

◆ FilamentGroup()

Slic3r::FilamentGroup::FilamentGroup ( const FilamentGroupContext & ctx_)
inlineexplicit
167:ctx(ctx_) {}
FilamentGroupContext ctx
Definition FilamentGroup.hpp:194

References ctx.

Member Function Documentation

◆ calc_filament_group()

std::vector< int > Slic3r::FilamentGroup::calc_filament_group ( int * cost = nullptr)
1037 {
1038 /*auto extruder_variant_list = ctx.nozzle_info.extruder_nozzle_list;
1039 for (auto nozzle : ctx.nozzle_info.nozzle_list)
1040 if (nozzle.volume_type == NozzleVolumeType::nvtTPUHighFlow)
1041 return calc_filament_group_for_tpu(cost);*/
1042
1043 try {
1044 if (FGMode::MatchMode == ctx.group_info.mode)
1045 return calc_filament_group_for_match(cost);
1046 }
1047 catch (const FilamentGroupException&) {
1048 }
1049
1050 return calc_filament_group_for_flush(cost);
1051 }
std::vector< int > calc_filament_group_for_flush(int *cost=nullptr)
Definition FilamentGroup.cpp:1253
std::vector< int > calc_filament_group_for_match(int *cost=nullptr)
Definition FilamentGroup.cpp:1053
@ MatchMode
Definition FilamentGroup.hpp:37

References calc_filament_group_for_flush(), calc_filament_group_for_match(), ctx, and Slic3r::MatchMode.

Referenced by Slic3r::ToolOrdering::get_recommended_filament_maps().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ calc_filament_group_for_flush()

std::vector< int > Slic3r::FilamentGroup::calc_filament_group_for_flush ( int * cost = nullptr)
1254 {
1255 auto used_filaments = collect_sorted_used_filaments(ctx.model_info.layer_filaments);
1256
1257 std::vector<int> ret = calc_min_flush_group(cost);
1258 std::vector<std::vector<int>> memoryed_maps = this->m_memoryed_groups;
1259 memoryed_maps.insert(memoryed_maps.begin(), ret);
1260
1261 std::vector<FilamentGroupUtils::FilamentInfo> used_filament_info;
1262 for (auto f : used_filaments) {
1263 used_filament_info.emplace_back(ctx.model_info.filament_info[f]);
1264 }
1265
1266 ret = select_best_group_for_ams(memoryed_maps, ctx.nozzle_info.nozzle_list, used_filaments, used_filament_info, ctx.machine_info.machine_filament_info, ctx.group_info.has_filament_switcher);
1267 return ret;
1268 }
std::vector< std::vector< int > > m_memoryed_groups
Definition FilamentGroup.hpp:196
std::vector< int > calc_min_flush_group(int *cost=nullptr)
Definition FilamentGroup.cpp:901
std::vector< int > select_best_group_for_ams(const std::vector< std::vector< int > > &filament_to_nozzles, const std::vector< MultiNozzleUtils::NozzleInfo > &nozzle_list, const std::vector< unsigned int > &used_filaments, const std::vector< FilamentGroupUtils::FilamentInfo > &used_filament_info, const std::vector< std::vector< MachineFilamentInfo > > &machine_filament_info_, const bool has_filament_switcher, const double color_threshold)
Select the group that best fit the filaments in AMS.
Definition FilamentGroup.cpp:427
std::vector< unsigned int > collect_sorted_used_filaments(const std::vector< std::vector< unsigned int > > &layer_filaments)
Definition FilamentGroup.cpp:563
static double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
Definition FillGyroid.cpp:124

References calc_min_flush_group(), Slic3r::collect_sorted_used_filaments(), ctx, Slic3r::f(), m_memoryed_groups, and Slic3r::select_best_group_for_ams().

Referenced by calc_filament_group(), Slic3r::calc_filament_group_for_manual_multi_nozzle(), and Slic3r::calc_filament_group_for_match_multi_nozzle().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ calc_filament_group_for_match()

std::vector< int > Slic3r::FilamentGroup::calc_filament_group_for_match ( int * cost = nullptr)
1054 {
1055 using namespace FlushPredict;
1056 constexpr int SupportPreferScore = 3;
1057
1058 auto used_filaments = collect_sorted_used_filaments(ctx.model_info.layer_filaments);
1059 std::vector<FilamentGroupUtils::FilamentInfo> used_filament_list;
1060 for (auto f : used_filaments)
1061 used_filament_list.emplace_back(ctx.model_info.filament_info[f]);
1062
1063 std::vector<MachineFilamentInfo> machine_filament_list;
1064 std::map<MachineFilamentInfo, std::set<int>> machine_filament_set;
1065 for (size_t eid = 0; eid < ctx.machine_info.machine_filament_info.size();++eid) {
1066 for (auto& filament : ctx.machine_info.machine_filament_info[eid]) {
1067 machine_filament_set[filament].insert(machine_filament_list.size());
1068 machine_filament_list.emplace_back(filament);
1069 }
1070 }
1071
1072 if (machine_filament_list.empty())
1073 throw FilamentGroupException(FilamentGroupException::EmptyAmsFilaments,"Empty ams filament in For-Match mode.");
1074
1075 std::map<int, int> unprintable_limit_indices; // key stores filament idx in used_filament, value stores unprintable extruder
1076 extract_unprintable_limit_indices(ctx.model_info.unprintable_filaments, used_filaments, unprintable_limit_indices);
1077 unprintable_limit_indices = rebuild_unprintables(used_filaments, unprintable_limit_indices);
1078
1079 std::vector<std::vector<float>> color_dist_matrix(used_filament_list.size(), std::vector<float>(machine_filament_list.size()));
1080 for (size_t i = 0; i < used_filament_list.size(); ++i) {
1081 for (size_t j = 0; j < machine_filament_list.size(); ++j) {
1082 color_dist_matrix[i][j] = calc_color_distance(
1083 RGBColor(used_filament_list[i].color.r, used_filament_list[i].color.g, used_filament_list[i].color.b),
1084 RGBColor(machine_filament_list[j].color.r, machine_filament_list[j].color.g, machine_filament_list[j].color.b)
1085 );
1086 }
1087 }
1088
1089 std::vector<int>l_nodes(used_filaments.size());
1090 std::iota(l_nodes.begin(), l_nodes.end(), 0);
1091 std::vector<int>r_nodes(machine_filament_list.size());
1092 std::iota(r_nodes.begin(), r_nodes.end(), 0);
1093 std::vector<int>machine_filament_capacity(machine_filament_list.size(),l_nodes.size());
1094 std::vector<int>extruder_filament_count(2, 0);
1095
1096 auto is_extruder_filament_compatible = [&unprintable_limit_indices](int filament_idx, int extruder_id) {
1097 auto iter = unprintable_limit_indices.find(filament_idx);
1098 if (iter != unprintable_limit_indices.end() && iter->second == extruder_id)
1099 return false;
1100 return true;
1101 };
1102
1103 auto build_unlink_limits = [](const std::vector<int>& l_nodes, const std::vector<int>& r_nodes, const std::function<bool(int, int)>& can_link) {
1104 std::unordered_map<int, std::vector<int>> unlink_limits;
1105 for (size_t i = 0; i < l_nodes.size(); ++i) {
1106 std::vector<int> unlink_filaments;
1107 for (size_t j = 0; j < r_nodes.size(); ++j) {
1108 if (!can_link(l_nodes[i], r_nodes[j]))
1109 unlink_filaments.emplace_back(j);
1110 }
1111 if (!unlink_filaments.empty())
1112 unlink_limits.emplace(i, std::move(unlink_filaments));
1113 }
1114 return unlink_limits;
1115 };
1116
1117 auto optimize_map_to_machine_filament = [&](const std::vector<int>& map_to_machine_filament, const std::vector<int>& l_nodes, const std::vector<int>& r_nodes, std::vector<int>& filament_map) {
1118 std::vector<int> ungrouped_filaments;
1119 std::vector<int> filaments_to_optimize;
1120
1121 auto map_filament_to_machine_filament = [&](int filament_idx, int machine_filament_idx) {
1122 auto& machine_filament = machine_filament_list[machine_filament_idx];
1123 filament_map[used_filaments[filament_idx]] = machine_filament.extruder_id; // set extruder id to filament map
1124 extruder_filament_count[machine_filament.extruder_id] += 1; // increase filament count in extruder
1125 };
1126 auto unmap_filament_to_machine_filament = [&](int filament_idx, int machine_filament_idx) {
1127 auto& machine_filament = machine_filament_list[machine_filament_idx];
1128 extruder_filament_count[machine_filament.extruder_id] -= 1; // increase filament count in extruder
1129 };
1130
1131 for (size_t idx = 0; idx < map_to_machine_filament.size(); ++idx) {
1132 if (map_to_machine_filament[idx] == MaxFlowGraph::INVALID_ID) {
1133 ungrouped_filaments.emplace_back(l_nodes[idx]);
1134 continue;
1135 }
1136 int used_filament_idx = l_nodes[idx];
1137 int machine_filament_idx = r_nodes[map_to_machine_filament[idx]];
1138 auto& machine_filament = machine_filament_list[machine_filament_idx];
1139 if (machine_filament_set[machine_filament].size() > 1 && unprintable_limit_indices.count(used_filament_idx) == 0)
1140 filaments_to_optimize.emplace_back(idx);
1141
1142 map_filament_to_machine_filament(used_filament_idx, machine_filament_idx);
1143 }
1144 // try to optimize the result
1145 for (auto idx : filaments_to_optimize) {
1146 int filament_idx = l_nodes[idx];
1147 bool is_support_filament = used_filament_list[filament_idx].usage_type == FilamentUsageType::SupportOnly;
1148 int old_machine_filament_idx = r_nodes[map_to_machine_filament[idx]];
1149 auto& old_machine_filament = machine_filament_list[old_machine_filament_idx];
1150
1151 unmap_filament_to_machine_filament(filament_idx, old_machine_filament_idx);
1152
1153 auto optional_filaments = machine_filament_set[old_machine_filament];
1154
1155 // Phase 1: collect all candidates and compute their preference scores
1156 std::vector<std::pair<int, int>> valid_candidates; // available machine-filament idx and its score
1157 for (auto machine_filament : optional_filaments) {
1158 int new_extruder_id = machine_filament_list[machine_filament].extruder_id;
1159
1160 // preference score for this assignment
1161 int preference_score = 0;
1162 bool new_extruder_prefer_support = ctx.machine_info.prefer_non_model_filament[new_extruder_id];
1163
1164 // reward a support filament assigned to a support-preferring nozzle
1165 if (is_support_filament && new_extruder_prefer_support) {
1166 preference_score += SupportPreferScore;
1167 }
1168
1169 valid_candidates.emplace_back(machine_filament, preference_score);
1170 }
1171 // Phase 2: determine the best preference score
1172 int best_preference_score = 0;
1173 for (const auto& candidate : valid_candidates) {
1174 if (candidate.second >= best_preference_score) {
1175 best_preference_score = candidate.second;
1176 }
1177 }
1178
1179 // Phase 3: among candidates with the best preference score, pick the most load-balanced one
1180 int best_candidate = -1;
1181 int best_gap = std::numeric_limits<int>::max();
1182
1183 for (const auto& candidate : valid_candidates) {
1184 // only consider candidates with the best preference score
1185 int machine_filament = candidate.first;
1186 int score = candidate.second;
1187 if (score == best_preference_score) {
1188 int new_extruder_id = machine_filament_list[machine_filament].extruder_id;
1189 int new_gap = std::abs(extruder_filament_count[new_extruder_id] + 1 - extruder_filament_count[1 - new_extruder_id]);
1190
1191 // among equal-preference candidates, pick the one giving the most balanced load
1192 if (new_gap < best_gap) {
1193 best_gap = new_gap;
1194 best_candidate = machine_filament;
1195 }
1196 }
1197 }
1198 // apply the best choice
1199 if (best_candidate != -1) {
1200 map_filament_to_machine_filament(filament_idx, best_candidate);
1201 } else {
1202 map_filament_to_machine_filament(filament_idx, old_machine_filament_idx);
1203 }
1204 }
1205 return ungrouped_filaments;
1206 };
1207
1208 std::vector<int> group(ctx.group_info.total_filament_num, ctx.machine_info.master_extruder_id);
1209 std::vector<int> ungrouped_filaments;
1210
1211 auto unlink_limits_full = build_unlink_limits(l_nodes, r_nodes, [&used_filament_list, &machine_filament_list, is_extruder_filament_compatible](int used_filament_idx, int machine_filament_idx) {
1212 return used_filament_list[used_filament_idx].type == machine_filament_list[machine_filament_idx].type &&
1213 used_filament_list[used_filament_idx].is_support == machine_filament_list[machine_filament_idx].is_support &&
1214 is_extruder_filament_compatible(used_filament_idx, machine_filament_list[machine_filament_idx].extruder_id);
1215 });
1216
1217 {
1218 MatchModeGroupSolver s(color_dist_matrix, l_nodes, r_nodes, machine_filament_capacity, unlink_limits_full);
1219 ungrouped_filaments = optimize_map_to_machine_filament(s.solve(), l_nodes, r_nodes,group);
1220 if (ungrouped_filaments.empty())
1221 return group;
1222 }
1223
1224 // additionally remove type limits
1225 {
1226 l_nodes = ungrouped_filaments;
1227 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) {
1228 return is_extruder_filament_compatible(used_filament_idx, machine_filament_list[machine_filament_idx].extruder_id);
1229 });
1230
1231 MatchModeGroupSolver s(color_dist_matrix, l_nodes, r_nodes, machine_filament_capacity, unlink_limits);
1232 ungrouped_filaments = optimize_map_to_machine_filament(s.solve(), l_nodes, r_nodes, group);
1233 if (ungrouped_filaments.empty())
1234 return group;
1235 }
1236
1237 // remove all limits
1238 {
1239 l_nodes = ungrouped_filaments;
1240 MatchModeGroupSolver s(color_dist_matrix, l_nodes, r_nodes, machine_filament_capacity, {});
1241 auto ret = optimize_map_to_machine_filament(s.solve(), l_nodes, r_nodes, group);
1242 for (size_t idx = 0; idx < ret.size(); ++idx) {
1243 if (ret[idx] == MaxFlowGraph::INVALID_ID)
1244 assert(false);
1245 else
1246 group[used_filaments[l_nodes[idx]]] = machine_filament_list[r_nodes[ret[idx]]].extruder_id;
1247 }
1248 }
1249
1250 return group;
1251 }
@ EmptyAmsFilaments
Definition FilamentGroupUtils.hpp:54
std::map< int, int > rebuild_unprintables(const std::vector< unsigned int > &used_filaments, const std::map< int, int > &extruder_unprintables)
Definition FilamentGroup.cpp:925
float calc_color_distance(const LABColor &lab1, const LABColor &lab2)
Definition FlushVolPredictor.cpp:73
void extract_unprintable_limit_indices(const std::vector< std::set< int > > &unprintable_elems, const std::vector< unsigned int > &used_filaments, std::map< int, int > &unplaceable_limits)
Definition FilamentGroupUtils.cpp:247
bool is_support_filament(int extruder_id, bool strict_check)
Definition GUI_App.cpp:10492
const int INVALID_ID
Definition ToolOrderUtils.hpp:21
@ SupportOnly
Definition PrintConfig.hpp:70
zaa_minimize_perimeter_height filament_map((ConfigOptionInts, filament_volume_map))((ConfigOptionInts

References FlushPredict::calc_color_distance(), Slic3r::collect_sorted_used_filaments(), ctx, Slic3r::FilamentGroupUtils::FilamentGroupException::EmptyAmsFilaments, Slic3r::FilamentGroupUtils::extract_unprintable_limit_indices(), Slic3r::f(), Slic3r::filament_map(), Slic3r::MaxFlowGraph::INVALID_ID, rebuild_unprintables(), Slic3r::MatchModeGroupSolver::solve(), and Slic3r::SupportOnly.

Referenced by calc_filament_group(), and Slic3r::calc_filament_group_for_match_multi_nozzle().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ calc_filament_group_for_tpu()

std::vector< int > Slic3r::FilamentGroup::calc_filament_group_for_tpu ( int * cost = nullptr)
1270 {
1271
1272 auto used_filaments = collect_sorted_used_filaments(ctx.model_info.layer_filaments);
1273 std::vector<FilamentGroupUtils::FilamentInfo> used_filament_list;
1274 for (auto f : used_filaments)
1275 used_filament_list.emplace_back(ctx.model_info.filament_info[f]);
1276
1277 std::vector<std::vector<float>> print_time_matrix(used_filaments.size(), std::vector<float>(ctx.nozzle_info.extruder_nozzle_list.size()));
1278 for (int i = 0; i < used_filaments.size(); ++i){
1279 for (int j = 0; j < ctx.nozzle_info.extruder_nozzle_list.size(); ++j){
1280 print_time_matrix[i][j] = ctx.speed_info.filament_print_time[used_filaments[i]][j];
1281 if (ctx.nozzle_info.nozzle_list[j].volume_type == nvtTPUHighFlow) // when both TPU High Flow and other nozzle types exist, prefer assigning filament to the TPU High Flow nozzle
1282 print_time_matrix[i][j] *= 0.9;
1283 }
1284 }
1285
1286 std::vector<int> l_nodes(used_filaments.size());
1287 std::iota(l_nodes.begin(), l_nodes.end(), 0);
1288 std::vector<int> r_nodes(ctx.nozzle_info.extruder_nozzle_list.size());
1289 std::iota(r_nodes.begin(), r_nodes.end(), 0);
1290 std::vector<int> machine_filament_capacity({int(used_filaments.size()), int(used_filaments.size())});
1291
1292 std::map<int, int> unprintable_limit_indices; // key stores filament idx in used_filament, value stores unprintable extruder
1293 extract_unprintable_limit_indices(ctx.model_info.unprintable_filaments, used_filaments, unprintable_limit_indices);
1294 unprintable_limit_indices = rebuild_unprintables(used_filaments, unprintable_limit_indices);
1295
1296 std::unordered_map<int, std::vector<int>> unlink_limits(used_filaments.size());
1297 for (int i = 0; i < used_filaments.size(); i++) {
1298 auto iter = unprintable_limit_indices.find(i);
1299 if (iter == unprintable_limit_indices.end() || iter->second < 0 || iter->second >= 2) continue;
1300 unlink_limits[i].emplace_back(iter->second);
1301 }
1302
1303 MatchModeGroupSolver s(print_time_matrix, l_nodes, r_nodes, machine_filament_capacity, unlink_limits);
1304 auto ret = s.solve();
1305 for (size_t idx = 0; idx < ret.size(); ++idx) {
1306 if (ret[idx] == MaxFlowGraph::INVALID_ID) {
1307 assert(false);
1308 ret[idx] = 1;
1309 }
1310 }
1311 std::vector<int> group(ctx.group_info.total_filament_num, ctx.machine_info.master_extruder_id);
1312 for (int i = 0; i < ret.size(); ++i) group[used_filaments[i]] = ret[i];
1313 return group;
1314 }
@ nvtTPUHighFlow
Definition PrintConfig.hpp:624

References Slic3r::collect_sorted_used_filaments(), ctx, Slic3r::FilamentGroupUtils::extract_unprintable_limit_indices(), Slic3r::f(), Slic3r::MaxFlowGraph::INVALID_ID, Slic3r::nvtTPUHighFlow, rebuild_unprintables(), and Slic3r::MatchModeGroupSolver::solve().

Here is the call graph for this function:

◆ calc_group_by_enum()

std::vector< int > Slic3r::FilamentGroup::calc_group_by_enum ( int k,
const std::vector< unsigned int > & used_filaments,
const std::unordered_map< int, std::vector< int > > & unplaceable_limits,
int * cost = nullptr )
private
234 {
235 static constexpr int UNPLACEABLE_LIMIT_REWARD = 10000;
236 static constexpr int MAX_SIZE_LIMIT_REWARD = 5000;
237 static constexpr int SUPPORT_PREFER_REWARD = 100;
238 static constexpr int BEST_FIT_LIMIT_REWARD = 10;
239
240 int n = (int)used_filaments.size();
241
242 std::vector<std::vector<int>> candidates(n);
243 for (int i = 0; i < n; i++) {
244 std::unordered_set<int> group_set;
245 if (auto it = unplaceable_limits.find(i); it != unplaceable_limits.end()) {
246 for (int g = 0; g < k; g++) group_set.insert(g);
247 for (int g : it->second) group_set.erase(g);
248 } else {
249 for (int g = 0; g < k; g++) group_set.insert(g);
250 }
251 candidates[i].assign(group_set.begin(), group_set.end());
252 }
253
254 auto vector_equal = [](const std::vector<uint64_t>& a, const std::vector<uint64_t>& b) -> bool {
255 if (a.size() != b.size()) return false;
256 for (size_t i = 0; i < a.size(); i++) {
257 if (a[i] != b[i]) return false;
258 }
259 return true;
260 };
261 auto vector_hash = [](const std::vector<uint64_t>& v) -> size_t {
262 size_t h = 0;
263 for (auto val : v) { h ^= val + GOLDEN_RATIO_32 + (h << 6) + (h >> 2); }
264 return h;
265 };
266 std::unordered_set<std::vector<uint64_t>, decltype(vector_hash), decltype(vector_equal)> group_set(0, vector_hash, vector_equal);
267 std::vector<uint64_t> group_hashs;
268
269 std::vector<size_t> nozzles_hash(k);
270 for (const auto& nozzle : ctx.nozzle_info.nozzle_list) {
271 if (nozzle.group_id < k) {
272 auto it = ctx.nozzle_info.nozzle_status.find(nozzle.group_id);
273 int loaded_filament = (it != ctx.nozzle_info.nozzle_status.end()) ? it->second : -1;
274 nozzles_hash[nozzle.group_id] = fnv_hash_nozzle(nozzle.volume_type, nozzle.group_id > 0, loaded_filament);
275 }
276 }
277
278 std::vector<int> best_full_map(ctx.group_info.total_filament_num, ctx.machine_info.master_extruder_id);
279 double best_score = std::numeric_limits<double>::max();
280 int best_prefer_level = 0;
281 int best_flush = 0;
282
283 const long long total = (long long)std::pow(k, n);
284 for (long long mask = 0; mask < total; mask++) {
285 long long num = mask;
286 std::unordered_map<int, std::vector<int>> nozzles_filaments;
287 std::vector<int> groups_count(k, 0);
288 std::vector<int> used_labels(n, 0);
289
290 for (int i = 0; i < n; i++) {
291 int g_id = num % k;
292 num /= k;
293 used_labels[i] = g_id;
294 nozzles_filaments[g_id].emplace_back(i);
295 groups_count[g_id]++;
296 }
297
298 // Hash dedup
299 group_hashs.clear();
300 for (auto& nf : nozzles_filaments) {
301 uint64_t filament_mask = 0;
302 for (int filament : nf.second) filament_mask |= (1ULL << filament);
303 size_t gh = nozzles_hash[nf.first];
304 gh ^= (std::hash<uint64_t>{}(filament_mask) + GOLDEN_RATIO_32 + (gh << 6) + (gh >> 2));
305 group_hashs.emplace_back(gh);
306 }
307 std::sort(group_hashs.begin(), group_hashs.end());
308 if (group_set.find(group_hashs) != group_set.end()) continue;
309 group_set.insert(group_hashs);
310
311 // Prefer level
312 int prefer_level = 0;
313 int placeable_count = 0;
314 for (int i = 0; i < n; i++) {
315 if (std::find(candidates[i].begin(), candidates[i].end(), used_labels[i]) != candidates[i].end())
316 placeable_count++;
317 }
318 prefer_level += placeable_count * UNPLACEABLE_LIMIT_REWARD;
319
320 bool size_ok = true;
321 for (int g = 0; g < k; g++) {
322 if (g < (int)ctx.machine_info.max_group_size.size() && groups_count[g] > ctx.machine_info.max_group_size[g])
323 size_ok = false;
324 }
325 if (size_ok)
326 prefer_level += MAX_SIZE_LIMIT_REWARD;
327
328 if (ctx.group_info.strategy == FGStrategy::BestFit) {
329 bool all_full = true;
330 for (int g = 0; g < k; g++) {
331 if (g < (int)ctx.machine_info.max_group_size.size() && groups_count[g] < ctx.machine_info.max_group_size[g])
332 all_full = false;
333 }
334 if (all_full)
335 prefer_level += BEST_FIT_LIMIT_REWARD;
336 }
337
338 for (int g = 0; g < k; g++) {
339 if (g < (int)ctx.machine_info.prefer_non_model_filament.size() && ctx.machine_info.prefer_non_model_filament[g]) {
340 for (int fidx : nozzles_filaments[g]) {
341 if (ctx.model_info.filament_info[used_filaments[fidx]].usage_type == SupportOnly)
342 prefer_level += SUPPORT_PREFER_REWARD;
343 }
344 }
345 }
346
347 // Build full map and evaluate
348 std::vector<int> full_map(ctx.group_info.total_filament_num, ctx.machine_info.master_extruder_id);
349 for (int i = 0; i < n; ++i)
350 full_map[used_filaments[i]] = used_labels[i];
351
352 int flush_vol = 0;
353 double score = full_evaluate(used_filaments, full_map, ctx, get_custom_seq, &flush_vol);
354
355 int master_ex_id = ctx.machine_info.master_extruder_id;
356 if (master_ex_id < k && groups_count[master_ex_id] < (int)(used_filaments.size() + 1) / 2)
358
359 if (prefer_level > best_prefer_level || (prefer_level == best_prefer_level && score < best_score)) {
360 best_score = score;
361 best_prefer_level = prefer_level;
362 best_full_map = full_map;
363 best_flush = flush_vol;
364 }
365
366 MemoryedGroup mg(used_labels, score, prefer_level);
367 update_memoryed_groups(mg, ctx.group_info.max_gap_threshold, m_memoryed_heap);
368 }
369
370 if (cost) *cost = best_flush;
371 return best_full_map;
372 }
static const int ABSOLUTE_FLUSH_GAP_TOLERANCE
Definition FilamentGroup.hpp:23
FilamentGroupUtils::MemoryedGroup MemoryedGroup
Definition FilamentGroup.hpp:164
MemoryedGroupHeap m_memoryed_heap
Definition FilamentGroup.hpp:195
std::optional< std::function< bool(int, std::vector< int > &)> > get_custom_seq
Definition FilamentGroup.hpp:199
T * begin(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:838
T * end(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:841
void update_memoryed_groups(const MemoryedGroup &item, const double gap_threshold, MemoryedGroupHeap &groups)
Definition FilamentGroup.cpp:518
static double full_evaluate(const std::vector< unsigned int > &used_filaments, const std::vector< int > &filament_nozzle_map, const FilamentGroupContext &ctx, std::optional< std::function< bool(int, std::vector< int > &)> > get_custom_seq=std::nullopt, int *out_flush=nullptr)
Definition FilamentGroup.cpp:114
@ BestFit
Definition FilamentGroup.hpp:32
static uint64_t fnv_hash_nozzle(int volume_type, int is_right_extruder, int loaded_filament=-1)
Definition FilamentGroup.cpp:67
constexpr uint32_t GOLDEN_RATIO_32
Definition FilamentGroup.cpp:38

References ABSOLUTE_FLUSH_GAP_TOLERANCE, Slic3r::BestFit, ctx, Slic3r::fnv_hash_nozzle(), Slic3r::full_evaluate(), get_custom_seq, Slic3r::GOLDEN_RATIO_32, m_memoryed_heap, Slic3r::SupportOnly, and Slic3r::FilamentGroupUtils::update_memoryed_groups().

Referenced by calc_min_flush_group().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ calc_group_by_kmedoids()

std::vector< int > Slic3r::FilamentGroup::calc_group_by_kmedoids ( int k,
const std::vector< unsigned int > & used_filaments,
const std::unordered_map< int, std::vector< int > > & unplaceable_limits,
int * cost = nullptr )
private
379 {
380 auto distance_evaluator = std::make_shared<FlushDistanceEvaluator>(ctx.model_info.flush_matrix, used_filaments, ctx.model_info.layer_filaments);
381 KMediods PAM(k, (int)used_filaments.size(), distance_evaluator, ctx.machine_info.master_extruder_id);
382 PAM.set_unplacable_limits(unplaceable_limits);
383 PAM.set_memory_threshold(ctx.group_info.max_gap_threshold);
384
385 std::vector<std::pair<std::set<int>, int>> cluster_size_limit;
386 for (auto& [extruder_id, nozzles] : ctx.nozzle_info.extruder_nozzle_list) {
387 std::pair<std::set<int>, int> clusters;
388 clusters.first = std::set<int>(nozzles.begin(), nozzles.end());
389 clusters.second = ctx.machine_info.max_group_size.at(extruder_id);
390 cluster_size_limit.emplace_back(clusters);
391 }
392 PAM.set_cluster_group_size(cluster_size_limit);
393
394 PAM.do_clustering(ctx, m_clustering_budget);
395
396 m_memoryed_heap = PAM.get_memoryed_groups();
397
398 auto labels = PAM.get_cluster_labels();
399 std::vector<int> full_map(ctx.group_info.total_filament_num, ctx.machine_info.master_extruder_id);
400 for (int i = 0; i < (int)labels.size(); ++i)
401 full_map[used_filaments[i]] = labels[i];
402
403 if (cost) {
404 int flush_vol = 0;
405 full_evaluate(used_filaments, full_map, ctx, get_custom_seq, &flush_vol);
406 *cost = flush_vol;
407 }
408
409 return full_map;
410 }
ClusteringBudget m_clustering_budget
Definition FilamentGroup.hpp:197

References ctx, Slic3r::KMediods::do_clustering(), Slic3r::full_evaluate(), Slic3r::KMediods::get_cluster_labels(), get_custom_seq, Slic3r::KMediods::get_memoryed_groups(), m_clustering_budget, m_memoryed_heap, Slic3r::KMediods::set_cluster_group_size(), Slic3r::KMediods::set_memory_threshold(), and Slic3r::KMediods::set_unplacable_limits().

Referenced by calc_min_flush_group().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ calc_min_flush_group()

std::vector< int > Slic3r::FilamentGroup::calc_min_flush_group ( int * cost = nullptr)
private
902 {
903 auto used_filaments = collect_sorted_used_filaments(ctx.model_info.layer_filaments);
904 int n = (int)used_filaments.size();
905 int k = (int)ctx.nozzle_info.nozzle_list.size();
906
907 std::unordered_map<int, std::vector<int>> unplaceable_limits;
908 extract_unprintable_limit_indices(ctx.model_info.unprintable_filaments, used_filaments, unplaceable_limits);
909 unplaceable_limits = rebuild_nozzle_unprintables(used_filaments, unplaceable_limits, ctx.group_info.filament_volume_map);
910
912 std::vector<int> result;
913
914 long long estimated = estimate_dedup_enum_count(k, n, ctx);
915 if (estimated < ENUM_THRESHOLD)
916 result = calc_group_by_enum(k, used_filaments, unplaceable_limits, cost);
917 else
918 result = calc_group_by_kmedoids(k, used_filaments, unplaceable_limits, cost);
919
920 change_memoryed_heaps_to_arrays(m_memoryed_heap, ctx.group_info.total_filament_num, used_filaments, m_memoryed_groups);
921
922 return result;
923 }
std::unordered_map< int, std::vector< int > > rebuild_nozzle_unprintables(const std::vector< unsigned int > &used_filaments, const std::unordered_map< int, std::vector< int > > &extruder_unprintables, const std::vector< int > &filament_volume_map)
Definition FilamentGroup.cpp:1318
std::vector< int > calc_group_by_kmedoids(int k, const std::vector< unsigned int > &used_filaments, const std::unordered_map< int, std::vector< int > > &unplaceable_limits, int *cost=nullptr)
Definition FilamentGroup.cpp:374
FilamentGroupUtils::MemoryedGroupHeap MemoryedGroupHeap
Definition FilamentGroup.hpp:165
std::vector< int > calc_group_by_enum(int k, const std::vector< unsigned int > &used_filaments, const std::unordered_map< int, std::vector< int > > &unplaceable_limits, int *cost=nullptr)
Definition FilamentGroup.cpp:229
static constexpr long long ENUM_THRESHOLD
Definition FilamentGroup.cpp:36
static long long estimate_dedup_enum_count(int k, int n, const FilamentGroupContext &ctx)
Definition FilamentGroup.cpp:199
static void change_memoryed_heaps_to_arrays(MemoryedGroupHeap &heap, const int total_filament_num, const std::vector< unsigned int > &used_filaments, std::vector< std::vector< int > > &arrs)
Definition FilamentGroup.cpp:41

References calc_group_by_enum(), calc_group_by_kmedoids(), Slic3r::change_memoryed_heaps_to_arrays(), Slic3r::collect_sorted_used_filaments(), ctx, Slic3r::ENUM_THRESHOLD, Slic3r::estimate_dedup_enum_count(), Slic3r::FilamentGroupUtils::extract_unprintable_limit_indices(), m_memoryed_groups, m_memoryed_heap, and rebuild_nozzle_unprintables().

Referenced by calc_filament_group_for_flush().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_memoryed_groups()

std::vector< std::vector< int > > Slic3r::FilamentGroup::get_memoryed_groups ( ) const
inline
172{ return m_memoryed_groups; }

References m_memoryed_groups.

◆ rebuild_context()

void Slic3r::FilamentGroup::rebuild_context ( const std::unordered_map< int, std::vector< int > > & merged_filaments)
private
1001 {
1002 if (merged_filaments.empty())
1003 return;
1004
1005 FilamentGroupContext new_ctx = ctx;
1006
1007 std::unordered_map<int, int> filament_merge_map = get_merged_filament_map(merged_filaments);
1008
1009 // modify layer filaments
1010 for (auto& layer_filament : new_ctx.model_info.layer_filaments) {
1011 for (auto& f : layer_filament) {
1012 if (auto iter = filament_merge_map.find((int)(f)); iter != filament_merge_map.end()) {
1013 f = iter->second;
1014 }
1015 }
1016 }
1017
1018 for (auto& unprintables : new_ctx.model_info.unprintable_filaments) {
1019 std::set<int> new_unprintables;
1020 for (auto f : unprintables) {
1021 if (auto iter = filament_merge_map.find((int)(f)); iter != filament_merge_map.end()) {
1022 new_unprintables.insert(iter->second);
1023 }
1024 else {
1025 new_unprintables.insert(f);
1026 }
1027 }
1028 }
1029
1030 ctx = new_ctx;
1031 return;
1032 }
static std::unordered_map< int, int > get_merged_filament_map(const std::unordered_map< int, std::vector< int > > &merged_filaments)
Definition FilamentGroup.cpp:55

References ctx, Slic3r::f(), Slic3r::get_merged_filament_map(), Slic3r::FilamentGroupContext::ModelInfo::layer_filaments, Slic3r::FilamentGroupContext::model_info, and Slic3r::FilamentGroupContext::ModelInfo::unprintable_filaments.

Here is the call graph for this function:

◆ rebuild_nozzle_unprintables()

std::unordered_map< int, std::vector< int > > Slic3r::FilamentGroup::rebuild_nozzle_unprintables ( const std::vector< unsigned int > & used_filaments,
const std::unordered_map< int, std::vector< int > > & extruder_unprintables,
const std::vector< int > & filament_volume_map )
private
1319 {
1320 std::unordered_map<int, std::vector<int>> nozzle_unprintables;
1321
1322 for(size_t fidx = 0 ;fidx<used_filaments.size(); ++fidx){
1323 NozzleVolumeType expected_volume = NozzleVolumeType(filament_volume_map[used_filaments[fidx]]);
1324 std::vector<int> unexpected_extruders;
1325 if(extruder_unprintables.find(fidx) != extruder_unprintables.end()){
1326 unexpected_extruders = extruder_unprintables.at(fidx);
1327 }
1328
1329 auto unprintable_volumes = ctx.model_info.unprintable_volumes[used_filaments[fidx]];
1330
1331 std::vector<int> unprintable_nozzles;
1332 for(size_t nozzle_idx =0 ;nozzle_idx < ctx.nozzle_info.nozzle_list.size(); ++nozzle_idx){
1333 auto nozzle_info = ctx.nozzle_info.nozzle_list[nozzle_idx];
1334
1335 if(std::find(unexpected_extruders.begin(), unexpected_extruders.end(), nozzle_info.extruder_id)!= unexpected_extruders.end() || (expected_volume!=nvtHybrid && expected_volume != nozzle_info.volume_type) ||
1336 (unprintable_volumes.count(nozzle_info.volume_type) != 0))
1337 unprintable_nozzles.push_back(nozzle_idx);
1338 }
1339 if(unprintable_nozzles.empty())
1340 continue;
1341
1342 sort_remove_duplicates(unprintable_nozzles);
1343 nozzle_unprintables[fidx] = unprintable_nozzles;
1344 }
1345
1346 return nozzle_unprintables;
1347 }
NozzleVolumeType
Definition PrintConfig.hpp:618
@ nvtHybrid
Definition PrintConfig.hpp:621
void sort_remove_duplicates(std::vector< T > &vec)
Definition libslic3r.h:231

References ctx, Slic3r::nvtHybrid, and Slic3r::sort_remove_duplicates().

Referenced by calc_min_flush_group().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ rebuild_unprintables()

std::map< int, int > Slic3r::FilamentGroup::rebuild_unprintables ( const std::vector< unsigned int > & used_filaments,
const std::map< int, int > & extruder_unprintables )
private
926 {
927 std::map<int, int> ret;
928 for (int f_idx = 0; f_idx < used_filaments.size(); f_idx++) {
929 int unprintable_ext = -1;
930 if (extruder_unprintables.find(f_idx) != extruder_unprintables.end()) {
931 unprintable_ext = extruder_unprintables.at(f_idx);
932 }
933
934 bool multi_unprintable = false;
935 auto unprintable_volumes = ctx.model_info.unprintable_volumes[used_filaments[f_idx]];
936 for (int nozzle_idx = 0; nozzle_idx != ctx.nozzle_info.nozzle_list.size(); nozzle_idx++) {
937 auto nozzle_info = ctx.nozzle_info.nozzle_list[nozzle_idx];
938
939 if (unprintable_volumes.count(nozzle_info.volume_type)) {
940 if (unprintable_ext == -1)
941 unprintable_ext = nozzle_info.extruder_id;
942 else if (unprintable_ext != nozzle_info.extruder_id)
943 multi_unprintable = true;
944 }
945 }
946
947 if (!multi_unprintable && unprintable_ext != -1) ret[f_idx] = unprintable_ext;
948
949 }
950 return ret;
951 }

References ctx.

Referenced by calc_filament_group_for_match(), and calc_filament_group_for_tpu().

Here is the caller graph for this function:

◆ seperate_merged_filaments()

std::vector< int > Slic3r::FilamentGroup::seperate_merged_filaments ( const std::vector< int > & filament_map,
const std::unordered_map< int, std::vector< int > > & merged_filaments )
private
989 {
990 std::vector<int> ret_map = filament_map;
991 for (auto& elem : merged_filaments) {
992 int src = elem.first;
993 for (auto f : elem.second) {
994 ret_map[f] = ret_map[src];
995 }
996 }
997 return ret_map;
998 }

References Slic3r::f(), and Slic3r::filament_map().

Here is the call graph for this function:

◆ set_clustering_budget()

void Slic3r::FilamentGroup::set_clustering_budget ( const ClusteringBudget & budget)
inline
169{ m_clustering_budget = budget; }

References m_clustering_budget.

◆ try_merge_filaments()

std::unordered_map< int, std::vector< int > > Slic3r::FilamentGroup::try_merge_filaments ( )
private
954 {
955 std::unordered_map<int, std::vector<int>>merged_filaments;
956
957 std::unordered_map<std::string, std::vector<int>> merge_filament_map;
958
959 auto unprintable_stat_to_str = [unprintable_filaments = this->ctx.model_info.unprintable_filaments](int idx) {
960 std::string str;
961 for (size_t eid = 0; eid < unprintable_filaments.size(); ++eid) {
962 if (unprintable_filaments[eid].count(idx)) {
963 if (eid > 0)
964 str += ',';
965 str += std::to_string(idx);
966 }
967 }
968 return str;
969 };
970
971 for (size_t idx = 0; idx < ctx.model_info.filament_ids.size(); ++idx) {
972 std::string id = ctx.model_info.filament_ids[idx];
973 Color color = ctx.model_info.filament_info[idx].color;
974 std::string unprintable_str = unprintable_stat_to_str(idx);
975
976 std::string key = id + "," + color.to_hex_str(true) + "," + unprintable_str;
977 merge_filament_map[key].push_back(idx);
978 }
979
980 for (auto& elem : merge_filament_map) {
981 if (elem.second.size() > 1) {
982 merged_filaments[elem.second.front()] = elem.second;
983 }
984 }
985 return merged_filaments;
986 }
static unsigned int count
Definition AuxiliaryDataViewModel.cpp:246
std::array< std::size_t, 3 > Color
Definition ColorUtils.hpp:15

References count, ctx, Slic3r::FilamentGroupContext::model_info, Slic3r::FilamentGroupUtils::Color::to_hex_str(), and Slic3r::FilamentGroupContext::ModelInfo::unprintable_filaments.

Here is the call graph for this function:

Member Data Documentation

◆ ctx

◆ get_custom_seq

std::optional<std::function<bool(int, std::vector<int>&)> > Slic3r::FilamentGroup::get_custom_seq

◆ m_clustering_budget

ClusteringBudget Slic3r::FilamentGroup::m_clustering_budget
private

◆ m_memoryed_groups

std::vector<std::vector<int> > Slic3r::FilamentGroup::m_memoryed_groups
private

◆ m_memoryed_heap

MemoryedGroupHeap Slic3r::FilamentGroup::m_memoryed_heap
private

The documentation for this class was generated from the following files: