diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index 938b72126a..00439b6715 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -1,6 +1,7 @@ #include "../libslic3r.h" #include "../Exception.hpp" #include "../Model.hpp" +#include "../MixedFilament.hpp" #include "../Preset.hpp" #include "../Utils.hpp" #include "../LocalesUtils.hpp" @@ -574,6 +575,51 @@ bool bbs_is_valid_object_type(const std::string& type) namespace Slic3r { +static size_t physical_filament_count_from_project_config(const DynamicPrintConfig &config) +{ + if (const auto *opt = config.option("filament_colour"); opt != nullptr && !opt->values.empty()) + return opt->values.size(); + if (const auto *opt = config.option("filament_settings_id"); opt != nullptr && !opt->values.empty()) + return opt->values.size(); + if (const auto *opt = config.option("filament_ids"); opt != nullptr && !opt->values.empty()) + return opt->values.size(); + if (const auto *opt = config.option("default_filament_colour"); opt != nullptr && !opt->values.empty()) + return opt->values.size(); + if (const auto *opt = config.option("nozzle_diameter"); opt != nullptr && !opt->values.empty()) + return opt->values.size(); + return 0; +} + +static int max_supported_filament_id_from_project_config(const DynamicPrintConfig &config) +{ + const size_t physical_count = physical_filament_count_from_project_config(config); + if (physical_count == 0) + return std::numeric_limits::max(); + + std::vector physical_colors; + if (const auto *opt = config.option("filament_colour"); opt != nullptr) + physical_colors = opt->values; + else if (const auto *opt = config.option("default_filament_colour"); opt != nullptr) + physical_colors = opt->values; + if (physical_colors.size() < physical_count) + physical_colors.resize(physical_count, "#FFFFFF"); + else if (physical_colors.size() > physical_count) + physical_colors.resize(physical_count); + + size_t max_filament_id = physical_count; + if (physical_count >= 2) { + if (const auto *mixed_defs_opt = config.option("mixed_filament_definitions"); + mixed_defs_opt != nullptr && !mixed_defs_opt->value.empty()) { + MixedFilamentManager mixed_mgr; + mixed_mgr.auto_generate(physical_colors); + mixed_mgr.load_custom_entries(mixed_defs_opt->value, physical_colors); + max_filament_id = mixed_mgr.total_filaments(physical_count); + } + } + + return max_filament_id >= size_t(std::numeric_limits::max()) ? std::numeric_limits::max() : int(max_filament_id); +} + void PlateData::parse_filament_info(GCodeProcessorResult *result) { if (!result) return; @@ -1787,7 +1833,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) // extract slic3r print config file // _extract_print_config_from_archive(archive, stat, config, config_substitutions, filename); //} else - if (!dont_load_config && boost::algorithm::iequals(name, BBS_PROJECT_CONFIG_FILE)) { + if (boost::algorithm::iequals(name, BBS_PROJECT_CONFIG_FILE)) { // extract slic3r print config file _extract_project_config_from_archive(archive, stat, config, config_substitutions, model); } @@ -2085,8 +2131,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) ++object_idx; } - const ConfigOptionStrings* filament_ids_opt = config.option("filament_settings_id"); - int max_filament_id = filament_ids_opt ? filament_ids_opt->size() : std::numeric_limits::max(); + const int max_filament_id = max_supported_filament_id_from_project_config(config); for (ModelObject* mo : m_model->objects) { const ConfigOptionInt* extruder_opt = dynamic_cast(mo->config.option("extruder")); int extruder_id = 0; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 4114c7d58c..3ef372ca36 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -68,15 +68,20 @@ static DynamicPrintConfig& printer_config() return wxGetApp().preset_bundle->printers.get_edited_preset().config; } +static size_t total_filaments_count(size_t physical_count) +{ + if (wxGetApp().preset_bundle == nullptr) + return physical_count; + + return wxGetApp().preset_bundle->mixed_filaments.total_filaments(physical_count); +} + static int filaments_count() { if (wxGetApp().preset_bundle == nullptr) return 0; - int physical = wxGetApp().filaments_cnt(); - // Include enabled mixed (virtual) filaments in the count. - const auto &mixed_mgr = wxGetApp().preset_bundle->mixed_filaments; - return static_cast(mixed_mgr.total_filaments(physical)); + return static_cast(total_filaments_count(size_t(std::max(wxGetApp().filaments_cnt(), 0)))); } static void take_snapshot(const std::string& snapshot_name) @@ -959,6 +964,7 @@ void ObjectList::update_objects_list_filament_column_when_delete_filament(size_t int replace_filament_id) { m_prevent_update_filament_in_config = true; + const size_t total_filaments = total_filaments_count(filaments_count); // BBS: update extruder values even when filaments_count is 1, because it may be reduced from value greater than 1 if (m_objects) @@ -967,7 +973,7 @@ void ObjectList::update_objects_list_filament_column_when_delete_filament(size_t update_filament_colors(); // set show/hide for this column - set_filament_column_hidden(filaments_count == 1); + set_filament_column_hidden(total_filaments == 1); // a workaround for a wrong last column width updating under OSX GetColumn(colEditing)->SetWidth(25); @@ -977,6 +983,7 @@ void ObjectList::update_objects_list_filament_column_when_delete_filament(size_t void ObjectList::update_objects_list_filament_column(size_t filaments_count) { assert(filaments_count >= 1); + const size_t total_filaments = total_filaments_count(filaments_count); if (printer_technology() == ptSLA) filaments_count = 1; @@ -985,12 +992,12 @@ void ObjectList::update_objects_list_filament_column(size_t filaments_count) // BBS: update extruder values even when filaments_count is 1, because it may be reduced from value greater than 1 if (m_objects) - update_filament_values_for_items(filaments_count); + update_filament_values_for_items(printer_technology() == ptSLA ? 1 : total_filaments); update_filament_colors(); // set show/hide for this column - set_filament_column_hidden(filaments_count == 1); + set_filament_column_hidden((printer_technology() == ptSLA ? 1 : total_filaments) == 1); //a workaround for a wrong last column width updating under OSX auto em = em_unit(this); GetColumn(colEditing)->SetWidth(m_columns_width[colEditing]*em); diff --git a/src/slic3r/GUI/GUI_ObjectTable.cpp b/src/slic3r/GUI/GUI_ObjectTable.cpp index bca8a9e00f..474d37b5fc 100644 --- a/src/slic3r/GUI/GUI_ObjectTable.cpp +++ b/src/slic3r/GUI/GUI_ObjectTable.cpp @@ -2805,50 +2805,48 @@ int ObjectTablePanel::init_bitmap() int ObjectTablePanel::init_filaments_and_colors() { - //DynamicPrintConfig& global_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; - const DynamicPrintConfig* global_config = m_plater->config(); const std::vector filament_presets = wxGetApp().preset_bundle->filament_presets; - m_filaments_count = filament_presets.size(); + const std::vector filament_colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(); + 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; set_default_filaments_and_colors(); return -1; } - const ConfigOptionStrings* filament_opt = dynamic_cast(global_config->option("filament_colour")); - if (filament_opt == nullptr) { - set_default_filaments_and_colors(); - return -1; - } m_filaments_colors.resize(m_filaments_count); m_filaments_name.resize(m_filaments_count); - unsigned int color_count = filament_opt->values.size(); - if (color_count != m_filaments_count) { - BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", invalid color count:%1%, extruder count: %2%") %color_count %m_filaments_count; - } - - unsigned int i = 0; + const size_t physical_count = filament_presets.size(); ColorRGB rgb; - while (i < m_filaments_count) { - const std::string& txt_color = global_config->opt_string("filament_colour", i); - if (i < color_count) { - if (decode_color(txt_color, rgb)) - { - m_filaments_colors[i] = wxColour(rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar()); - } - else - { - m_filaments_colors[i] = *wxGREEN; - } - } - else { + + for (int i = 0; i < m_filaments_count; ++i) { + if (size_t(i) < filament_colors.size() && decode_color(filament_colors[size_t(i)], rgb)) + m_filaments_colors[i] = wxColour(rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar()); + else m_filaments_colors[i] = *wxGREEN; + + if (size_t(i) < physical_count) { + m_filaments_name[i] = wxString(std::to_string(i + 1) + ": " + filament_presets[size_t(i)]); + continue; } - //parse the filaments - m_filaments_name[i] = wxString(std::to_string(i+1) + ": " + filament_presets[i]); + size_t mixed_offset = 0; + for (const MixedFilament &mf : wxGetApp().preset_bundle->mixed_filaments.mixed_filaments()) { + if (!mf.enabled || mf.deleted) + continue; + if (size_t(i) != physical_count + mixed_offset) { + ++mixed_offset; + continue; + } - i++; + 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)); + break; + } + + if (m_filaments_name[i].empty()) + m_filaments_name[i] = wxString::Format("%d: Filament %d", i + 1, i + 1); } return 0; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fa3985ed09..4e42859452 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3873,6 +3873,22 @@ void Sidebar::update_mixed_filament_panel() if (!p->m_panel_mixed_filaments_title || !p->m_panel_mixed_filaments_content) return; + auto refresh_model_canvas_colors = []() { + Plater *plater = wxGetApp().plater(); + if (plater == nullptr) + return; + + auto refresh_canvas = [](GLCanvas3D *canvas) { + if (canvas == nullptr || !canvas->is_initialized()) + return; + canvas->update_volumes_colors_by_extruder(); + canvas->render(); + }; + + refresh_canvas(plater->get_view3D_canvas3D()); + refresh_canvas(plater->get_assmeble_canvas3D()); + }; + int prev_rows_view_y = 0; for (wxWindow *child : p->m_panel_mixed_filaments_content->GetChildren()) { if (auto *scrolled = dynamic_cast(child)) { @@ -4325,6 +4341,7 @@ void Sidebar::update_mixed_filament_panel() p->m_panel_mixed_filaments_title->Hide(); p->m_panel_mixed_filaments_content->Hide(); Layout(); + refresh_model_canvas_colors(); return; } @@ -4740,6 +4757,7 @@ void Sidebar::update_mixed_filament_panel() p->m_panel_mixed_filaments_content->Layout(); m_scrolled_sizer->Layout(); Layout(); + refresh_model_canvas_colors(); } void Sidebar::add_filament() { @@ -7142,6 +7160,32 @@ std::vector Plater::priv::load_files(const std::vector& input_ << boost::format(", plate_data.size %1%, project_preset.size %2%, is_bbs_3mf %3%, file_version %4% \n") % plate_data.size() % project_presets.size() % (en_3mf_file_type == En3mfType::From_BBS) % file_version.to_string(); + auto imported_string_count = [&config_loaded](const char *key) -> size_t { + if (const auto *opt = config_loaded.option(key)) + return opt->values.size(); + return 0; + }; + auto imported_float_count = [&config_loaded](const char *key) -> size_t { + if (const auto *opt = config_loaded.option(key)) + return opt->values.size(); + return 0; + }; + + std::vector imported_filament_colors; + size_t imported_physical_filaments = 0; + if (const auto *filament_colors_opt = config_loaded.option("filament_colour")) { + imported_filament_colors = filament_colors_opt->values; + imported_physical_filaments = imported_filament_colors.size(); + } + if (imported_physical_filaments == 0) + imported_physical_filaments = imported_string_count("filament_settings_id"); + if (imported_physical_filaments == 0) + imported_physical_filaments = imported_string_count("filament_ids"); + if (imported_physical_filaments == 0) + imported_physical_filaments = imported_string_count("default_filament_colour"); + if (imported_physical_filaments == 0) + imported_physical_filaments = imported_float_count("nozzle_diameter"); + // 1. add extruder for prusa model if the number of existing extruders is not enough // 2. add extruder for BBS or Other model if only import geometry if (en_3mf_file_type == En3mfType::From_Prusa || (load_model && !load_config)) { @@ -7154,6 +7198,57 @@ std::vector Plater::priv::load_files(const std::vector& input_ } } int size = extruderIds.size() == 0 ? 0 : *(extruderIds.rbegin()); + const bool geometry_only_project_import = load_model && !load_config && imported_physical_filaments > 0; + const size_t desired_physical_filaments = geometry_only_project_import ? + std::min(imported_physical_filaments, size_t(MAXIMUM_EXTRUDER_NUMBER)) : 0; + BOOST_LOG_TRIVIAL(info) << "3MF geometry import filament detection" + << " imported_physical=" << imported_physical_filaments + << " imported_colors=" << imported_filament_colors.size() + << " config_filament_settings_id=" << imported_string_count("filament_settings_id") + << " config_filament_ids=" << imported_string_count("filament_ids") + << " config_default_filament_colour=" << imported_string_count("default_filament_colour") + << " config_nozzle_diameter=" << imported_float_count("nozzle_diameter") + << " model_max_extruder=" << size + << " geometry_only_project_import=" << (geometry_only_project_import ? 1 : 0); + if (geometry_only_project_import) + size = int(desired_physical_filaments); + + PresetBundle *preset_bundle = wxGetApp().preset_bundle; + if (geometry_only_project_import && preset_bundle != nullptr) { + const size_t current_num_filaments = preset_bundle->filament_presets.size(); + const bool current_project_empty = this->model.objects.empty(); + if (current_project_empty) { + static const t_config_option_keys imported_project_option_keys = { + "filament_colour", + "mixed_filament_definitions", + "mixed_filament_gradient_mode", + "mixed_filament_height_lower_bound", + "mixed_filament_height_upper_bound", + "mixed_filament_advanced_dithering", + "mixed_filament_pointillism_pixel_size", + "mixed_filament_pointillism_line_gap", + "mixed_filament_surface_indentation" + }; + preset_bundle->project_config.apply_only(config_loaded, imported_project_option_keys, true); + if (current_num_filaments != desired_physical_filaments) + preset_bundle->set_num_filaments(unsigned(desired_physical_filaments)); + else + preset_bundle->update_multi_material_filament_presets(); + BOOST_LOG_TRIVIAL(info) << "3MF geometry import applied imported project config" + << " current_num_filaments=" << current_num_filaments + << " desired_physical_filaments=" << desired_physical_filaments + << " mixed_enabled=" << preset_bundle->mixed_filaments.enabled_count(); + wxGetApp().plater()->on_filaments_change(desired_physical_filaments); + } else if (current_num_filaments < desired_physical_filaments) { + std::vector new_colors; + if (imported_filament_colors.size() > current_num_filaments) { + new_colors.assign(imported_filament_colors.begin() + current_num_filaments, + imported_filament_colors.begin() + desired_physical_filaments); + } + preset_bundle->set_num_filaments(unsigned(desired_physical_filaments), new_colors); + wxGetApp().plater()->on_filaments_change(desired_physical_filaments); + } + } int filament_size = sidebar->combos_filament().size(); while (filament_size < MAXIMUM_EXTRUDER_NUMBER && filament_size < size) { @@ -7497,6 +7592,10 @@ std::vector Plater::priv::load_files(const std::vector& input_ // BBS: add preset combo box re-active logic // currently found only needs re-active here wxGetApp().load_current_presets(false, false); + // Some preset-tab refresh paths rebuild printer/filament UI from the + // active presets but do not preserve the mixed manager instance. + // Rebuild it explicitly from project_config before clamping object IDs. + preset_bundle->update_multi_material_filament_presets(); // Update filament colors for the MM-printer profile in the full config // to avoid black (default) colors for Extruders in the ObjectList, // when for extruder colors are used filament colors