Keep mixed-color filaments intact when the extruder count changes (#15385)

* Keep mixed-color filaments intact when the extruder count changes

The extruder-count spinner resized the filament arrays in bulk at the tail,
which is where mixed-color slots live, so a new filament landed behind the
mix and the sidebar skipped a slot number. It now adds and removes one slot
at a time through the same calls the sidebar's +/- buttons use, so a new
slot opens ahead of the mixed tail and a removal renumbers object filament
ids, painted facets, custom g-code and mixed components rather than
clamping them away.

Drops the vector overload of set_num_filaments(), which this leaves without
callers.
This commit is contained in:
SoftFever
2026-08-26 19:06:33 +08:00
committed by GitHub
parent 9dc9b42475
commit 5552ed6cf1
6 changed files with 246 additions and 88 deletions
+10 -4
View File
@@ -8906,10 +8906,16 @@ void GUI_App::load_current_presets(bool active_preset_combox/*= false*/, bool ch
auto* nozzle_diameter = edited_printer_preset.config.option<ConfigOptionFloats>("nozzle_diameter");
if (nozzle_diameter) {
// Mixed-color slots are virtual filaments kept at the tail of the list, so they have no
// nozzle of their own. Sizing to the nozzle count alone would silently drop the mixes of
// a just-loaded project, and update_extruder_count() would then strip the facets painted
// with them.
preset_bundle->set_num_filaments(nozzle_diameter->values.size() + preset_bundle->num_mixed_filaments());
// nozzle of their own and the count has to allow for them. Only ever grow: this sizes
// the list so the combo boxes have something to bind to, and set_num_filaments() trims
// at the raw tail, so shrinking here would eat the mixes rather than the surplus
// physical slots. A list longer than the nozzle count is a state the app reaches
// legitimately - raising the extruder count and not saving the printer preset leaves
// exactly that on the next start - and losing the project's mixes to it is worse than
// carrying a filament the printer has no nozzle for until the count is next changed.
const size_t target = nozzle_diameter->values.size() + preset_bundle->num_mixed_filaments();
if (target > preset_bundle->filament_presets.size())
preset_bundle->set_num_filaments(target);
}
}
this->plater()->set_printer_technology(printer_technology);
+7 -4
View File
@@ -5506,12 +5506,15 @@ void Sidebar::add_custom_filament(wxColour new_col, const std::string& preset_na
// Mixed-color slots are kept at the tail of the filament arrays, so a new physical
// filament has to be inserted just after the last physical one rather than appended.
// total == every slot (physical + mixed); insert_pos == the physical slot count.
size_t total = wxGetApp().preset_bundle->filament_presets.size();
size_t insert_pos = p->combos_filament.size();
// Count off filament_is_mixed, not filament_presets or the combos: the extruder-count spinner
// reaches this before the sidebar has rebuilt, and update_multi_material_filament_presets()
// can have grown filament_presets alone.
auto *bundle = wxGetApp().preset_bundle;
size_t insert_pos = bundle->num_physical_filaments();
size_t total = insert_pos + bundle->num_mixed_filaments();
int filament_count = (int)(total + 1);
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
wxGetApp().preset_bundle->set_num_filaments(filament_count, new_color);
bundle->set_num_filaments(filament_count, new_color);
// Maintain physical-first ordering: rotate the new slot from end to insert_pos.
// No mixed slots -> insert_pos == total -> every rotate below is a no-op.
+18 -14
View File
@@ -2174,21 +2174,25 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
//Orca: sync filament num if it's a multi tool printer
if (opt_key == "extruders_count" && !m_config->opt_bool("single_extruder_multi_material")){
auto num_extruder = boost::any_cast<size_t>(value);
int old_filament_size = wxGetApp().preset_bundle->filament_presets.size();
std::vector<std::string> new_colors;
for (int i = old_filament_size; i < num_extruder; ++i) {
wxColour new_col = Plater::get_next_color_for_filament();
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
new_colors.push_back(new_color);
const size_t num_extruder = boost::any_cast<size_t>(value);
auto *bundle = wxGetApp().preset_bundle;
Sidebar &sidebar = wxGetApp().plater()->sidebar();
// A tool changer feeds filament N from nozzle N, so the extruder count sizes the physical
// run only; mixed slots are virtual and keep the tail. Go one slot at a time through the
// sidebar's own +/- calls: they insert ahead of the mixed tail and renumber filament ids,
// painted facets, custom g-code and mixed components, which a bulk resize clamps away.
// Both also refresh the print tab and export the selections, so nothing to do afterwards.
size_t physical = bundle->num_physical_filaments();
while (physical != num_extruder) {
if (physical < num_extruder)
sidebar.add_custom_filament(Plater::get_next_color_for_filament());
else
sidebar.delete_filament(physical - 1); // physical > num_extruder >= 1
const size_t updated = bundle->num_physical_filaments();
if (updated == physical)
break; // the call declined, e.g. the total slot limit - do not spin
physical = updated;
}
// Mixed-color slots are virtual filaments at the tail of the list with no nozzle of their
// own, so they are carried on top of the new extruder count instead of being truncated.
const size_t total_filaments = num_extruder + wxGetApp().preset_bundle->num_mixed_filaments();
wxGetApp().preset_bundle->set_num_filaments(total_filaments, new_colors);
wxGetApp().plater()->on_filament_count_change(total_filaments);
wxGetApp().get_tab(Preset::TYPE_PRINT)->update();
wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config);
}
//Orca: disable purge_in_prime_tower if single_extruder_multi_material is disabled