diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 8215cb3aef..3df6c84643 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -3905,14 +3905,8 @@ void PresetBundle::export_selections(AppConfig &config) BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": printer %1%, print %2%, filaments[0] %3% ")%printers.get_selected_preset_name() % prints.get_selected_preset_name() %filament_presets[0]; } -void PresetBundle::set_num_filaments(unsigned int n, std::string new_color) +size_t PresetBundle::resize_project_filament_slots(size_t n) { - unsigned old_filament_count = this->filament_presets.size(); - if (n > old_filament_count && old_filament_count != 0) - filament_presets.resize(n, filament_presets.back()); - else { - filament_presets.resize(n); - } ConfigOptionStrings* filament_color = project_config.option("filament_colour"); ConfigOptionStrings *filament_multi_color = project_config.option("filament_multi_colour"); ConfigOptionStrings* filament_color_type = project_config.option("filament_colour_type"); @@ -3938,9 +3932,25 @@ void PresetBundle::set_num_filaments(unsigned int n, std::string new_color) ams_multi_color_filment.resize(n); resize_mixed_filament_metadata(project_config, old_slot_count, n); + return old_slot_count; +} + +void PresetBundle::set_num_filaments(unsigned int n, std::string new_color) +{ + unsigned old_filament_count = this->filament_presets.size(); + if (n > old_filament_count && old_filament_count != 0) + filament_presets.resize(n, filament_presets.back()); + else { + filament_presets.resize(n); + } + + const size_t old_slot_count = resize_project_filament_slots(n); //BBS set new filament color to new_color if (!new_color.empty()) { + ConfigOptionStrings* filament_color = project_config.option("filament_colour"); + ConfigOptionStrings *filament_multi_color = project_config.option("filament_multi_colour"); + ConfigOptionStrings* filament_color_type = project_config.option("filament_colour_type"); for (size_t i = old_slot_count; i < n; i++) { filament_color->values[i] = new_color; filament_multi_color->values[i] = new_color; @@ -5377,6 +5387,23 @@ DynamicPrintConfig PresetBundle::full_fff_config(bool apply_extruder, std::optio add_if_some_non_empty(std::move(print_compatible_printers), "print_compatible_printers"); out.option("extruder_ams_count", true)->values = save_extruder_ams_count_to_string(this->extruder_ams_counts); + // The slice config is consumed by code that indexes per-slot arrays by material id + // (MultiMaterialSegmentation sizes its state count from filament_colour). After the + // producer-side lockstep in update_multi_material_filament_presets() these project-scoped + // arrays are expected to be exactly num_filaments; the property the pipeline relies on is + // that none is shorter (a short filament_colour was the OOB in #16432). Pad if needed. + for (const char* per_slot_key : { "filament_colour", "filament_colour_type", "filament_multi_colour" }) { + auto* per_slot_opt = dynamic_cast(out.option(per_slot_key, true)); + if (per_slot_opt == nullptr) + continue; + if (per_slot_opt->size() < num_filaments) { + BOOST_LOG_TRIVIAL(warning) << "full_fff_config: per-slot array " << per_slot_key + << " has " << per_slot_opt->size() << " entries, padding to num_filaments=" << num_filaments; + per_slot_opt->resize(num_filaments, FullPrintConfig::defaults().option(per_slot_key)); + } + assert(per_slot_opt->size() >= num_filaments); + } + out.option("printer_technology", true)->value = ptFFF; return out; } @@ -7911,6 +7938,15 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam this->filament_presets.resize(num_extruders, this->filament_presets.empty() ? this->filaments.first_visible().name : this->filament_presets.back()); num_filaments = this->filament_presets.size(); + + // Keep the project-scoped per-slot arrays in lockstep with the padded preset list, so + // consumers that size from filament_colour (MultiMaterialSegmentation) agree with those + // that size from the preset list / filament_diameter (PrintObjectSlice). Grow only: a + // colour array longer than the preset list is a valid state (a project with more slots + // than nozzles) and must not be truncated here. + const auto* filament_colour = this->project_config.option("filament_colour"); + if (filament_colour == nullptr || filament_colour->values.size() < num_filaments) + this->resize_project_filament_slots(num_filaments); } if (to_delete_filament_id == -1) to_delete_filament_id = num_filaments; diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index c6f79b741e..bdd6ff6294 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -394,6 +394,12 @@ public: // slots live. A caller adding physical filaments has to add num_mixed_filaments() on top and // then move the new slots ahead of the mixed tail, as Sidebar::add_custom_filament does. void set_num_filaments(unsigned int n, std::string new_col = ""); + // Grow or shrink the project-scoped per-slot arrays (filament_colour, filament_colour_type, + // filament_multi_colour, the filament maps and the mixed metadata) to n, without touching + // filament_presets and without rebuilding flush volumes. Callers that resize filament_presets + // must keep these arrays in lockstep with it. Returns the array size before the resize so the + // caller can seed the new tail. + size_t resize_project_filament_slots(size_t n); void update_num_filaments(unsigned int to_del_flament_id); void get_ams_cobox_infos(AMSComboInfo &combox_info); diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 2bbe68e4b2..84e183dff3 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -1073,8 +1073,14 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance const auto &layer_ranges = print_object.shared_regions()->layer_ranges; double z = print_object.get_layer(int(range.begin()))->slice_z; auto it_layer_range = layer_range_first(layer_ranges, z); + // The per-layer row width of the segmentation is the number of material channels it + // was built with (filament_colour.size()). Size the bound from the data itself rather + // than from filament_diameter.size(): those two arrays can diverge (the project's + // colour array can end up shorter than the padded preset list), and trusting the + // larger count reads past the end of every row. // BBS - const size_t num_extruders = print_object.print()->config().filament_diameter.size(); + const size_t num_extruders = segmentation.empty() ? 0 : segmentation.front().size(); + assert(num_extruders == print_object.print()->config().filament_diameter.size()); struct ByExtruder { ExPolygons expolygons; @@ -1097,7 +1103,8 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance by_extruder.assign(num_extruders, ByExtruder()); by_region.assign(layer.region_count(), ByRegion()); bool layer_split = false; - for (size_t extruder_id = 0; extruder_id < num_extruders; ++ extruder_id) { + const size_t layer_channels = segmentation[layer_id].size(); + for (size_t extruder_id = 0; extruder_id < std::min(num_extruders, layer_channels); ++ extruder_id) { ByExtruder ®ion = by_extruder[extruder_id]; append(region.expolygons, std::move(segmentation[layer_id][extruder_id])); if (! region.expolygons.empty()) { diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 9611e7ec5c..29f70cfc79 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1610,6 +1610,12 @@ void PlaterPresetComboBox::show_default_color_picker() { DynamicPrintConfig* cfg = &wxGetApp().preset_bundle->project_config; auto colors = static_cast(cfg->option("filament_colour")->clone()); + // The combo can outlive the project's colour array (e.g. a slot padded onto the printer's + // nozzle count); guard before indexing so a stale slot index cannot read past the end. + if (m_filament_idx < 0 || m_filament_idx >= static_cast(colors->values.size())) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": m_filament_idx %1% out of range %2%") % m_filament_idx % colors->values.size(); + return; + } wxColour current_clr(colors->values[m_filament_idx]); if (!current_clr.IsOk()) current_clr = wxColour(0, 0, 0); // Don't set alfa to transparence