From 0716b8518ef62e0eac7c45de8bafb1458d8f9a8e Mon Sep 17 00:00:00 2001 From: "xun.zhang" Date: Thu, 8 Aug 2024 11:23:29 +0800 Subject: [PATCH] ENH: add tool order function 1.Use min cost max flow to solve the tool order jira:NEW Signed-off-by: xun.zhang Change-Id: I909845039b67c7fe3ddd42580ad3f1d71d52262d --- src/libslic3r/FilamentGroup.hpp | 2 + src/libslic3r/GCode/ToolOrderUtils.cpp | 121 ++++++++++++++++++++++++- src/libslic3r/GCode/ToolOrderUtils.hpp | 31 ++++++- src/libslic3r/GCode/ToolOrdering.cpp | 23 ++--- 4 files changed, 159 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/FilamentGroup.hpp b/src/libslic3r/FilamentGroup.hpp index 20e54b40d..075787873 100644 --- a/src/libslic3r/FilamentGroup.hpp +++ b/src/libslic3r/FilamentGroup.hpp @@ -52,6 +52,7 @@ namespace Slic3r std::optional&)>> get_custom_seq; }; + class KMediods { enum INIT_TYPE @@ -83,5 +84,6 @@ namespace Slic3r private: std::vectorm_filament_labels; }; + } #endif // !FILAMENT_GROUP_HPP diff --git a/src/libslic3r/GCode/ToolOrderUtils.cpp b/src/libslic3r/GCode/ToolOrderUtils.cpp index aaee24e89..6d7f60fb5 100644 --- a/src/libslic3r/GCode/ToolOrderUtils.cpp +++ b/src/libslic3r/GCode/ToolOrderUtils.cpp @@ -8,6 +8,124 @@ namespace Slic3r { + MCMF::MCMF(const FlushMatrix& matrix_, const std::vector& u_nodes, const std::vector& v_nodes) + { + matrix = matrix_; + l_nodes = u_nodes; + r_nodes = v_nodes; + + total_nodes = u_nodes.size() + v_nodes.size() + 2; + + source_id = total_nodes - 2; + sink_id = total_nodes - 1; + + adj.resize(total_nodes); + + //add edge from source to left nodes,set capacity to 1, cost to 0 + for (int i = 0; i < l_nodes.size(); ++i) + add_edge(source_id, i, 1, 0); + + //add edge from right nodes to sink,set capacity to 1, cost to 0 + for (int i = 0; i < r_nodes.size(); ++i) + add_edge(l_nodes.size() + i, sink_id, 1, 0); + + for (int i = 0; i < l_nodes.size(); ++i) { + int from_idx = i; + for (int j = 0; j < r_nodes.size(); ++j) { + int to_idx = l_nodes.size() + j; + add_edge(from_idx, to_idx, 1, get_distance(i, j)); + } + } + } + + std::vector MCMF::solve() + { + while (spfa(source_id, sink_id)); + + std::vectormatching(l_nodes.size(), -1); + // to get the match info, just traverse the left nodes and + // check the edges with flow > 0 and linked to right nodes + for (int u = 0; u < l_nodes.size(); ++u) { + for (int eid : adj[u]) { + Edge& e = edges[eid]; + if (e.flow > 0 && e.to >= l_nodes.size() && e.to < l_nodes.size() + r_nodes.size()) + matching[e.from] = r_nodes[e.to - l_nodes.size()]; + } + } + + return matching; + } + + void MCMF::add_edge(int from, int to, int capacity, int cost) + { + adj[from].emplace_back(edges.size()); + edges.emplace_back(from, to, capacity, cost); + //also add reverse edge ,set capacity to zero,cost to negative + adj[to].emplace_back(edges.size()); + edges.emplace_back(to, from, 0, -cost); + } + + bool MCMF::spfa(int source, int sink) + { + std::vectordist(total_nodes, INF); + std::vectorin_queue(total_nodes, false); + std::vectorflow(total_nodes, INF); + std::vectorprev(total_nodes, 0); + + std::queueq; + q.push(source); + in_queue[source] = true; + dist[source] = 0; + + while (!q.empty()) { + int now_at = q.front(); + q.pop(); + in_queue[now_at] = false; + + for (auto eid : adj[now_at]) //traverse all linked edges + { + Edge& e = edges[eid]; + if (e.flowdist[now_at] + e.cost) { + dist[e.to] = dist[now_at] + e.cost; + prev[e.to] = eid; + flow[e.to] = std::min(flow[now_at], e.capacity - e.flow); + if (!in_queue[e.to]) { + q.push(e.to); + in_queue[e.to] = true; + } + } + } + } + + if (dist[sink] == INF) + return false; + + int now_at = sink; + while (now_at != source) { + int prev_edge = prev[now_at]; + edges[prev_edge].flow += flow[sink]; + edges[prev_edge ^ 1].flow -= flow[sink]; + now_at = edges[prev_edge].from; + } + + return true; + } + + int MCMF::get_distance(int idx_in_left, int idx_in_right) + { + if (l_nodes[idx_in_left] == -1) { + return 0; + //TODO: test more here + int sum = 0; + for (int i = 0; i < matrix.size(); ++i) + sum += matrix[i][idx_in_right]; + sum /= matrix.size(); + return -sum; + } + + return matrix[l_nodes[idx_in_left]][r_nodes[idx_in_right]]; + } + //solve the problem by searching the least flush of current filament static std::vector solve_extruder_order_with_greedy(const std::vector>& wipe_volumes, const std::vector curr_layer_extruders, @@ -171,6 +289,7 @@ namespace Slic3r } + // get best filament order of single nozzle std::vector get_extruders_order(const std::vector>& wipe_volumes, const std::vector& curr_layer_extruders, @@ -202,7 +321,6 @@ namespace Slic3r } - int reorder_filaments_for_minimum_flush_volume(const std::vector& filament_lists, const std::vector& filament_maps, const std::vector>& layer_filaments, @@ -378,5 +496,4 @@ namespace Slic3r return cost; } - } diff --git a/src/libslic3r/GCode/ToolOrderUtils.hpp b/src/libslic3r/GCode/ToolOrderUtils.hpp index 14ba2a7b9..4cc14e1b1 100644 --- a/src/libslic3r/GCode/ToolOrderUtils.hpp +++ b/src/libslic3r/GCode/ToolOrderUtils.hpp @@ -9,6 +9,36 @@ namespace Slic3r { using FlushMatrix = std::vector>; +class MCMF +{ + const int INF = std::numeric_limits::max(); + struct Edge + { + int from, to, capacity, cost, flow; + Edge(int u, int v, int cap, int cst) : from(u), to(v), capacity(cap), cost(cst), flow(0) {} + }; + +public: + MCMF(const FlushMatrix &matrix_, const std::vector &u_nodes, const std::vector &v_nodes); + std::vector solve(); + +private: + void add_edge(int from, int to, int capacity, int cost); + bool spfa(int source, int sink); + int get_distance(int idx_in_left, int idx_in_right); + +private: + FlushMatrix matrix; + std::vector l_nodes; + std::vector r_nodes; + + int total_nodes; + int source_id; + int sink_id; + + std::vector edges; + std::vector> adj; +}; std::vector get_extruders_order(const std::vector> &wipe_volumes, const std::vector &curr_layer_extruders, @@ -24,6 +54,5 @@ int reorder_filaments_for_minimum_flush_volume(const std::vector & std::optional &)>> get_custom_seq, std::vector> *filament_sequences); - } #endif // !TOOL_ORDER_UTILS_HPP diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index ba76e9c51..e77d697bd 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -851,29 +851,22 @@ std::vector ToolOrdering::get_recommended_filament_maps(const std::vectorret(filament_nums,0); // if mutli_extruder, calc group,otherwise set to 0 - if (extruder_nums == 2) - { + if (extruder_nums == 2) { std::vector extruder_ams_count_str = print_config->extruder_ams_count.values; - auto extruder_ams_counts = get_extruder_ams_count(extruder_ams_count_str); - std::vector group_size = { 16, 16 }; + auto extruder_ams_counts = get_extruder_ams_count(extruder_ams_count_str); + std::vector group_size = {16, 16}; if (extruder_ams_counts.size() > 0) { assert(extruder_ams_counts.size() == 2); for (int i = 0; i < extruder_ams_counts.size(); ++i) { - group_size[i] = 0; - const auto& ams_count = extruder_ams_counts[i]; - for (auto iter = ams_count.begin(); iter != ams_count.end(); ++iter) { - group_size[i] += iter->first * iter->second; - } + group_size[i] = 0; + const auto &ams_count = extruder_ams_counts[i]; + for (auto iter = ams_count.begin(); iter != ams_count.end(); ++iter) { group_size[i] += iter->first * iter->second; } } } - FilamentGroup fg( - nozzle_flush_mtx, - (int)filament_nums, - group_size - ); + FilamentGroup fg(nozzle_flush_mtx, (int) filament_nums, group_size); fg.get_custom_seq = get_custom_seq; - ret = fg.calc_filament_group(layer_filaments, FGStrategy::BestFit); + ret = fg.calc_filament_group(layer_filaments, FGStrategy::BestFit); } return ret;