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:
parent
f0d2ad3dbe
commit
0b95bdd9d9
|
@ -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);
|
||||
|
||||
|
|
|
@ -7,38 +7,68 @@
|
|||
#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);
|
||||
|
||||
struct FlushTimeMachine
|
||||
{
|
||||
private:
|
||||
std::chrono::high_resolution_clock::time_point start;
|
||||
|
||||
public:
|
||||
void time_machine_start()
|
||||
{
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
int time_machine_end()
|
||||
{
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||
return duration.count();
|
||||
}
|
||||
};
|
||||
|
||||
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:
|
||||
std::chrono::high_resolution_clock::time_point start;
|
||||
|
||||
public:
|
||||
void time_machine_start()
|
||||
{
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
int time_machine_end()
|
||||
{
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||
return duration.count();
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
std::vector<FlushMatrix> flush_matrix;
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue