ENH: do another map for ams filaments

1.If the group result differs little in flush,we will choose the one
that best fits the ams filaments

jira:NONE

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: Icd147b406e3494c841ef13564ad1b1231ad798fd
This commit is contained in:
xun.zhang 2024-09-06 17:49:03 +08:00 committed by lane.wei
parent f0d2ad3dbe
commit 0b95bdd9d9
5 changed files with 276 additions and 26 deletions

View File

@ -3,6 +3,7 @@
#include <queue>
#include <random>
#include <cassert>
#include <sstream>
namespace Slic3r
{
@ -52,6 +53,161 @@ namespace Slic3r
return true;
}
static int calc_color_distance(const Color &src, const Color &dst)
{
double rmean = (src.r + dst.r) / 2.f;
double dr = src.r - dst.r;
double dg = src.g - dst.g;
double db = src.b - dst.b;
return sqrt((512 + rmean) / 256.f * dr * dr + 4 * dg * dg + (767 - rmean) / 256 * db * db);
}
// clear the array and heap,save the groups in heap to the array
static void change_memoryed_heaps_to_arrays(FilamentGroupUtils::MemoryedGroupHeap& heap,const int total_filament_num,const std::vector<unsigned int>& used_filaments, std::vector<std::vector<int>>& arrs)
{
// switch the label idx
arrs.clear();
while (!heap.empty()) {
auto top = heap.top();
heap.pop();
std::vector<int> labels_tmp(total_filament_num, 0);
for (size_t idx = 0; idx < top.group.size(); ++idx)
labels_tmp[used_filaments[idx]] = top.group[idx];
arrs.emplace_back(std::move(labels_tmp));
}
}
Color::Color(const std::string& hexstr) {
if (hexstr.empty() || (hexstr.length() != 9 && hexstr.length() != 7) || hexstr[0] != '#')
{
assert(false);
r = 0, g = 0, b = 0, a = 255;
return;
}
auto hexToByte = [](const std::string& hex)->unsigned char
{
unsigned int byte;
std::istringstream(hex) >> std::hex >> byte;
return static_cast<unsigned char>(byte);
};
r = hexToByte(hexstr.substr(1, 2));
g = hexToByte(hexstr.substr(3, 2));
b = hexToByte(hexstr.substr(5, 2));
if (hexstr.size() == 9)
a = hexToByte(hexstr.substr(7, 2));
}
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)
{
assert(used_filaments.size() == ams_filament_colors_str.size());
// change the color str to real colors
std::vector<Color>used_filament_colors;
std::vector<std::vector<Color>>ams_filament_colors;
for (auto& item : used_filament_colors_str)
used_filament_colors.emplace_back(Color(item));
for (auto& arr : ams_filament_colors_str) {
std::vector<Color>tmp;
for (auto& item : arr)
tmp.emplace_back(Color(item));
ams_filament_colors.emplace_back(std::move(tmp));
}
int best_cost = std::numeric_limits<int>::max();
std::vector<int>best_map;
for (auto& map : map_lists) {
std::vector<std::vector<Color>>group_colors(2);
for (size_t i = 0; i < used_filaments.size(); ++i) {
if (map[used_filaments[i]] == 0)
group_colors[0].emplace_back(used_filament_colors[i]);
else
group_colors[1].emplace_back(used_filament_colors[i]);
}
int tmp_cost = 0;
for (size_t i = 0; i < 2; ++i) {
if (group_colors[i].empty() || ams_filament_colors[i].empty())
continue;
std::vector<std::vector<float>>distance_matrix(group_colors[i].size(), std::vector<float>(ams_filament_colors[i].size()));
// calculate color distance matrix
for (size_t src = 0; src < group_colors[i].size(); ++src) {
for (size_t dst = 0; dst < ams_filament_colors[i].size(); ++dst)
distance_matrix[src][dst] = calc_color_distance(group_colors[i][src], ams_filament_colors[i][dst]);
}
// get min cost by min cost max flow
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);
auto ams_map = mcmf.solve();
for (size_t idx = 0; idx < ams_map.size(); ++idx) {
if (ams_map[idx] == -1)
continue;
tmp_cost += distance_matrix[idx][ams_map[idx]];
}
}
if (tmp_cost < best_cost) {
best_cost = tmp_cost;
best_map = map;
}
}
return best_map;
}
void FilamentGroupUtils::update_memoryed_groups(const MemoryedGroup& item, const double gap_threshold, MemoryedGroupHeap& groups)
{
auto emplace_if_accepatle = [gap_threshold](MemoryedGroupHeap& heap, const MemoryedGroup& elem, const MemoryedGroup& best) {
if (best.cost == 0) {
if (std::abs(elem.cost - best.cost) <= ABSOLUTE_FLUSH_GAP_TOLERANCE)
heap.push(elem);
return;
}
double gap_rate = (double)std::abs(elem.cost - best.cost) / (double)best.cost;
if (gap_rate < gap_threshold)
heap.push(elem);
};
if (groups.empty()) {
groups.push(item);
}
else {
auto top = groups.top();
// we only memory items with the highest prefer level
if (top.prefer_level > item.prefer_level)
return;
else if (top.prefer_level == item.prefer_level) {
if (top.cost <= item.cost) {
emplace_if_accepatle(groups, item, top);
}
// find a group with lower cost, rebuild the heap
else {
MemoryedGroupHeap new_heap;
new_heap.push(item);
while (!groups.empty()) {
auto top = groups.top();
groups.pop();
emplace_if_accepatle(new_heap, top, item);
}
groups = std::move(new_heap);
}
}
// find a group with the higher prefer level, rebuild the heap
else {
groups = MemoryedGroupHeap();
groups.push(item);
}
}
}
std::vector<unsigned int> collect_sorted_used_filaments(const std::vector<std::vector<unsigned int>>& layer_filaments)
{
std::set<unsigned int>used_filaments_set;
@ -217,7 +373,7 @@ namespace Slic3r
void KMediods2::do_clustering(const FGStrategy& g_strategy, int timeout_ms)
{
FlushTimeMachine T;
FilamentGroupUtils::FlushTimeMachine T;
T.time_machine_start();
if (m_elem_count < m_k) {
@ -245,6 +401,15 @@ namespace Slic3r
best_cost = new_cost;
best_labels = new_labels;
}
{
MemoryedGroup g;
g.prefer_level = 1; // in non enum mode, we use the same prefer level
g.cost = new_cost;
g.group = new_labels;
update_memoryed_groups(g, memory_threshold, memoryed_groups);
}
if (T.time_machine_end() > timeout_ms)
break;
}
@ -281,6 +446,9 @@ namespace Slic3r
static constexpr int UNPLACEABLE_LIMIT_REWARD = 100; // reward value if the group result follows the unprintable limit
static constexpr int MAX_SIZE_LIMIT_REWARD = 10; // reward value if the group result follows the max size per extruder
static constexpr int BEST_FIT_LIMIT_REWARD = 1; // reward value if the group result try to fill the max size per extruder
MemoryedGroupHeap memoryed_groups;
auto bit_count_one = [](uint64_t n)
{
int count = 0;
@ -360,15 +528,26 @@ namespace Slic3r
best_cost = total_cost;
best_label = filament_maps;
}
{
MemoryedGroup mg;
mg.prefer_level = prefer_level;
mg.cost = total_cost;
mg.group = std::move(filament_maps);
update_memoryed_groups(mg, memory_threshold, memoryed_groups);
}
}
if (cost)
*cost = best_cost;
std::vector<int> filament_labels(m_context.total_filament_num, 0);
for (int i = 0; i < best_label.size(); ++i)
for (size_t i = 0; i < best_label.size(); ++i)
filament_labels[used_filaments[i]] = best_label[i];
change_memoryed_heaps_to_arrays(memoryed_groups, m_context.total_filament_num, used_filaments, m_memoryed_groups);
return filament_labels;
}
@ -400,9 +579,16 @@ namespace Slic3r
KMediods2 PAM((int)used_filaments.size(),distance_evaluator);
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();
{
auto memoryed_groups = PAM.get_memoryed_groups();
change_memoryed_heaps_to_arrays(memoryed_groups, m_context.total_filament_num, used_filaments, m_memoryed_groups);
}
if(cost)
*cost=reorder_filaments_for_minimum_flush_volume(used_filaments,filament_labels,layer_filaments,m_context.flush_matrix,std::nullopt,nullptr);

View File

@ -7,14 +7,36 @@
#include <set>
#include <map>
#include <vector>
#include <queue>
#include "GCode/ToolOrderUtils.hpp"
const static int DEFAULT_CLUSTER_SIZE = 16;
const static int ABSOLUTE_FLUSH_GAP_TOLERANCE = 2000;
namespace Slic3r
{
std::vector<unsigned int>collect_sorted_used_filaments(const std::vector<std::vector<unsigned int>>& layer_filaments);
enum FGStrategy {
BestCost,
BestFit
};
struct Color
{
unsigned char r = 0;
unsigned char g = 0;
unsigned char b = 0;
unsigned char a = 255;
Color(unsigned char r_ = 0, unsigned char g_ = 0, unsigned char b_ = 0, unsigned a_ = 255) :r(r_), g(g_), b(b_), a(a_) {}
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
{
private:
@ -34,10 +56,18 @@ namespace Slic3r
}
};
enum FGStrategy {
BestCost,
BestFit
struct MemoryedGroup {
int cost{ 0 };
int prefer_level{ 0 };
std::vector<int>group;
bool operator>(const MemoryedGroup& other) const {
return prefer_level < other.prefer_level || (prefer_level == other.prefer_level && cost > other.cost);
}
};
using MemoryedGroupHeap = std::priority_queue<MemoryedGroup, std::vector<MemoryedGroup>, std::greater<MemoryedGroup>>;
void update_memoryed_groups(const MemoryedGroup& item,const double gap_threshold, MemoryedGroupHeap& groups);
}
struct FilamentGroupContext
{
@ -62,14 +92,21 @@ namespace Slic3r
class FilamentGroup
{
using MemoryedGroupHeap = FilamentGroupUtils::MemoryedGroupHeap;
using MemoryedGroup = FilamentGroupUtils::MemoryedGroup;
public:
FilamentGroup(const FilamentGroupContext& context);
std::vector<int> calc_filament_group(const std::vector<std::vector<unsigned int>>& layer_filaments, const FGStrategy& g_strategy = FGStrategy::BestFit, int* cost = nullptr);
public:
std::vector<int> calc_filament_group_by_enum(const std::vector<std::vector<unsigned int>>& layer_filaments, const std::vector<unsigned int>& used_filaments, const FGStrategy& g_strategy, int* cost = nullptr);
std::vector<int> 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 = nullptr, int timeout_ms = 300);
void set_memory_threshold(double threshold) { memory_threshold = threshold; }
std::vector<std::vector<int>> get_memoryed_groups()const { return m_memoryed_groups; }
private:
FilamentGroupContext m_context;
double memory_threshold{ 0 };
std::vector<std::vector<int>> m_memoryed_groups;
public:
std::optional<std::function<bool(int, std::vector<int>&)>> get_custom_seq;
};
@ -77,6 +114,9 @@ namespace Slic3r
class KMediods2
{
using MemoryedGroupHeap = FilamentGroupUtils::MemoryedGroupHeap;
using MemoryedGroup = FilamentGroupUtils::MemoryedGroup;
enum INIT_TYPE
{
Random = 0,
@ -97,6 +137,10 @@ namespace Slic3r
void set_unplaceable_limits(const std::map<int, int>& placeable_limits) { m_unplaceable_limits = placeable_limits; }
void do_clustering(const FGStrategy& g_strategy,int timeout_ms = 100);
void set_memory_threshold(double threshold) { memory_threshold; }
MemoryedGroupHeap get_memoryed_groups()const { return memoryed_groups; }
std::vector<int>get_cluster_labels()const { return m_cluster_labels; }
private:
@ -110,6 +154,9 @@ namespace Slic3r
int m_elem_count;
const int m_k = 2;
double memory_threshold{ 0 };
FilamentGroupUtils::MemoryedGroupHeap memoryed_groups;
std::vector<int>m_cluster_labels;
};
}

View File

@ -889,7 +889,7 @@ float get_flush_volume(const std::vector<int> &filament_maps, const std::vector<
return flush_volume;
}
std::vector<int> ToolOrdering::get_recommended_filament_maps(const std::vector<std::vector<unsigned int>>& layer_filaments, const PrintConfig* print_config, const std::vector<std::set<int>>&physical_unprintables,const std::vector<std::set<int>>&geometric_unprintables)
std::vector<int> ToolOrdering::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)
{
if (!print_config || layer_filaments.empty())
return std::vector<int>();
@ -967,8 +967,25 @@ std::vector<int> ToolOrdering::get_recommended_filament_maps(const std::vector<s
}
else {
FilamentGroup fg(context);
fg.set_memory_threshold(0.02);
fg.get_custom_seq = get_custom_seq;
ret = fg.calc_filament_group(layer_filaments, FGStrategy::BestFit);
auto memoryed_maps = fg.get_memoryed_groups();
std::vector<std::string>used_colors;
for (size_t idx = 0; idx < used_filaments.size(); ++idx)
used_colors.emplace_back(print_config->filament_colour.get_at(used_filaments[idx]));
auto ams_filament_info = print->get_extruder_filament_info();
std::vector<std::vector<std::string>> ams_colors;
for (auto& arr : ams_filament_info) {
std::vector<std::string>colors;
for (auto& item : arr)
colors.emplace_back(item.option<ConfigOptionStrings>("filament_colour")->get_at(0));
ams_colors.emplace_back(std::move(colors));
}
ret = select_best_group_for_ams(memoryed_maps, used_filaments, used_colors, ams_colors);
}
}
@ -1040,7 +1057,7 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
print_config = &(m_print_object_ptr->print()->config());
}
filament_maps = ToolOrdering::get_recommended_filament_maps(layer_filaments, print_config, physical_unprintables, geometric_unprintables);
filament_maps = ToolOrdering::get_recommended_filament_maps(layer_filaments, print_config, m_print, physical_unprintables, geometric_unprintables);
if (filament_maps.empty())
return;
@ -1138,7 +1155,7 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
if (map_mode == fmmManual)
{
std::vector<std::vector<unsigned int>>filament_sequences_one_extruder;
std::vector<int>filament_maps_auto = get_recommended_filament_maps(layer_filaments, print_config, physical_unprintables, geometric_unprintables);
std::vector<int>filament_maps_auto = get_recommended_filament_maps(layer_filaments, print_config, m_print, physical_unprintables, geometric_unprintables);
reorder_filaments_for_minimum_flush_volume(
filament_lists,
filament_maps_auto,

View File

@ -234,7 +234,7 @@ public:
* called in dual extruder mode, the value in map will be 0 or 1
* 0 based group id
*/
static std::vector<int> get_recommended_filament_maps(const std::vector<std::vector<unsigned int>>& layer_filaments, const PrintConfig* print_config, const std::vector<std::set<int>>& physical_unprintables, const std::vector<std::set<int>>& geometric_unprintables);
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_geometrical_unprintables(const std::vector<std::vector<int>>& unprintable_arrs, const PrintConfig* config);

View File

@ -1879,7 +1879,7 @@ void Print::process(std::unordered_map<std::string, long long>* slice_time, bool
auto map_mode = get_filament_map_mode();
// get recommended filament map
if (map_mode == FilamentMapMode::fmmAuto) {
filament_maps = ToolOrdering::get_recommended_filament_maps(all_filaments, &config(), physical_unprintables, geometric_unprintables);
filament_maps = ToolOrdering::get_recommended_filament_maps(all_filaments, &config(), this, physical_unprintables, geometric_unprintables);
std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value + 1; });
update_filament_maps_to_config(filament_maps);
}