ENH: seperate statitics for auto for flush mode

1.Compare stats of AutoForFlush with other filament map modes
2.Refine the function signature of filament group

jira:NONE

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: Iec2aff069d8c44343df8b82f045c61354c7d1c2a
This commit is contained in:
xun.zhang 2024-12-23 19:50:56 +08:00 committed by lane.wei
parent d27304e5ef
commit 95e49986f3
6 changed files with 53 additions and 58 deletions

View File

@ -2297,8 +2297,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
{ {
// save the flush statitics stored in tool ordering by object // save the flush statitics stored in tool ordering by object
print.m_statistics_by_extruder_count.stats_by_single_extruder += tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::SingleExt); print.m_statistics_by_extruder_count.stats_by_single_extruder += tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::SingleExt);
print.m_statistics_by_extruder_count.stats_by_multi_extruder_auto += tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::MultiExtAuto); print.m_statistics_by_extruder_count.stats_by_multi_extruder_best += tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::MultiExtBest);
print.m_statistics_by_extruder_count.stats_by_multi_extruder_manual += tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::MultiExtManual); print.m_statistics_by_extruder_count.stats_by_multi_extruder_curr += tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::MultiExtCurr);
// save sorted filament sequences // save sorted filament sequences
const auto& layer_tools = tool_ordering.layer_tools(); const auto& layer_tools = tool_ordering.layer_tools();
for (const auto& lt : layer_tools) for (const auto& lt : layer_tools)
@ -2379,8 +2379,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
{ {
//save the flush statitics stored in tool ordering //save the flush statitics stored in tool ordering
print.m_statistics_by_extruder_count.stats_by_single_extruder = tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::SingleExt); print.m_statistics_by_extruder_count.stats_by_single_extruder = tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::SingleExt);
print.m_statistics_by_extruder_count.stats_by_multi_extruder_auto = tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::MultiExtAuto); print.m_statistics_by_extruder_count.stats_by_multi_extruder_best = tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::MultiExtBest);
print.m_statistics_by_extruder_count.stats_by_multi_extruder_manual = tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::MultiExtManual); print.m_statistics_by_extruder_count.stats_by_multi_extruder_curr = tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::MultiExtCurr);
// save sorted filament sequences // save sorted filament sequences
const auto& layer_tools = tool_ordering.layer_tools(); const auto& layer_tools = tool_ordering.layer_tools();
for (const auto& lt : layer_tools) for (const auto& lt : layer_tools)

View File

@ -1009,19 +1009,20 @@ float get_flush_volume(const std::vector<int> &filament_maps, const std::vector<
return flush_volume; 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 Print* print, 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 Print* print, const FilamentMapMode mode,const std::vector<std::set<int>>&physical_unprintables,const std::vector<std::set<int>>&geometric_unprintables)
{ {
using namespace FilamentGroupUtils; using namespace FilamentGroupUtils;
if (!print_config || layer_filaments.empty()) if (!print || layer_filaments.empty())
return std::vector<int>(); return std::vector<int>();
const unsigned int filament_nums = (unsigned int)(print_config->filament_colour.values.size() + EPSILON); const auto& print_config = print->config();
const unsigned int filament_nums = (unsigned int)(print_config.filament_colour.values.size() + EPSILON);
// get flush matrix // get flush matrix
std::vector<FlushMatrix> nozzle_flush_mtx; std::vector<FlushMatrix> nozzle_flush_mtx;
size_t extruder_nums = print_config->nozzle_diameter.values.size(); size_t extruder_nums = print_config.nozzle_diameter.values.size();
for (size_t nozzle_id = 0; nozzle_id < extruder_nums; ++nozzle_id) { for (size_t nozzle_id = 0; nozzle_id < extruder_nums; ++nozzle_id) {
std::vector<float> flush_matrix(cast<float>(get_flush_volumes_matrix(print_config->flush_volumes_matrix.values, nozzle_id, extruder_nums))); std::vector<float> flush_matrix(cast<float>(get_flush_volumes_matrix(print_config.flush_volumes_matrix.values, nozzle_id, extruder_nums)));
std::vector<std::vector<float>> wipe_volumes; std::vector<std::vector<float>> wipe_volumes;
for (unsigned int i = 0; i < filament_nums; ++i) for (unsigned int i = 0; i < filament_nums; ++i)
wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * filament_nums, flush_matrix.begin() + (i + 1) * filament_nums)); wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * filament_nums, flush_matrix.begin() + (i + 1) * filament_nums));
@ -1029,14 +1030,7 @@ std::vector<int> ToolOrdering::get_recommended_filament_maps(const std::vector<s
nozzle_flush_mtx.emplace_back(wipe_volumes); nozzle_flush_mtx.emplace_back(wipe_volumes);
} }
std::vector<LayerPrintSequence> other_layers_seqs; std::vector<LayerPrintSequence> other_layers_seqs = get_other_layers_print_sequence(print_config.other_layers_print_sequence_nums.value, print_config.other_layers_print_sequence.values);
const ConfigOptionInts* other_layers_print_sequence_op = print_config->option<ConfigOptionInts>("other_layers_print_sequence");
const ConfigOptionInt* other_layers_print_sequence_nums_op = print_config->option<ConfigOptionInt>("other_layers_print_sequence_nums");
if (other_layers_print_sequence_op && other_layers_print_sequence_nums_op) {
const std::vector<int>& print_sequence = other_layers_print_sequence_op->values;
int sequence_nums = other_layers_print_sequence_nums_op->value;
other_layers_seqs = get_other_layers_print_sequence(sequence_nums, print_sequence);
}
// other_layers_seq: the layer_idx and extruder_idx are base on 1 // other_layers_seq: the layer_idx and extruder_idx are base on 1
auto get_custom_seq = [&other_layers_seqs](int layer_idx, std::vector<int>& out_seq) -> bool { auto get_custom_seq = [&other_layers_seqs](int layer_idx, std::vector<int>& out_seq) -> bool {
@ -1053,18 +1047,18 @@ std::vector<int> ToolOrdering::get_recommended_filament_maps(const std::vector<s
std::vector<int>ret(filament_nums, 0); std::vector<int>ret(filament_nums, 0);
// if mutli_extruder, calc group,otherwise set to 0 // if mutli_extruder, calc group,otherwise set to 0
if (extruder_nums == 2) { if (extruder_nums == 2) {
std::vector<std::string> extruder_ams_count_str = print_config->extruder_ams_count.values; std::vector<std::string> extruder_ams_count_str = print_config.extruder_ams_count.values;
auto extruder_ams_counts = get_extruder_ams_count(extruder_ams_count_str); auto extruder_ams_counts = get_extruder_ams_count(extruder_ams_count_str);
std::vector<int> group_size = calc_max_group_size(extruder_ams_counts, false); std::vector<int> group_size = calc_max_group_size(extruder_ams_counts, false);
auto machine_filament_info = build_machine_filaments(print->get_extruder_filament_info()); auto machine_filament_info = build_machine_filaments(print->get_extruder_filament_info());
std::vector<std::string> filament_types = print_config->filament_type.values; std::vector<std::string> filament_types = print_config.filament_type.values;
std::vector<std::string> filament_colours = print_config->filament_colour.values; std::vector<std::string> filament_colours = print_config.filament_colour.values;
// speacially handle tpu filaments // speacially handle tpu filaments
auto used_filaments = collect_sorted_used_filaments(layer_filaments); auto used_filaments = collect_sorted_used_filaments(layer_filaments);
auto tpu_filaments = get_filament_by_type(used_filaments, print_config, "TPU"); auto tpu_filaments = get_filament_by_type(used_filaments, &print_config, "TPU");
FGMode fg_mode = print_config->filament_map_mode.value == FilamentMapMode::fmmAutoForMatch ? FGMode::MatchMode: FGMode::FlushMode; FGMode fg_mode = mode == FilamentMapMode::fmmAutoForMatch ? FGMode::MatchMode: FGMode::FlushMode;
std::vector<std::set<int>> ext_unprintable_filaments; std::vector<std::set<int>> ext_unprintable_filaments;
collect_unprintable_limits(physical_unprintables, geometric_unprintables, ext_unprintable_filaments); // TODO: throw exception if fail or set it to status collect_unprintable_limits(physical_unprintables, geometric_unprintables, ext_unprintable_filaments); // TODO: throw exception if fail or set it to status
@ -1079,7 +1073,7 @@ std::vector<int> ToolOrdering::get_recommended_filament_maps(const std::vector<s
context.machine_info.machine_filament_info = machine_filament_info; context.machine_info.machine_filament_info = machine_filament_info;
context.machine_info.max_group_size = std::move(group_size); context.machine_info.max_group_size = std::move(group_size);
context.machine_info.master_extruder_id = print_config->master_extruder_id.value - 1; // switch to 0 based idx context.machine_info.master_extruder_id = print_config.master_extruder_id.value - 1; // switch to 0 based idx
context.group_info.total_filament_num = (int)(filament_nums); context.group_info.total_filament_num = (int)(filament_nums);
context.group_info.max_gap_threshold = 0.01; context.group_info.max_gap_threshold = 0.01;
@ -1108,10 +1102,10 @@ FilamentChangeStats ToolOrdering::get_filament_change_stats(FilamentChangeMode m
{ {
case Slic3r::ToolOrdering::SingleExt: case Slic3r::ToolOrdering::SingleExt:
return m_stats_by_single_extruder; return m_stats_by_single_extruder;
case Slic3r::ToolOrdering::MultiExtAuto: case Slic3r::ToolOrdering::MultiExtBest:
return m_stats_by_multi_extruder_auto; return m_stats_by_multi_extruder_best;
case Slic3r::ToolOrdering::MultiExtManual: case Slic3r::ToolOrdering::MultiExtCurr:
return m_stats_by_multi_extruder_manual; return m_stats_by_multi_extruder_curr;
default: default:
break; break;
} }
@ -1166,7 +1160,7 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
print_config = &(m_print_object_ptr->print()->config()); print_config = &(m_print_object_ptr->print()->config());
} }
filament_maps = ToolOrdering::get_recommended_filament_maps(layer_filaments, print_config, m_print, physical_unprintables, geometric_unprintables); filament_maps = ToolOrdering::get_recommended_filament_maps(layer_filaments, m_print, map_mode, physical_unprintables, geometric_unprintables);
if (filament_maps.empty()) if (filament_maps.empty())
return; return;
@ -1235,10 +1229,11 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
auto curr_flush_info = calc_filament_change_info_by_toolorder(print_config, filament_maps, nozzle_flush_mtx, filament_sequences); auto curr_flush_info = calc_filament_change_info_by_toolorder(print_config, filament_maps, nozzle_flush_mtx, filament_sequences);
if (nozzle_nums <= 1) if (nozzle_nums <= 1)
m_stats_by_single_extruder = curr_flush_info; m_stats_by_single_extruder = curr_flush_info;
else if (map_mode < fmmManual) else {
m_stats_by_multi_extruder_auto = curr_flush_info; m_stats_by_multi_extruder_curr = curr_flush_info;
else if (map_mode == fmmManual) if (map_mode == fmmAutoForFlush)
m_stats_by_multi_extruder_manual = curr_flush_info; m_stats_by_multi_extruder_best = curr_flush_info;
}
// in multi extruder mode,collect data with other mode // in multi extruder mode,collect data with other mode
if (nozzle_nums > 1) { if (nozzle_nums > 1) {
@ -1258,11 +1253,11 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
); );
m_stats_by_single_extruder = calc_filament_change_info_by_toolorder(print_config, maps_without_group, nozzle_flush_mtx, filament_sequences_one_extruder); m_stats_by_single_extruder = calc_filament_change_info_by_toolorder(print_config, maps_without_group, nozzle_flush_mtx, filament_sequences_one_extruder);
} }
// if in manual mode,also calculate the info by auto mode // if not in best for flush mode,also calculate the info by best for flush mode
if (map_mode == fmmManual) if (map_mode != fmmAutoForFlush)
{ {
std::vector<std::vector<unsigned int>>filament_sequences_one_extruder; std::vector<std::vector<unsigned int>>filament_sequences_one_extruder;
std::vector<int>filament_maps_auto = get_recommended_filament_maps(layer_filaments, print_config, m_print, physical_unprintables, geometric_unprintables); std::vector<int>filament_maps_auto = get_recommended_filament_maps(layer_filaments, m_print, fmmAutoForFlush, physical_unprintables, geometric_unprintables);
reorder_filaments_for_minimum_flush_volume( reorder_filaments_for_minimum_flush_volume(
filament_lists, filament_lists,
filament_maps_auto, filament_maps_auto,
@ -1271,7 +1266,7 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
get_custom_seq, get_custom_seq,
&filament_sequences_one_extruder &filament_sequences_one_extruder
); );
m_stats_by_multi_extruder_auto = calc_filament_change_info_by_toolorder(print_config, filament_maps_auto, nozzle_flush_mtx, filament_sequences_one_extruder); m_stats_by_multi_extruder_best = calc_filament_change_info_by_toolorder(print_config, filament_maps_auto, nozzle_flush_mtx, filament_sequences_one_extruder);
} }
} }

View File

@ -182,8 +182,8 @@ class ToolOrdering
public: public:
enum FilamentChangeMode { enum FilamentChangeMode {
SingleExt, SingleExt,
MultiExtAuto, MultiExtBest,
MultiExtManual MultiExtCurr
}; };
ToolOrdering() = default; ToolOrdering() = default;
@ -204,8 +204,8 @@ public:
void clear() { void clear() {
m_layer_tools.clear(); m_layer_tools.clear();
m_stats_by_single_extruder.clear(); m_stats_by_single_extruder.clear();
m_stats_by_multi_extruder_auto.clear(); m_stats_by_multi_extruder_best.clear();
m_stats_by_multi_extruder_manual.clear(); m_stats_by_multi_extruder_curr.clear();
} }
// Only valid for non-sequential print: // Only valid for non-sequential print:
@ -240,7 +240,7 @@ public:
* called in dual extruder mode, the value in map will be 0 or 1 * called in dual extruder mode, the value in map will be 0 or 1
* 0 based group id * 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 Print* print, 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 Print* print,const FilamentMapMode mode, 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); 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 std::vector<std::set<int>> get_geometrical_unprintables(const std::vector<std::vector<int>>& unprintable_arrs, const PrintConfig* config);
@ -281,8 +281,8 @@ private:
bool m_sorted = false; bool m_sorted = false;
FilamentChangeStats m_stats_by_single_extruder; FilamentChangeStats m_stats_by_single_extruder;
FilamentChangeStats m_stats_by_multi_extruder_manual; FilamentChangeStats m_stats_by_multi_extruder_curr;
FilamentChangeStats m_stats_by_multi_extruder_auto; FilamentChangeStats m_stats_by_multi_extruder_best;
}; };
} // namespace SLic3r } // namespace SLic3r

View File

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

View File

@ -699,12 +699,12 @@ struct StatisticsByExtruderCount
{ {
// flush weight comes first,then comes filament change time // flush weight comes first,then comes filament change time
FilamentChangeStats stats_by_single_extruder; FilamentChangeStats stats_by_single_extruder;
FilamentChangeStats stats_by_multi_extruder_auto; FilamentChangeStats stats_by_multi_extruder_best;
FilamentChangeStats stats_by_multi_extruder_manual; FilamentChangeStats stats_by_multi_extruder_curr;
void clear() { void clear() {
stats_by_single_extruder.clear(); stats_by_single_extruder.clear();
stats_by_multi_extruder_auto.clear(); stats_by_multi_extruder_best.clear();
stats_by_multi_extruder_manual.clear(); stats_by_multi_extruder_curr.clear();
} }
}; };

View File

@ -4668,8 +4668,8 @@ void GCodeViewer::render_legend_color_arr_recommen(float window_padding)
auto is_auto = filament_map_mode < FilamentMapMode::fmmManual; auto is_auto = filament_map_mode < FilamentMapMode::fmmManual;
bool has_tips = true; bool has_tips = true;
if (is_auto) { if (is_auto) {
float saved_flush_weight = stats_by_extruder.stats_by_single_extruder.filament_flush_weight - stats_by_extruder.stats_by_multi_extruder_auto.filament_flush_weight; float saved_flush_weight = stats_by_extruder.stats_by_single_extruder.filament_flush_weight - stats_by_extruder.stats_by_multi_extruder_curr.filament_flush_weight;
int saved_filament_changed_time = stats_by_extruder.stats_by_single_extruder.filament_change_count - stats_by_extruder.stats_by_multi_extruder_auto.filament_change_count; int saved_filament_changed_time = stats_by_extruder.stats_by_single_extruder.filament_change_count - stats_by_extruder.stats_by_multi_extruder_curr.filament_change_count;
if (!(saved_flush_weight > EPSILON || saved_filament_changed_time > 0)) has_tips = false; if (!(saved_flush_weight > EPSILON || saved_filament_changed_time > 0)) has_tips = false;
} }
// BBS AMS containers // BBS AMS containers
@ -4744,16 +4744,16 @@ void GCodeViewer::render_legend_color_arr_recommen(float window_padding)
} }
return static_cast<int>(num); return static_cast<int>(num);
}; };
if (filament_map_mode < fmmManual) { if (filament_map_mode == fmmAutoForFlush) {
float saved_flush_weight = stats_by_extruder.stats_by_single_extruder.filament_flush_weight - stats_by_extruder.stats_by_multi_extruder_auto.filament_flush_weight; float saved_flush_weight = stats_by_extruder.stats_by_single_extruder.filament_flush_weight - stats_by_extruder.stats_by_multi_extruder_best.filament_flush_weight;
int saved_filament_changed_time = stats_by_extruder.stats_by_single_extruder.filament_change_count - stats_by_extruder.stats_by_multi_extruder_auto.filament_change_count; int saved_filament_changed_time = stats_by_extruder.stats_by_single_extruder.filament_change_count - stats_by_extruder.stats_by_multi_extruder_best.filament_change_count;
if (saved_flush_weight > EPSILON || saved_filament_changed_time > 0) { if (saved_flush_weight > EPSILON || saved_filament_changed_time > 0) {
imgui.text(_u8L("This arrangement would be optimal.")); imgui.text(_u8L("This arrangement would be optimal."));
imgui.text_wrapped(from_u8((boost::format(_u8L("Save %1%g filament and %2% changes than one-extruder printer.")) % number_format(saved_flush_weight) % saved_filament_changed_time).str()), parent_width); imgui.text_wrapped(from_u8((boost::format(_u8L("Save %1%g filament and %2% changes than one-extruder printer.")) % number_format(saved_flush_weight) % saved_filament_changed_time).str()), parent_width);
} }
} else if (filament_map_mode == fmmManual) { } else if (filament_map_mode != fmmAutoForFlush) {
float more_cost = stats_by_extruder.stats_by_multi_extruder_manual.filament_flush_weight - stats_by_extruder.stats_by_multi_extruder_auto.filament_flush_weight; float more_cost = stats_by_extruder.stats_by_multi_extruder_curr.filament_flush_weight - stats_by_extruder.stats_by_multi_extruder_best.filament_flush_weight;
int more_time = stats_by_extruder.stats_by_multi_extruder_manual.filament_change_count - stats_by_extruder.stats_by_multi_extruder_auto.filament_change_count; int more_time = stats_by_extruder.stats_by_multi_extruder_curr.filament_change_count - stats_by_extruder.stats_by_multi_extruder_best.filament_change_count;
if (more_cost > EPSILON || more_time > 0) { if (more_cost > EPSILON || more_time > 0) {
is_optimal_group = false; is_optimal_group = false;
@ -4763,8 +4763,8 @@ void GCodeViewer::render_legend_color_arr_recommen(float window_padding)
imgui.text_wrapped(from_u8((boost::format(_u8L("Cost %1%g filament and %2% changes more than optimal arrangement.")) % number_format(more_cost) % more_time).str()), parent_width); imgui.text_wrapped(from_u8((boost::format(_u8L("Cost %1%g filament and %2% changes more than optimal arrangement.")) % number_format(more_cost) % more_time).str()), parent_width);
ImGui::PopStyleColor(1); ImGui::PopStyleColor(1);
} else { } else {
float saved_flush_weight = stats_by_extruder.stats_by_single_extruder.filament_flush_weight - stats_by_extruder.stats_by_multi_extruder_auto.filament_flush_weight; float saved_flush_weight = stats_by_extruder.stats_by_single_extruder.filament_flush_weight - stats_by_extruder.stats_by_multi_extruder_best.filament_flush_weight;
int saved_filament_changed_time = stats_by_extruder.stats_by_single_extruder.filament_change_count - stats_by_extruder.stats_by_multi_extruder_auto.filament_change_count; int saved_filament_changed_time = stats_by_extruder.stats_by_single_extruder.filament_change_count - stats_by_extruder.stats_by_multi_extruder_best.filament_change_count;
if (saved_flush_weight > EPSILON || saved_filament_changed_time > 0) { if (saved_flush_weight > EPSILON || saved_filament_changed_time > 0) {
imgui.text(_u8L("This arrangement would be optimal.")); imgui.text(_u8L("This arrangement would be optimal."));
imgui.text_wrapped(from_u8((boost::format(_u8L("Save %1%g filament and %2% changes than one-extruder printer.")) % number_format(saved_flush_weight) % saved_filament_changed_time).str()), parent_width); imgui.text_wrapped(from_u8((boost::format(_u8L("Save %1%g filament and %2% changes than one-extruder printer.")) % number_format(saved_flush_weight) % saved_filament_changed_time).str()), parent_width);