ENH: refine filament group algorithm
1.Use max flow network to handle limit 2.Support setting master extruder id 3.Fix the issue in the KMedoids algorithm where data is overwritten after each retry. jira:NONE Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Idd2bedf39f61e7a65eb4199852f60b8fbebe0a7d
This commit is contained in:
parent
2ee87600cb
commit
3cfb49a1b9
|
@ -99,6 +99,84 @@ namespace Slic3r
|
|||
a = hexToByte(hexstr.substr(7, 2));
|
||||
}
|
||||
|
||||
bool can_swap_groups(const int extruder_id_0, const std::set<int>& group_0, const int extruder_id_1, const std::set<int>& group_1, const FilamentGroupContext& ctx)
|
||||
{
|
||||
std::vector<std::set<int>>extruder_unprintables(2);
|
||||
{
|
||||
std::vector<std::set<int>> physical_unprintables = ctx.physical_unprintables;
|
||||
std::vector<std::set<int>> geometric_unprintables = ctx.geometric_unprintables;
|
||||
remove_intersection(physical_unprintables[0], physical_unprintables[1]);
|
||||
remove_intersection(geometric_unprintables[0], geometric_unprintables[1]);
|
||||
std::map<int, std::vector<int>>unplaceable_limts;
|
||||
for (auto& unprintables : { physical_unprintables,geometric_unprintables }) {
|
||||
for (auto& group_id : { extruder_id_0,extruder_id_1 }) {
|
||||
for (auto f : unprintables[group_id]) {
|
||||
// TODO: xcr: check whether group_id has been inside the vector ?
|
||||
if (unplaceable_limts.count(f) == 0)
|
||||
unplaceable_limts[f].emplace_back(group_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& elem : unplaceable_limts) {
|
||||
sort_remove_duplicates(elem.second);
|
||||
}
|
||||
|
||||
for (auto& elem : unplaceable_limts) {
|
||||
for (auto& eid : elem.second) {
|
||||
if (eid == extruder_id_0) {
|
||||
extruder_unprintables[0].insert(elem.first);
|
||||
}
|
||||
if (eid == extruder_id_1) {
|
||||
extruder_unprintables[1].insert(elem.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check printable limits
|
||||
for (auto fid : group_0) {
|
||||
if (extruder_unprintables[1].count(fid) > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto fid : group_1) {
|
||||
if (extruder_unprintables[0].count(fid) > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// check extruder capacity ,if result before exchange meets the constraints and the result after exchange does not meet the constraints, return false
|
||||
if (ctx.max_group_size[extruder_id_0] >= group_0.size() && ctx.max_group_size[extruder_id_1] >= group_1.size() && (ctx.max_group_size[extruder_id_0] < group_1.size() || ctx.max_group_size[extruder_id_1] < group_0.size()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// only support extruder nums with 2, try to swap the master extruder id with the other extruder id
|
||||
bool optimize_group_for_master_extruder(const std::vector<unsigned int>& used_filaments,const FilamentGroupContext& ctx, std::vector<int>& filament_map)
|
||||
{
|
||||
std::unordered_map<int, std::set<int>> groups;
|
||||
for (size_t idx = 0; idx < used_filaments.size(); ++idx) {
|
||||
int filament_id = used_filaments[idx];
|
||||
int group_id = filament_map[filament_id];
|
||||
groups[group_id].insert(filament_id);
|
||||
}
|
||||
|
||||
int none_master_extruder_id = 1 - ctx.master_extruder_id;
|
||||
assert(0 <= none_master_extruder_id && none_master_extruder_id <= 1);
|
||||
|
||||
if (can_swap_groups(none_master_extruder_id, groups[none_master_extruder_id], ctx.master_extruder_id, groups[ctx.master_extruder_id], ctx)
|
||||
&& groups[none_master_extruder_id].size()>groups[ctx.master_extruder_id].size()) {
|
||||
for (auto fid : groups[none_master_extruder_id])
|
||||
filament_map[fid] = ctx.master_extruder_id;
|
||||
for (auto fid : groups[ctx.master_extruder_id])
|
||||
filament_map[fid] = none_master_extruder_id;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<int> select_best_group_for_ams(const std::vector<std::vector<int>>& map_lists, const std::vector<unsigned int>& used_filaments, const std::vector<std::string>& used_filament_colors_str, const std::vector<std::vector<std::string>>& ams_filament_colors_str)
|
||||
{
|
||||
// change the color str to real colors
|
||||
|
@ -142,7 +220,7 @@ namespace Slic3r
|
|||
std::vector<int>l_nodes(group_colors[i].size()), r_nodes(ams_filament_colors[i].size());
|
||||
std::iota(l_nodes.begin(), l_nodes.end(), 0);
|
||||
std::iota(r_nodes.begin(), r_nodes.end(), 0);
|
||||
MCMF mcmf(distance_matrix, l_nodes, r_nodes);
|
||||
MinCostMaxFlow mcmf(distance_matrix, l_nodes, r_nodes);
|
||||
auto ams_map = mcmf.solve();
|
||||
|
||||
for (size_t idx = 0; idx < ams_map.size(); ++idx) {
|
||||
|
@ -290,7 +368,7 @@ namespace Slic3r
|
|||
new_group_size[gid] -= 1;
|
||||
}
|
||||
else {
|
||||
label = 0;
|
||||
label = m_default_group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -377,6 +455,15 @@ namespace Slic3r
|
|||
|
||||
if (m_elem_count < m_k) {
|
||||
m_cluster_labels = cluster_small_data(m_unplaceable_limits, m_max_cluster_size);
|
||||
{
|
||||
std::vector<int>cluster_center(m_k, -1);
|
||||
for (size_t idx = 0; idx < m_cluster_labels.size(); ++idx) {
|
||||
if (cluster_center[m_cluster_labels[idx]] == -1)
|
||||
cluster_center[m_cluster_labels[idx]] = idx;
|
||||
}
|
||||
MemoryedGroup g(m_cluster_labels, calc_cost(m_cluster_labels, cluster_center), 1);
|
||||
update_memoryed_groups(g, memory_threshold, memoryed_groups);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -402,10 +489,7 @@ namespace Slic3r
|
|||
}
|
||||
|
||||
{
|
||||
MemoryedGroup g;
|
||||
g.prefer_level = 1; // in non enum mode, we use the same prefer level
|
||||
g.cost = new_cost;
|
||||
g.group = new_labels;
|
||||
MemoryedGroup g(new_labels,new_cost,1);
|
||||
update_memoryed_groups(g, memory_threshold, memoryed_groups);
|
||||
}
|
||||
|
||||
|
@ -433,10 +517,11 @@ namespace Slic3r
|
|||
std::vector<unsigned int> used_filaments = collect_sorted_used_filaments(layer_filaments);
|
||||
|
||||
int used_filament_num = used_filaments.size();
|
||||
|
||||
if (used_filament_num < 10)
|
||||
return calc_filament_group_by_enum(layer_filaments, used_filaments, g_strategy, cost);
|
||||
else
|
||||
return calc_filament_group_by_pam2(layer_filaments, used_filaments, g_strategy, cost, 100);
|
||||
return calc_filament_group_by_pam2(layer_filaments, used_filaments, g_strategy, cost, 300);
|
||||
}
|
||||
|
||||
// sorted used_filaments
|
||||
|
@ -529,10 +614,7 @@ namespace Slic3r
|
|||
}
|
||||
|
||||
{
|
||||
MemoryedGroup mg;
|
||||
mg.prefer_level = prefer_level;
|
||||
mg.cost = total_cost;
|
||||
mg.group = std::move(filament_maps);
|
||||
MemoryedGroup mg(filament_maps,total_cost,prefer_level);
|
||||
update_memoryed_groups(mg, memory_threshold, memoryed_groups);
|
||||
}
|
||||
}
|
||||
|
@ -553,9 +635,7 @@ namespace Slic3r
|
|||
// sorted used_filaments
|
||||
std::vector<int> FilamentGroup::calc_filament_group_by_pam2(const std::vector<std::vector<unsigned int>>& layer_filaments, const std::vector<unsigned int>& used_filaments, const FGStrategy& g_strategy, int*cost,int timeout_ms)
|
||||
{
|
||||
std::vector<int>filament_labels_ret(m_context.total_filament_num, 0);
|
||||
if (used_filaments.size() == 1)
|
||||
return filament_labels_ret;
|
||||
std::vector<int>filament_labels_ret(m_context.total_filament_num, m_context.master_extruder_id);
|
||||
|
||||
std::map<int, int>unplaceable_limits;
|
||||
{
|
||||
|
@ -575,13 +655,13 @@ namespace Slic3r
|
|||
}
|
||||
|
||||
auto distance_evaluator = std::make_shared<FlushDistanceEvaluator>(m_context.flush_matrix[0], used_filaments, layer_filaments);
|
||||
KMediods2 PAM((int)used_filaments.size(),distance_evaluator);
|
||||
KMediods2 PAM((int)used_filaments.size(),distance_evaluator,m_context.master_extruder_id);
|
||||
PAM.set_max_cluster_size(m_context.max_group_size);
|
||||
PAM.set_unplaceable_limits(unplaceable_limits);
|
||||
PAM.set_memory_threshold(memory_threshold);
|
||||
PAM.do_clustering(g_strategy, timeout_ms);
|
||||
std::vector<int>filament_labels = PAM.get_cluster_labels();
|
||||
|
||||
std::vector<int>filament_labels = PAM.get_cluster_labels();
|
||||
|
||||
{
|
||||
auto memoryed_groups = PAM.get_memoryed_groups();
|
||||
|
|
|
@ -33,8 +33,6 @@ namespace Slic3r
|
|||
Color(const std::string& hexstr);
|
||||
};
|
||||
|
||||
std::vector<int> select_best_group_for_ams(const std::vector<std::vector<int>>& map_lists, const std::vector<unsigned int>& used_filaments, const std::vector<std::string>& used_filament_colors, const std::vector<std::vector<std::string>>& ams_filament_colros);
|
||||
|
||||
namespace FilamentGroupUtils
|
||||
{
|
||||
struct FlushTimeMachine
|
||||
|
@ -57,12 +55,15 @@ namespace Slic3r
|
|||
};
|
||||
|
||||
struct MemoryedGroup {
|
||||
int cost{ 0 };
|
||||
int prefer_level{ 0 };
|
||||
std::vector<int>group;
|
||||
MemoryedGroup() = default;
|
||||
MemoryedGroup(const std::vector<int>& group_, const int cost_, const int prefer_level_) :group(group_), cost(cost_), prefer_level(prefer_level_) {}
|
||||
bool operator>(const MemoryedGroup& other) const {
|
||||
return prefer_level < other.prefer_level || (prefer_level == other.prefer_level && cost > other.cost);
|
||||
}
|
||||
|
||||
int cost{ 0 };
|
||||
int prefer_level{ 0 };
|
||||
std::vector<int>group;
|
||||
};
|
||||
using MemoryedGroupHeap = std::priority_queue<MemoryedGroup, std::vector<MemoryedGroup>, std::greater<MemoryedGroup>>;
|
||||
|
||||
|
@ -76,8 +77,14 @@ namespace Slic3r
|
|||
std::vector<std::set<int>>geometric_unprintables;
|
||||
std::vector<int>max_group_size;
|
||||
int total_filament_num;
|
||||
int master_extruder_id;
|
||||
};
|
||||
|
||||
std::vector<int> select_best_group_for_ams(const std::vector<std::vector<int>>& map_lists, const std::vector<unsigned int>& used_filaments, const std::vector<std::string>& used_filament_colors, const std::vector<std::vector<std::string>>& ams_filament_colros);
|
||||
|
||||
bool optimize_group_for_master_extruder(const std::vector<unsigned int>& used_filaments, const FilamentGroupContext& ctx, std::vector<int>& filament_map);
|
||||
|
||||
bool can_swap_groups(const int extruder_id_0, const std::set<int>& group_0, const int extruder_id_1, const std::set<int>& group_1, const FilamentGroupContext& ctx);
|
||||
|
||||
class FlushDistanceEvaluator
|
||||
{
|
||||
|
@ -123,9 +130,10 @@ namespace Slic3r
|
|||
Farthest
|
||||
};
|
||||
public:
|
||||
KMediods2(const int elem_count, const std::shared_ptr<FlushDistanceEvaluator>& evaluator) :
|
||||
KMediods2(const int elem_count, const std::shared_ptr<FlushDistanceEvaluator>& evaluator, int default_group_id = 0) :
|
||||
m_evaluator{ evaluator },
|
||||
m_elem_count{ elem_count }
|
||||
m_elem_count{ elem_count },
|
||||
m_default_group_id{ default_group_id }
|
||||
{
|
||||
m_max_cluster_size = std::vector<int>(m_k, DEFAULT_CLUSTER_SIZE);
|
||||
}
|
||||
|
@ -138,7 +146,7 @@ namespace Slic3r
|
|||
|
||||
void do_clustering(const FGStrategy& g_strategy,int timeout_ms = 100);
|
||||
|
||||
void set_memory_threshold(double threshold) { memory_threshold; }
|
||||
void set_memory_threshold(double threshold) { memory_threshold = threshold; }
|
||||
MemoryedGroupHeap get_memoryed_groups()const { return memoryed_groups; }
|
||||
|
||||
std::vector<int>get_cluster_labels()const { return m_cluster_labels; }
|
||||
|
@ -147,17 +155,17 @@ namespace Slic3r
|
|||
std::vector<int>cluster_small_data(const std::map<int, int>& unplaceable_limits, const std::vector<int>& group_size);
|
||||
std::vector<int>assign_cluster_label(const std::vector<int>& center, const std::map<int, int>& unplaceable_limits, const std::vector<int>& group_size, const FGStrategy& strategy);
|
||||
int calc_cost(const std::vector<int>& labels, const std::vector<int>& medoids);
|
||||
private:
|
||||
protected:
|
||||
FilamentGroupUtils::MemoryedGroupHeap memoryed_groups;
|
||||
std::shared_ptr<FlushDistanceEvaluator> m_evaluator;
|
||||
std::map<int, int>m_unplaceable_limits;
|
||||
std::vector<int>m_max_cluster_size;
|
||||
int m_elem_count;
|
||||
const int m_k = 2;
|
||||
|
||||
double memory_threshold{ 0 };
|
||||
FilamentGroupUtils::MemoryedGroupHeap memoryed_groups;
|
||||
|
||||
std::vector<int>m_cluster_labels;
|
||||
std::vector<int>m_max_cluster_size;
|
||||
|
||||
const int m_k = 2;
|
||||
int m_elem_count;
|
||||
int m_default_group_id{ 0 };
|
||||
double memory_threshold{ 0 };
|
||||
};
|
||||
}
|
||||
#endif // !FILAMENT_GROUP_HPP
|
||||
|
|
|
@ -8,8 +8,123 @@
|
|||
namespace Slic3r
|
||||
{
|
||||
|
||||
MCMF::MCMF(const FlushMatrix& matrix_, const std::vector<int>& u_nodes, const std::vector<int>& v_nodes)
|
||||
MaxFlow::MaxFlow(const std::vector<int>& u_nodes, const std::vector<int>& v_nodes,
|
||||
const std::unordered_map<int, std::vector<int>>& uv_link_limits,
|
||||
const std::unordered_map<int, std::vector<int>>& uv_unlink_limits,
|
||||
const std::vector<int>& u_capacity,
|
||||
const std::vector<int>& v_capacity)
|
||||
{
|
||||
assert(u_capacity.empty() || u_capacity.size() == u_nodes.size());
|
||||
assert(v_capacity.empty() || v_capacity.size() == v_nodes.size());
|
||||
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
|
||||
for (int idx = 0; idx < l_nodes.size(); ++idx) {
|
||||
int capacity = u_capacity.empty() ? 1 : u_capacity[idx];
|
||||
add_edge(source_id, idx, capacity);
|
||||
}
|
||||
// add edge from right nodes to sink node
|
||||
for (int idx = 0; idx < r_nodes.size(); ++idx) {
|
||||
int capacity = v_capacity.empty() ? 1 : v_capacity[idx];
|
||||
add_edge(l_nodes.size() + idx, sink_id, capacity);
|
||||
}
|
||||
|
||||
// add edge from left nodes to right nodes
|
||||
for (int i = 0; i < l_nodes.size(); ++i) {
|
||||
int from_idx = i;
|
||||
// process link limits , i can only link to uv_link_limits
|
||||
if (auto iter = uv_link_limits.find(i); iter != uv_link_limits.end()) {
|
||||
for (auto r_id : iter->second)
|
||||
add_edge(from_idx, l_nodes.size() + r_id, 1);
|
||||
continue;
|
||||
}
|
||||
// process unlink limits
|
||||
std::optional<std::vector<int>> unlink_limits;
|
||||
if (auto iter = uv_unlink_limits.find(i); iter != uv_unlink_limits.end())
|
||||
unlink_limits = iter->second;
|
||||
|
||||
for (int j = 0; j < r_nodes.size(); ++j) {
|
||||
// check whether i can link to j
|
||||
if (unlink_limits.has_value() && std::find(unlink_limits->begin(), unlink_limits->end(), j) != unlink_limits->end())
|
||||
continue;
|
||||
add_edge(from_idx, l_nodes.size() + j, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MaxFlow::add_edge(int from, int to, int capacity)
|
||||
{
|
||||
adj[from].emplace_back(edges.size());
|
||||
edges.emplace_back(from, to, capacity);
|
||||
//also add reverse edge ,set capacity to zero
|
||||
adj[to].emplace_back(edges.size());
|
||||
edges.emplace_back(to, from, 0);
|
||||
}
|
||||
|
||||
std::vector<int> MaxFlow::solve() {
|
||||
std::vector<int> augment;
|
||||
std::vector<int> previous(total_nodes, 0);
|
||||
while (1) {
|
||||
std::vector<int>(total_nodes, 0).swap(augment);
|
||||
std::queue<int> travel;
|
||||
travel.push(source_id);
|
||||
augment[source_id] = INF;
|
||||
while (!travel.empty()) {
|
||||
int from = travel.front();
|
||||
travel.pop();
|
||||
|
||||
// traverse all linked edges
|
||||
for (int i = 0; i < adj[from].size(); ++i) {
|
||||
int eid = adj[from][i];
|
||||
Edge& tmp = edges[eid];
|
||||
if (augment[tmp.to] == 0 && tmp.capacity > tmp.flow) {
|
||||
previous[tmp.to] = eid;
|
||||
augment[tmp.to] = std::min(augment[from], tmp.capacity - tmp.flow);
|
||||
travel.push(tmp.to);
|
||||
}
|
||||
}
|
||||
|
||||
// already find an extend path, stop and do update
|
||||
if (augment[sink_id] != 0)
|
||||
break;
|
||||
}
|
||||
// no longer have extend path
|
||||
if (augment[sink_id] == 0)
|
||||
break;
|
||||
|
||||
for (int i = sink_id; i != source_id; i = edges[previous[i]].from) {
|
||||
edges[previous[i]].flow += augment[sink_id];
|
||||
edges[previous[i] ^ 1].flow -= augment[sink_id];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> matching(l_nodes.size(), -1);
|
||||
// to get the match info, just traverse the left nodes and
|
||||
// check the edge 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;
|
||||
}
|
||||
|
||||
MinCostMaxFlow::MinCostMaxFlow(const std::vector<std::vector<float>>& matrix_, const std::vector<int>& u_nodes, const std::vector<int>& v_nodes,
|
||||
const std::unordered_map<int, std::vector<int>>& uv_link_limits,
|
||||
const std::unordered_map<int, std::vector<int>>& uv_unlink_limits,
|
||||
const std::vector<int>& u_capacity,
|
||||
const std::vector<int>& v_capacity)
|
||||
{
|
||||
assert(u_capacity.empty() || u_capacity.size() == u_nodes.size());
|
||||
assert(v_capacity.empty() || v_capacity.size() == v_nodes.size());
|
||||
matrix = matrix_;
|
||||
l_nodes = u_nodes;
|
||||
r_nodes = v_nodes;
|
||||
|
@ -21,24 +136,39 @@ namespace Slic3r
|
|||
|
||||
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);
|
||||
|
||||
// add edge from source to left nodes,cost to 0
|
||||
for (int i = 0; i < l_nodes.size(); ++i) {
|
||||
int capacity = u_capacity.empty() ? 1 : u_capacity[i];
|
||||
add_edge(source_id, i, capacity, 0);
|
||||
}
|
||||
// add edge from right nodes to sink,cost to 0
|
||||
for (int i = 0; i < r_nodes.size(); ++i) {
|
||||
int capacity = v_capacity.empty() ? 1 : v_capacity[i];
|
||||
add_edge(l_nodes.size() + i, sink_id, capacity, 0);
|
||||
}
|
||||
// add edge from left node to right nodes
|
||||
for (int i = 0; i < l_nodes.size(); ++i) {
|
||||
int from_idx = i;
|
||||
// process link limits, i can only link to link_limits
|
||||
if (auto iter = uv_link_limits.find(i); iter != uv_link_limits.end()) {
|
||||
for (auto r_id : iter->second)
|
||||
add_edge(from_idx, l_nodes.size() + r_id, 1, get_distance(i, r_id));
|
||||
continue;
|
||||
}
|
||||
|
||||
// process unlink limits, check whether i can link to j
|
||||
std::optional<std::vector<int>> unlink_limits;
|
||||
if (auto iter = uv_unlink_limits.find(i); iter != uv_unlink_limits.end())
|
||||
unlink_limits = iter->second;
|
||||
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));
|
||||
if (unlink_limits.has_value() && std::find(unlink_limits->begin(), unlink_limits->end(), j) != unlink_limits->end())
|
||||
continue;
|
||||
add_edge(from_idx, l_nodes.size() + j, 1, get_distance(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> MCMF::solve()
|
||||
std::vector<int> MinCostMaxFlow::solve()
|
||||
{
|
||||
while (spfa(source_id, sink_id));
|
||||
|
||||
|
@ -56,7 +186,7 @@ namespace Slic3r
|
|||
return matching;
|
||||
}
|
||||
|
||||
void MCMF::add_edge(int from, int to, int capacity, int cost)
|
||||
void MinCostMaxFlow::add_edge(int from, int to, int capacity, int cost)
|
||||
{
|
||||
adj[from].emplace_back(edges.size());
|
||||
edges.emplace_back(from, to, capacity, cost);
|
||||
|
@ -65,7 +195,7 @@ namespace Slic3r
|
|||
edges.emplace_back(to, from, 0, -cost);
|
||||
}
|
||||
|
||||
bool MCMF::spfa(int source, int sink)
|
||||
bool MinCostMaxFlow::spfa(int source, int sink)
|
||||
{
|
||||
std::vector<int>dist(total_nodes, INF);
|
||||
std::vector<bool>in_queue(total_nodes, false);
|
||||
|
@ -111,7 +241,7 @@ namespace Slic3r
|
|||
return true;
|
||||
}
|
||||
|
||||
int MCMF::get_distance(int idx_in_left, int idx_in_right)
|
||||
int MinCostMaxFlow::get_distance(int idx_in_left, int idx_in_right)
|
||||
{
|
||||
if (l_nodes[idx_in_left] == -1) {
|
||||
return 0;
|
||||
|
|
|
@ -9,7 +9,37 @@ namespace Slic3r {
|
|||
|
||||
using FlushMatrix = std::vector<std::vector<float>>;
|
||||
|
||||
class MCMF
|
||||
class MaxFlow
|
||||
{
|
||||
private:
|
||||
const int INF = std::numeric_limits<int>::max();
|
||||
struct Edge {
|
||||
int from, to, capacity, flow;
|
||||
Edge(int u, int v, int cap) :from(u), to(v), capacity(cap), flow(0) {}
|
||||
};
|
||||
public:
|
||||
MaxFlow(const std::vector<int>& u_nodes, const std::vector<int>& v_nodes,
|
||||
const std::unordered_map<int, std::vector<int>>& uv_link_limits = {},
|
||||
const std::unordered_map<int, std::vector<int>>& uv_unlink_limits = {},
|
||||
const std::vector<int>& u_capacity = {},
|
||||
const std::vector<int>& v_capacity = {}
|
||||
);
|
||||
std::vector<int> solve();
|
||||
|
||||
private:
|
||||
void add_edge(int from, int to, int capacity);
|
||||
|
||||
|
||||
int total_nodes;
|
||||
int source_id;
|
||||
int sink_id;
|
||||
std::vector<Edge>edges;
|
||||
std::vector<int>l_nodes;
|
||||
std::vector<int>r_nodes;
|
||||
std::vector<std::vector<int>>adj;
|
||||
};
|
||||
|
||||
class MinCostMaxFlow
|
||||
{
|
||||
const int INF = std::numeric_limits<int>::max();
|
||||
struct Edge
|
||||
|
@ -19,7 +49,12 @@ class MCMF
|
|||
};
|
||||
|
||||
public:
|
||||
MCMF(const FlushMatrix &matrix_, const std::vector<int> &u_nodes, const std::vector<int> &v_nodes);
|
||||
MinCostMaxFlow(const std::vector<std::vector<float>>& matrix_, const std::vector<int>& u_nodes, const std::vector<int>& v_nodes,
|
||||
const std::unordered_map<int, std::vector<int>>& uv_link_limits = {},
|
||||
const std::unordered_map<int, std::vector<int>>& uv_unlink_limits = {},
|
||||
const std::vector<int>& u_capacity = {},
|
||||
const std::vector<int>& v_capacity = {}
|
||||
);
|
||||
std::vector<int> solve();
|
||||
|
||||
private:
|
||||
|
@ -28,16 +63,15 @@ private:
|
|||
int get_distance(int idx_in_left, int idx_in_right);
|
||||
|
||||
private:
|
||||
FlushMatrix matrix;
|
||||
std::vector<std::vector<float>> matrix;
|
||||
std::vector<int> l_nodes;
|
||||
std::vector<int> r_nodes;
|
||||
std::vector<Edge> edges;
|
||||
std::vector<std::vector<int>> adj;
|
||||
|
||||
int total_nodes;
|
||||
int source_id;
|
||||
int sink_id;
|
||||
|
||||
std::vector<Edge> edges;
|
||||
std::vector<std::vector<int>> adj;
|
||||
};
|
||||
|
||||
std::vector<unsigned int> get_extruders_order(const std::vector<std::vector<float>> &wipe_volumes,
|
||||
|
|
|
@ -35,8 +35,10 @@ static std::set<int>get_filament_by_type(const std::vector<unsigned int>& used_f
|
|||
return target_filaments;
|
||||
}
|
||||
|
||||
std::vector<std::set<int>> ToolOrdering::get_physical_unprintables(const std::vector<unsigned int>& used_filaments, const PrintConfig* config, int master_extruder_id)
|
||||
std::vector<std::set<int>> ToolOrdering::get_physical_unprintables(const std::vector<unsigned int>& used_filaments, const PrintConfig* config)
|
||||
{
|
||||
// master saved in config is 1 based,so we should transfer to 0 based here
|
||||
int master_extruder_id = config->master_extruder_id.value - 1;
|
||||
auto tpu_filaments = get_filament_by_type(used_filaments, config, "TPU");
|
||||
if (tpu_filaments.size() > 1) {
|
||||
throw Slic3r::RuntimeError(std::string("Only supports up to one TPU filament."));
|
||||
|
@ -528,9 +530,10 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
|||
}
|
||||
}
|
||||
|
||||
bool ToolOrdering::check_tpu_group(const std::vector<unsigned int>&used_filaments,const std::vector<int>& filament_maps,const PrintConfig* config,int master_extruder_id)
|
||||
bool ToolOrdering::check_tpu_group(const std::vector<unsigned int>&used_filaments,const std::vector<int>& filament_maps,const PrintConfig* config)
|
||||
{
|
||||
int check_extruder_id = -1;
|
||||
int master_extruder_id = config->master_extruder_id.value - 1; // transfer to 0 based idx
|
||||
std::map<int, std::vector<int>> extruder_filament_nums;
|
||||
for (unsigned int filament_id : used_filaments) {
|
||||
int extruder_id = filament_maps[filament_id];
|
||||
|
@ -547,7 +550,7 @@ bool ToolOrdering::check_tpu_group(const std::vector<unsigned int>&used_filament
|
|||
}
|
||||
|
||||
// TPU can only place in master extruder, and it should have no other filaments in the same extruder
|
||||
if (check_extruder_id != -1 && (check_extruder_id!=master_extruder_id || extruder_filament_nums[check_extruder_id].size() > 1)) {
|
||||
if (check_extruder_id != -1 && (check_extruder_id != master_extruder_id || extruder_filament_nums[check_extruder_id].size() > 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -942,17 +945,21 @@ std::vector<int> ToolOrdering::get_recommended_filament_maps(const std::vector<s
|
|||
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; }
|
||||
}
|
||||
// When the AMS count is 0, only external filament can be used, so set the capacity to 1.
|
||||
for(auto& size: group_size)
|
||||
if(size == 0)
|
||||
size = 1;
|
||||
}
|
||||
|
||||
FilamentGroupContext context;
|
||||
context.flush_matrix = std::move(nozzle_flush_mtx);
|
||||
context.geometric_unprintables = geometric_unprintables;
|
||||
context.physical_unprintables = physical_unprintables;
|
||||
context.max_group_size = std::move(group_size);
|
||||
context.total_filament_num = (int)filament_nums;
|
||||
|
||||
// TODO: load master extruder id from config
|
||||
int master_extruder_id = 1;
|
||||
{
|
||||
context.flush_matrix = std::move(nozzle_flush_mtx);
|
||||
context.geometric_unprintables = geometric_unprintables;
|
||||
context.physical_unprintables = physical_unprintables;
|
||||
context.max_group_size = std::move(group_size);
|
||||
context.total_filament_num = (int)filament_nums;
|
||||
context.master_extruder_id = print_config->master_extruder_id.value - 1; // transfer to 0 based idx
|
||||
}
|
||||
// speacially handle tpu filaments
|
||||
auto used_filaments = collect_sorted_used_filaments(layer_filaments);
|
||||
auto tpu_filaments = get_filament_by_type(used_filaments, print_config, "TPU");
|
||||
|
@ -960,9 +967,9 @@ std::vector<int> ToolOrdering::get_recommended_filament_maps(const std::vector<s
|
|||
if (!tpu_filaments.empty()) {
|
||||
for (size_t fidx = 0; fidx < filament_nums; ++fidx) {
|
||||
if (tpu_filaments.count(fidx))
|
||||
ret[fidx] = master_extruder_id;
|
||||
ret[fidx] = context.master_extruder_id;
|
||||
else
|
||||
ret[fidx] = 1 - master_extruder_id;
|
||||
ret[fidx] = 1 - context.master_extruder_id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -971,7 +978,16 @@ std::vector<int> ToolOrdering::get_recommended_filament_maps(const std::vector<s
|
|||
fg.get_custom_seq = get_custom_seq;
|
||||
|
||||
ret = fg.calc_filament_group(layer_filaments, FGStrategy::BestFit);
|
||||
auto memoryed_maps = fg.get_memoryed_groups();
|
||||
|
||||
// optimize for master extruder id
|
||||
optimize_group_for_master_extruder(used_filaments, context, ret);
|
||||
|
||||
// optimize according to AMS filaments
|
||||
std::vector<std::vector<int>>memoryed_maps{ ret };
|
||||
{
|
||||
auto tmp_maps = fg.get_memoryed_groups();
|
||||
memoryed_maps.insert(memoryed_maps.end(), std::make_move_iterator(tmp_maps.begin()), std::make_move_iterator(tmp_maps.end()));
|
||||
}
|
||||
|
||||
std::vector<std::string>used_colors;
|
||||
for (size_t idx = 0; idx < used_filaments.size(); ++idx)
|
||||
|
@ -1067,8 +1083,7 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
|
|||
}
|
||||
std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value - 1; });
|
||||
|
||||
// TODO: load the master extruder_id from config
|
||||
if (!check_tpu_group(used_filaments, filament_maps, print_config, 1)) {
|
||||
if (!check_tpu_group(used_filaments, filament_maps, print_config)) {
|
||||
if (map_mode == FilamentMapMode::fmmManual) {
|
||||
throw Slic3r::RuntimeError(std::string("Manual grouping error: TPU can only be placed in a nozzle alone."));
|
||||
}
|
||||
|
|
|
@ -236,9 +236,9 @@ public:
|
|||
*/
|
||||
static std::vector<int> get_recommended_filament_maps(const std::vector<std::vector<unsigned int>>& layer_filaments, const PrintConfig* print_config, const Print* print, const std::vector<std::set<int>>& physical_unprintables, const std::vector<std::set<int>>& geometric_unprintables);
|
||||
|
||||
static std::vector<std::set<int>> get_physical_unprintables(const std::vector<unsigned int>& layer_filaments, const PrintConfig* config, int master_extruder_id = 1);
|
||||
static std::vector<std::set<int>> get_physical_unprintables(const std::vector<unsigned int>& layer_filaments, const PrintConfig* config);
|
||||
static std::vector<std::set<int>> get_geometrical_unprintables(const std::vector<std::vector<int>>& unprintable_arrs, const PrintConfig* config);
|
||||
static bool check_tpu_group(const std::vector<unsigned int>&used_filaments,const std::vector<int>& filament_maps,const PrintConfig* config,int master_extruder_id);
|
||||
static bool check_tpu_group(const std::vector<unsigned int>&used_filaments,const std::vector<int>& filament_maps,const PrintConfig* config);
|
||||
|
||||
// should be called after doing reorder
|
||||
FilamentChangeStats get_filament_change_stats(FilamentChangeMode mode);
|
||||
|
|
|
@ -1885,8 +1885,7 @@ void Print::process(std::unordered_map<std::string, long long>* slice_time, bool
|
|||
}
|
||||
// check map valid both in auto and mannual mode
|
||||
std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) {return value - 1; });
|
||||
// TODO: load master extruder id from config
|
||||
if (!ToolOrdering::check_tpu_group(used_filaments, filament_maps, &m_config, 1)) {
|
||||
if (!ToolOrdering::check_tpu_group(used_filaments, filament_maps, &m_config)) {
|
||||
if (map_mode == FilamentMapMode::fmmManual) {
|
||||
throw Slic3r::RuntimeError(std::string("Manual grouping error: TPU can only be placed in a nozzle alone."));
|
||||
}
|
||||
|
|
|
@ -3165,6 +3165,11 @@ void PrintConfigDef::init_fff_params()
|
|||
def->set_default_value(new ConfigOptionStrings { "Direct Drive Normal" });
|
||||
def->cli = ConfigOptionDef::nocli;
|
||||
|
||||
def = this->add("master_extruder_id", coInt);
|
||||
def->label = "Master extruder id";
|
||||
def->tooltip = "Default extruder id to place filament";
|
||||
def->set_default_value(new ConfigOptionInt{ 1 });
|
||||
|
||||
def = this->add("print_extruder_id", coInts);
|
||||
def->label = "Print extruder id";
|
||||
def->tooltip = "Print extruder id";
|
||||
|
|
|
@ -1040,6 +1040,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionEnumsGeneric, nozzle_volume_type))
|
||||
((ConfigOptionStrings, extruder_ams_count))
|
||||
((ConfigOptionInts, printer_extruder_id))
|
||||
((ConfigOptionInt, master_extruder_id))
|
||||
((ConfigOptionStrings, printer_extruder_variant))
|
||||
//Orca
|
||||
((ConfigOptionBool, has_scarf_joint_seam))
|
||||
|
|
Loading…
Reference in New Issue