#ifndef TOOL_ORDER_UTILS_HPP #define TOOL_ORDER_UTILS_HPP #include #include #include #include #include #include #include #include #include "../MultiNozzleUtils.hpp" namespace Slic3r { using FlushMatrix = std::vector>; namespace MaxFlowGraph { const int INF = std::numeric_limits::max(); const int INVALID_ID = -1; // Upper bound for MCMF edge cost to prevent int overflow in SPFA causing infinite loops constexpr int MCMF_MAX_EDGE_COST = 10000000; } // Namespace-scope edge shared by the max-flow / min-cost-max-flow solvers below. // The default cost keeps the plain max-flow solvers (which never read cost) source-compatible. struct Edge { int from, to, capacity, cost, flow; Edge(int u, int v, int cap, int cst = 0) : from(u), to(v), capacity(cap), cost(cst), flow(0) {} }; class MaxFlowSolver { public: MaxFlowSolver(const std::vector& u_nodes, const std::vector& v_nodes, const std::unordered_map>& uv_link_limits = {}, const std::unordered_map>& uv_unlink_limits = {}, const std::vector& u_capacity = {}, const std::vector& v_capacity = {}, const std::vector, int>>& v_group_capacity = {} ); std::vector solve(); private: void add_edge(int from, int to, int capacity); int total_nodes; int source_id; int sink_id; std::vectoredges; std::vectorl_nodes; std::vectorr_nodes; std::vector>adj; }; struct MinCostMaxFlow; struct MaxFlowWithLowerBounds; class GeneralMinCostSolver { public: GeneralMinCostSolver(const std::vector>& matrix_, const std::vector& u_nodes, const std::vector& v_nodes); std::vector solve(); ~GeneralMinCostSolver(); private: std::unique_ptr m_solver; }; class GeneralMinCostLowerBoundsSolver { public: GeneralMinCostLowerBoundsSolver( const std::vector &matrix_, const std::vector& u_nodes, const std::vector& v_nodes, const std::vector& v_nodes_group, const std::unordered_map>& uv_link_limits = {}, const std::unordered_map>& uv_unlink_limits = {}); std::vector solve(); ~GeneralMinCostLowerBoundsSolver(); private: void build_feasible_graph(const std::unordered_set& no_lower_groups); void build_graph_with_feasible_result(); void add_edge_with_lower_bound(int from, int to, int lower, int upper, int cost); int get_distance(const int idx_in_left,const int idx_in_right); private: std::unique_ptr m_solver_lower_bounds; std::unique_ptr m_solver_min_cost; std::vector flush_matrix; std::vector l_nodes; std::vector r_nodes; std::vector r_nodes_group; std::unordered_map> m_uv_link_limits; std::unordered_map> m_uv_unlink_limits; int num_groups = 0; // support lower bounds struct LowerBoundEdge{ int edge_id; int lower; }; std::vector demand; std::vector lower_bound_edges; int super_source = -1; int super_sink = -1; int source_id = -1; int sink_id = -1; int max_flow_edges = 0; }; class GroupMinCostFlowSolver { public: GroupMinCostFlowSolver( const std::vector &matrix_, const std::vector &u_nodes, const std::vector &v_nodes, const std::vector &v_nodes_group, const std::unordered_map> &uv_link_limits = {}, const std::unordered_map> &uv_unlink_limits = {}); std::vector solve(); ~GroupMinCostFlowSolver(); private: void build_graph(); int get_flush_cost(int l_idx, int r_idx); std::unique_ptr m_solver; std::vector flush_matrix; std::vector l_nodes; std::vector r_nodes; std::vector r_nodes_group; std::unordered_map> m_uv_link_limits; std::unordered_map> m_uv_unlink_limits; int num_groups = 0; }; class MinFlushFlowSolver { public: MinFlushFlowSolver(const std::vector>& matrix_, const std::vector& u_nodes, const std::vector& v_nodes, const std::unordered_map>& uv_link_limits = {}, const std::unordered_map>& uv_unlink_limits = {}, const std::vector& u_capacity = {}, const std::vector& v_capacity = {}, const std::vector, int>>& v_group_capacity = {} ); std::vector solve(); ~MinFlushFlowSolver(); private: std::unique_ptr m_solver; }; class MatchModeGroupSolver { public: MatchModeGroupSolver(const std::vector>& matrix_, const std::vector& u_nodes, const std::vector& v_nodes, const std::vector& v_capacity, const std::unordered_map>& uv_unlink_limits = {}); std::vector solve(); ~MatchModeGroupSolver(); private: std::unique_ptr m_solver; }; std::vector get_extruders_order(const std::vector> &wipe_volumes, const std::vector &curr_layer_extruders, const std::vector &next_layer_extruders, const std::optional &start_extruder_id, bool use_forcast = false, float *cost = nullptr); int reorder_filaments_for_minimum_flush_volume(const std::vector &filament_lists, const std::vector &filament_maps, const std::vector> &layer_filaments, const std::vector &flush_matrix, std::optional &)>> get_custom_seq, std::vector> *filament_sequences, const std::unordered_map& nozzle_status = {}); // Order filaments within a per-nozzle grouping result (multi-nozzle extruders). Threads a // NozzleStatusRecorder describing the initial physical nozzle occupancy so the reorder can reward // keeping an already-loaded filament in place. int reorder_filaments_for_multi_nozzle_extruder(const std::vector& filament_lists, const MultiNozzleUtils::LayeredNozzleGroupResult& nozzle_group_result, const std::vector>& layer_filaments, const std::vector& flush_matrix, const std::function&)> get_custom_seq, std::vector> * filament_sequences, const MultiNozzleUtils::NozzleStatusRecorder& initial_status = {}); } #endif // !TOOL_ORDER_UTILS_HPP