Improve virtual color namings and added for parts

This commit is contained in:
Ian Bassi
2026-06-01 09:24:54 -03:00
parent 0346c4931a
commit 1ecedd3187
9 changed files with 189 additions and 39 deletions
+54
View File
@@ -1479,6 +1479,60 @@ std::string compute_mixed_filament_display_color(const MixedFilament &entry, con
mix_b);
}
std::string mixed_filament_standardized_name(const MixedFilament &entry, size_t num_physical)
{
const std::string normalized_pattern = MixedFilamentManager::normalize_manual_pattern(entry.manual_pattern);
if (!normalized_pattern.empty()) {
const std::string flattened = flatten_manual_pattern_groups(normalized_pattern);
return std::string("Pattern ") + (flattened.empty() ? normalized_pattern : flattened);
}
std::vector<std::pair<unsigned int, int>> parts;
parts.reserve(4);
auto append_part = [&parts](unsigned int component_id, int percent) {
if (component_id == 0 || percent <= 0)
return;
for (auto &part : parts) {
if (part.first == component_id) {
part.second += percent;
return;
}
}
parts.emplace_back(component_id, percent);
};
const std::vector<unsigned int> gradient_ids = decode_gradient_component_ids(entry.gradient_component_ids, num_physical);
if (gradient_ids.size() >= 3) {
std::vector<int> weights = decode_gradient_component_weights(entry.gradient_component_weights, gradient_ids.size());
if (weights.empty())
weights.assign(gradient_ids.size(), 1);
const std::vector<int> percentages = normalize_weight_vector_to_percent(weights);
for (size_t idx = 0; idx < gradient_ids.size() && idx < percentages.size(); ++idx)
append_part(gradient_ids[idx], percentages[idx]);
} else {
const unsigned int component_a = (entry.component_a >= 1 && entry.component_a <= num_physical)
? entry.component_a
: 1;
const unsigned int component_b = (entry.component_b >= 1 && entry.component_b <= num_physical)
? entry.component_b
: component_a;
const int pct_b = clamp_int(entry.mix_b_percent, 0, 100);
append_part(component_a, 100 - pct_b);
append_part(component_b, pct_b);
}
if (parts.empty())
return "1:100%";
std::ostringstream out;
for (size_t idx = 0; idx < parts.size(); ++idx) {
if (idx > 0)
out << " + ";
out << parts[idx].first << ':' << parts[idx].second << '%';
}
return out.str();
}
// ---------------------------------------------------------------------------
// MixedFilamentManager
// ---------------------------------------------------------------------------
+5
View File
@@ -144,6 +144,11 @@ std::pair<int, int> mixed_filament_apparent_pair_percentages(const MixedFilament
bool bias_mode_enabled);
std::string compute_mixed_filament_display_color(const MixedFilament &entry, const MixedFilamentDisplayContext &context);
// Build a standardized user-facing mixed filament name.
// - Pattern rows: "Pattern <flattened pattern>" (for example "Pattern 1212354").
// - Mix rows: "<id>:<pct>% + <id>:<pct>% ..." (for example "1:30% + 2:20% + 3:50%").
std::string mixed_filament_standardized_name(const MixedFilament &entry, size_t num_physical);
// ---------------------------------------------------------------------------
// MixedFilamentManager
//
+35 -6
View File
@@ -263,6 +263,33 @@ bool BitmapChoiceRenderer::GetValue(wxVariant& value) const
return true;
}
static std::vector<wxBitmap*> mixed_aware_extruder_icons(bool thin_icon = false)
{
std::vector<wxBitmap*> icons = get_extruder_color_icons(thin_icon);
if (Slic3r::GUI::wxGetApp().plater() == nullptr)
return icons;
const std::vector<std::string> all_colors =
Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(nullptr, true);
if (all_colors.size() <= icons.size())
return icons;
const double em = Slic3r::GUI::wxGetApp().em_unit();
const int icon_width = int((thin_icon ? 2.0 : 4.4) * em + 0.5);
const int icon_height = int(2.0 * em + 0.5);
for (size_t idx = icons.size(); idx < all_colors.size(); ++idx) {
if (all_colors[idx].empty()) {
icons.push_back(nullptr);
continue;
}
icons.push_back(get_extruder_color_icon(all_colors[idx], std::to_string(idx + 1), icon_width, icon_height));
}
return icons;
}
bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state)
{
const wxBitmap& icon = m_value.GetBitmap();
@@ -301,7 +328,7 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR
if (can_create_editor_ctrl && !can_create_editor_ctrl())
return nullptr;
std::vector<wxBitmap*> icons = get_extruder_color_icons();
std::vector<wxBitmap*> icons = mixed_aware_extruder_icons(false);
if (icons.empty())
return nullptr;
@@ -317,12 +344,14 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR
c_editor->Append(_L("default"), *get_default_extruder_color_icon());
for (size_t i = 0; i < icons.size(); i++)
c_editor->Append(wxString::Format("%d", i+1), *icons[i]);
c_editor->Append(wxString::Format("%d", i + 1), icons[i] ? *icons[i] : wxNullBitmap);
if (has_default_extruder && has_default_extruder())
c_editor->SetSelection(atoi(data.GetText().c_str()));
else
c_editor->SetSelection(atoi(data.GetText().c_str()) - 1);
int selection = (has_default_extruder && has_default_extruder())
? atoi(data.GetText().c_str())
: atoi(data.GetText().c_str()) - 1;
if (selection < 0 || selection >= int(c_editor->GetCount()))
selection = 0;
c_editor->SetSelection(selection);
c_editor->Bind(wxEVT_SET_FOCUS, [c_editor](wxFocusEvent& evt) {
#ifdef __WXGTK__
+9 -4
View File
@@ -56,7 +56,7 @@ static std::vector<unsigned int> ui_ordered_filament_ids()
return wxGetApp().plater()->sidebar().get_ui_ordered_filament_ids();
}
static wxString filament_menu_item_name(const int filament_id_1based, const int display_filament_id_1based)
static wxString filament_menu_item_name(const int filament_id_1based)
{
if (filament_id_1based <= 0)
return _L("Default");
@@ -76,7 +76,12 @@ static wxString filament_menu_item_name(const int filament_id_1based, const int
return wxString::Format(_L("Filament %d"), filament_id_1based);
}
return wxString::Format(_L("Mixed Filament %d"), display_filament_id_1based);
const auto &mgr = wxGetApp().preset_bundle->mixed_filaments;
const MixedFilament *mixed = mgr.mixed_filament_from_id(unsigned(filament_id_1based), size_t(physical));
if (mixed == nullptr)
return _L("Mixed Filament");
return from_u8(mixed_filament_standardized_name(*mixed, size_t(physical)));
}
static bool is_improper_category(const std::string& category, const int filaments_cnt, const bool is_object_settings = true)
@@ -1108,7 +1113,7 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu)
const bool is_active_extruder = actual_filament_id == initial_extruder;
const int icon_idx = actual_filament_id == 0 ? 0 : actual_filament_id - 1;
wxString item_name = filament_menu_item_name(actual_filament_id, int(display_idx));
wxString item_name = filament_menu_item_name(actual_filament_id);
if (is_active_extruder) {
item_name << " (" + _L("current") + ")";
@@ -2286,7 +2291,7 @@ void MenuFactory::append_menu_item_change_filament(wxMenu* menu)
const int actual_filament_id = display_idx == 0 ? 0 : int(ordered_filament_ids[display_idx - 1]);
bool is_active_extruder = false;
wxString item_name = filament_menu_item_name(actual_filament_id, int(display_idx));
wxString item_name = filament_menu_item_name(actual_filament_id);
if (is_active_extruder) {
item_name << " (" + _L("current") + ")";
+3 -3
View File
@@ -6551,9 +6551,9 @@ void ObjectList::OnEditingDone(wxDataViewEvent &event)
// BBS: remove "const" qualifier
void ObjectList::set_extruder_for_selected_items(const int extruder)
{
// BBS: check extruder id
std::vector<std::string> colors = wxGetApp().plater()->get_extruder_colors_from_plater_config();
if (extruder > colors.size())
// Accept any configured filament id, including mixed virtual filaments.
const int max_filaments = filaments_count();
if (extruder < 0 || extruder > max_filaments)
return;
wxDataViewItemArray sels;
+19 -4
View File
@@ -2804,13 +2804,28 @@ int ObjectTablePanel::init_bitmap()
m_undo_bitmap = create_scaled_bitmap("lock_normal", nullptr, 18);
m_color_bitmaps = get_extruder_color_icons();
const std::vector<std::string> all_colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(nullptr, true);
if (all_colors.size() > m_color_bitmaps.size()) {
const double em = wxGetApp().em_unit();
const int icon_width = int(4.4 * em + 0.5);
const int icon_height = int(2.0 * em + 0.5);
for (size_t idx = m_color_bitmaps.size(); idx < all_colors.size(); ++idx) {
if (all_colors[idx].empty()) {
m_color_bitmaps.push_back(nullptr);
continue;
}
m_color_bitmaps.push_back(get_extruder_color_icon(all_colors[idx], std::to_string(idx + 1), icon_width, icon_height));
}
}
return 0;
}
int ObjectTablePanel::init_filaments_and_colors()
{
const std::vector<std::string> filament_presets = wxGetApp().preset_bundle->filament_presets;
const std::vector<std::string> filament_colors = wxGetApp().plater()->get_extruder_colors_from_plater_config();
const std::vector<std::string> filament_colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(nullptr, true);
m_filaments_count = filament_colors.size();
if (m_filaments_count <= 0) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", can not get filaments, count: %1%, set to default") % m_filaments_count;
@@ -2844,9 +2859,9 @@ int ObjectTablePanel::init_filaments_and_colors()
continue;
}
m_filaments_name[i] = wxString::Format("%d: Mixed Filament %d (F%u + F%u)",
i + 1, i + 1,
unsigned(mf.component_a), unsigned(mf.component_b));
m_filaments_name[i] = wxString::Format("%d: %s",
i + 1,
from_u8(mixed_filament_standardized_name(mf, physical_count)));
break;
}
+19 -3
View File
@@ -423,15 +423,31 @@ void ObjectDataViewModelNode::UpdateExtruderAndColorIcon(wxString extruder /*= "
}
}
const size_t extruder_id_1based = extruder_idx;
if (extruder_idx > 0) --extruder_idx;
// Create the bitmap with color bars.
std::vector<wxBitmap*> bmps = get_extruder_color_icons(false);// use wide icons
if (bmps.empty()) {
m_extruder_bmp = wxNullBitmap;
if (!bmps.empty() && extruder_idx < bmps.size()) {
m_extruder_bmp = *bmps[extruder_idx];
return;
}
m_extruder_bmp = *bmps[extruder_idx >= bmps.size() ? 0 : extruder_idx];
// Fallback for mixed virtual filaments when the shared icon vector only covers physical filaments.
if (wxGetApp().plater() != nullptr) {
const std::vector<std::string> all_colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(nullptr, true);
if (extruder_id_1based >= 1 && extruder_id_1based <= all_colors.size() && !all_colors[extruder_id_1based - 1].empty()) {
const double em = wxGetApp().em_unit();
const int icon_width = int(4.4 * em + 0.5);
const int icon_height = int(2.0 * em + 0.5);
if (wxBitmap *bmp = get_extruder_color_icon(all_colors[extruder_id_1based - 1], std::to_string(extruder_id_1based), icon_width, icon_height)) {
m_extruder_bmp = *bmp;
return;
}
}
}
m_extruder_bmp = bmps.empty() ? wxNullBitmap : *bmps.front();
}
// *****************************************************************************
+25 -17
View File
@@ -963,9 +963,17 @@ struct DynamicFilamentList : DynamicList
return wxString::Format(_L("Filament %d"), int(filament_id_1based));
}
static wxString mixed_filament_label()
static wxString mixed_filament_label(unsigned int filament_id_1based, size_t num_physical)
{
return _L("Mixed Filament");
if (wxGetApp().preset_bundle == nullptr)
return _L("Mixed Filament");
const auto &mgr = wxGetApp().preset_bundle->mixed_filaments;
const MixedFilament *mixed = mgr.mixed_filament_from_id(filament_id_1based, num_physical);
if (mixed == nullptr)
return _L("Mixed Filament");
return from_u8(mixed_filament_standardized_name(*mixed, num_physical));
}
static wxBitmap *fallback_icon_from_color(unsigned int filament_id_1based,
@@ -994,7 +1002,7 @@ struct DynamicFilamentList : DynamicList
wxString label = filament_id_1based <= num_physical
? physical_filament_label(filament_id_1based)
: mixed_filament_label();
: mixed_filament_label(filament_id_1based, num_physical);
items.push_back({label, icon});
filament_ids.push_back(int(filament_id_1based));
@@ -4430,14 +4438,14 @@ void Sidebar::update_mixed_filament_panel(bool sync_manager)
for (const std::string &hex : physical_colors)
palette.emplace_back(parse_mixed_color(hex));
auto mixed_summary_text = [&mixed](size_t mixed_id) -> wxString {
auto mixed_display_name = [&mixed, num_physical](size_t mixed_id) -> wxString {
if (mixed_id >= mixed.size()) return wxString();
const MixedFilament &entry = mixed[mixed_id];
if (!entry.custom)
return wxString::Format("(Filament %u + Filament %u)", unsigned(entry.component_a), unsigned(entry.component_b));
const std::string normalized = MixedFilamentManager::normalize_manual_pattern(entry.manual_pattern);
if (!normalized.empty()) return _L("(Pattern)");
return wxString::Format("(F%u + F%u)", unsigned(entry.component_a), unsigned(entry.component_b));
return from_u8(mixed_filament_standardized_name(entry, num_physical));
};
auto mixed_summary_text = [](size_t) -> wxString {
return wxString();
};
auto apply_mixed_entry_changes = [this, preset_bundle, print_cfg, num_physical]
@@ -4545,9 +4553,7 @@ void Sidebar::update_mixed_filament_panel(bool sync_manager)
swatch->SetMinSize(wxSize(FromDIP(12), FromDIP(12)));
header_sizer->Add(swatch, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, compact_gap_x);
const int virtual_filament_id = int(num_physical + display_idx + 1);
auto *name_label = new wxStaticText(header_panel, wxID_ANY,
wxString::Format("Mixed Filament %d", virtual_filament_id));
auto *name_label = new wxStaticText(header_panel, wxID_ANY, mixed_display_name(mixed_id));
name_label->SetForegroundColour(mixed_text_fg);
header_sizer->Add(name_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, compact_gap_x);
@@ -4642,9 +4648,9 @@ void Sidebar::update_mixed_filament_panel(bool sync_manager)
};
auto ensure_editor = [this, mixed_id, num_physical, physical_colors, nozzle_diameters, palette,
preview_settings, component_bias_enabled, preset_bundle,
editor_host, editor_sizer, swatch, summary_label, header_panel, row,
rows_scroller, mixed_summary_text, apply_mixed_entry_changes, &mixed]() {
preview_settings, component_bias_enabled, preset_bundle,
editor_host, editor_sizer, swatch, name_label, summary_label, header_panel, row,
rows_scroller, mixed_display_name, mixed_summary_text, apply_mixed_entry_changes, &mixed]() {
if (!preset_bundle || !editor_sizer || editor_sizer->GetItemCount() > 0) return;
auto &mgr2 = preset_bundle->mixed_filaments;
auto &mfs2 = mgr2.mixed_filaments();
@@ -4653,14 +4659,16 @@ void Sidebar::update_mixed_filament_panel(bool sync_manager)
editor_host, mixed_id, mfs2[mixed_id], num_physical,
physical_colors, nozzle_diameters, palette, preview_settings,
component_bias_enabled,
[this, mixed_id, swatch, summary_label, header_panel, row, rows_scroller,
mixed_summary_text, apply_mixed_entry_changes]
[this, mixed_id, swatch, name_label, summary_label, header_panel, row, rows_scroller,
mixed_display_name, mixed_summary_text, apply_mixed_entry_changes]
(const MixedFilament &updated_mf) {
apply_mixed_entry_changes(mixed_id, updated_mf, true);
if (swatch) {
swatch->SetBackgroundColour(parse_mixed_color(updated_mf.display_color));
swatch->Refresh();
}
if (name_label)
name_label->SetLabel(mixed_display_name(mixed_id));
if (summary_label)
summary_label->SetLabel(mixed_summary_text(mixed_id));
if (header_panel) header_panel->Layout();
+20 -2
View File
@@ -783,6 +783,24 @@ void apply_extruder_selector(Slic3r::GUI::BitmapComboBox** ctrl,
bool use_thin_icon/* = false*/)
{
std::vector<wxBitmap*> icons = get_extruder_color_icons(use_thin_icon);
if (dynamic_cast<Slic3r::GUI::ObjectList*>(parent) != nullptr && Slic3r::GUI::wxGetApp().plater() != nullptr) {
const std::vector<std::string> all_colors =
Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(nullptr, true);
if (all_colors.size() > icons.size()) {
const double em = Slic3r::GUI::wxGetApp().em_unit();
const int icon_width = int((use_thin_icon ? 2.0 : 4.4) * em + 0.5);
const int icon_height = int(2.0 * em + 0.5);
for (size_t idx = icons.size(); idx < all_colors.size(); ++idx) {
if (all_colors[idx].empty()) {
icons.push_back(nullptr);
continue;
}
icons.push_back(get_extruder_color_icon(all_colors[idx], std::to_string(idx + 1), icon_width, icon_height));
}
}
}
if (!*ctrl) {
*ctrl = new Slic3r::GUI::BitmapComboBox(parent, wxID_ANY, wxEmptyString, pos, size, 0, nullptr, wxCB_READONLY);
@@ -811,13 +829,13 @@ void apply_extruder_selector(Slic3r::GUI::BitmapComboBox** ctrl,
for (wxBitmap* bmp : icons) {
if (i == 0) {
if (!first_item.empty())
(*ctrl)->Append(_(first_item), *bmp);
(*ctrl)->Append(_(first_item), bmp ? *bmp : wxNullBitmap);
++i;
}
(*ctrl)->Append(use_full_item_name
? Slic3r::GUI::from_u8((boost::format("%1% %2%") % str % i).str())
: wxString::Format("%d", i), *bmp);
: wxString::Format("%d", i), bmp ? *bmp : wxNullBitmap);
++i;
}
(*ctrl)->SetSelection(0);