diff --git a/resources/info/nozzle_incompatibles.json b/resources/info/nozzle_incompatibles.json new file mode 100644 index 0000000000..e9fe23ed21 --- /dev/null +++ b/resources/info/nozzle_incompatibles.json @@ -0,0 +1,34 @@ +{ + "incompatible_nozzles":{ + "Standard":{ + "0.2":[ + "Bambu PLA Marble", + "Bambu PLA Sparkle", + "Bambu PLA Wood", + "Bambu PLA Galaxy", + "Bambu PETG Translucent" + ], + "0.4":[], + "0.6":[ + "Bambu PLA Silk+", + "Bambu PLA Silk", + "Bambu PLA Aero", + "Bambu ASA-Aero" + ], + "0.8":[ + "Bambu PLA Silk+", + "Bambu PLA Silk", + "Bambu PLA Aero", + "Bambu ASA-Aero" + ] + }, + "High Flow":{ + "0.4":[ + "Bambu PLA-CF", + "Bambu PETG-CF" + ], + "0.6":[], + "0.8":[] + } + } +} \ No newline at end of file diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index fc835a2124..3c02f17c7c 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2710,6 +2710,47 @@ int Print::get_hrc_by_nozzle_type(const NozzleType&type) return 0; } +std::vector Print::get_incompatible_filaments_by_nozzle(const float nozzle_diameter, const std::optional nozzle_volume_type) +{ + static std::map>> incompatible_filaments; + if(incompatible_filaments.empty()){ + fs::path file_path = fs::path(resources_dir()) / "info" / "nozzle_incompatibles.json"; + boost::nowide::ifstream in(file_path.string()); + json j; + try { + j = json::parse(in); + for(auto& [volume_type, diameter_list] : j["incompatible_nozzles"].items()) { + for(auto& [diameter, filaments]: diameter_list.items()){ + incompatible_filaments[volume_type][diameter] = filaments.get>(); + } + } + } + catch(const json::parse_error& err){ + in.close(); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << file_path.string() << " got a nlohmann::detail::parse_error, reason = " << err.what(); + + incompatible_filaments[get_nozzle_volume_type_string(NozzleVolumeType::nvtHighFlow)] = {}; + incompatible_filaments[get_nozzle_volume_type_string(NozzleVolumeType::nvtStandard)] = {}; + } + } + std::ostringstream oss; + oss << std::fixed << std::setprecision(1) << nozzle_diameter; + std::string diameter_str = oss.str(); + + if(nozzle_volume_type.has_value()){ + return incompatible_filaments[get_nozzle_volume_type_string(nozzle_volume_type.value())][diameter_str]; + } + + std::vector incompatible_filaments_list; + for(auto& [volume_type, diameter_list] : incompatible_filaments){ + auto iter = diameter_list.find(diameter_str); + if(iter != diameter_list.end()){ + append(incompatible_filaments_list, iter->second); + } + } + return incompatible_filaments_list; +} + void Print::finalize_first_layer_convex_hull() { append(m_first_layer_convex_hull.points, m_skirt_convex_hull); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 5395dc1d9f..584ae68723 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -1080,6 +1080,7 @@ public: Vec2d translate_to_print_space(const Point &point) const; static FilamentTempType get_filament_temp_type(const std::string& filament_type); static int get_hrc_by_nozzle_type(const NozzleType& type); + static std::vector get_incompatible_filaments_by_nozzle(const float nozzle_diameter, const std::optional nozzle_volume_type = std::nullopt); static FilamentCompatibilityType check_multi_filaments_compatibility(const std::vector& filament_types); // similar to check_multi_filaments_compatibility, but the input is int, and may be negative (means unset) static bool is_filaments_compatible(const std::vector& types); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b43c428522..4810b20e9e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -137,6 +137,11 @@ std::string& get_right_extruder_unprintable_text() { return right_unprintable_text; } +std::string& get_nozzle_filament_incompatible_text() { + static std::string nozzle_filament_incompatible_text; + return nozzle_filament_incompatible_text; +} + static std::string format_number(float value) { std::ostringstream oss; @@ -3030,15 +3035,19 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re //if (printer_technology != ptSLA || !contained_min_one) // _set_warning_notification(EWarning::SlaSupportsOutside, false); - bool tpu_valid = cur_plate->check_tpu_printable_status(wxGetApp().preset_bundle->full_config(), wxGetApp().preset_bundle->get_used_tpu_filaments(cur_plate->get_extruders(true))); + auto full_config_temp = wxGetApp().preset_bundle->full_config(); + bool tpu_valid = cur_plate->check_tpu_printable_status(full_config_temp, wxGetApp().preset_bundle->get_used_tpu_filaments(cur_plate->get_extruders(true))); _set_warning_notification(EWarning::TPUPrintableError, !tpu_valid); - bool filament_printable = cur_plate->check_filament_printable(wxGetApp().preset_bundle->full_config(), filament_printable_error_msg); + bool filament_printable = cur_plate->check_filament_printable(full_config_temp, filament_printable_error_msg); _set_warning_notification(EWarning::FilamentPrintableError, !filament_printable); - bool mix_pla_and_petg = cur_plate->check_mixture_of_pla_and_petg(wxGetApp().preset_bundle->full_config()); + bool mix_pla_and_petg = cur_plate->check_mixture_of_pla_and_petg(full_config_temp); _set_warning_notification(EWarning::MixUsePLAAndPETG, !mix_pla_and_petg); + bool filament_nozzle_compatible = cur_plate->check_compatible_of_nozzle_and_filament(full_config_temp, wxGetApp().preset_bundle->filament_presets, get_nozzle_filament_incompatible_text()); + _set_warning_notification(EWarning::NozzleFilamentIncompatible, !filament_nozzle_compatible); + bool model_fits = contained_min_one && !m_model->objects.empty() && !partlyOut && object_results.filaments.empty() && tpu_valid && filament_printable; post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, model_fits)); ppl.get_curr_plate()->update_slice_ready_status(model_fits); @@ -3056,6 +3065,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re _set_warning_notification(EWarning::PrimeTowerOutside, false); _set_warning_notification(EWarning::MultiExtruderPrintableError,false); _set_warning_notification(EWarning::MultiExtruderHeightOutside,false); + _set_warning_notification(EWarning::NozzleFilamentIncompatible,false); + post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false)); } } @@ -10105,6 +10116,10 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) case EWarning::PrimeTowerOutside: text = _u8L("The prime tower extends beyond the plate boundary."); break; + case EWarning::NozzleFilamentIncompatible: { + text = _u8L(get_nozzle_filament_incompatible_text()); + break; + } } //BBS: this may happened when exit the app, plater is null if (!wxGetApp().plater()) @@ -10129,6 +10144,14 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) else notification_manager.close_slicing_customize_error_notification(NotificationType::BBLMixUsePLAAndPETG, NotificationLevel::WarningNotificationLevel); } + else if (warning == EWarning::NozzleFilamentIncompatible){ + if(state){ + notification_manager.push_slicing_customize_error_notification(NotificationType::BBLNozzleFilamentIncompatible, NotificationLevel::WarningNotificationLevel, text); + } + else{ + notification_manager.close_slicing_customize_error_notification(NotificationType::BBLNozzleFilamentIncompatible, NotificationLevel::WarningNotificationLevel); + } + } else { if (state) notification_manager.push_plater_warning_notification(text); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 499090d531..4de222eb49 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -393,6 +393,7 @@ class GLCanvas3D FilamentUnPrintableOnFirstLayer, MixUsePLAAndPETG, PrimeTowerOutside, + NozzleFilamentIncompatible, }; class RenderStats diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 22df0de3bf..0da9314bb2 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -159,6 +159,7 @@ enum class NotificationType BBLSliceMultiExtruderHeightOutside, BBLBedFilamentIncompatible, BBLMixUsePLAAndPETG, + BBLNozzleFilamentIncompatible, NotificationTypeCount }; diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index f652f4b7af..5254a0d63d 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -1787,6 +1787,85 @@ bool PartPlate::check_mixture_of_pla_and_petg(const DynamicPrintConfig &config) return true; } +bool PartPlate::check_compatible_of_nozzle_and_filament(const DynamicPrintConfig &config, const std::vector &filament_presets, std::string &error_msg) +{ + float nozzle_diameter = config.option("nozzle_diameter")->values[0]; + auto volume_type_opt = config.option("nozzle_volume_type"); + + auto get_filament_alias = [](std::string preset_name) -> std::string { + size_t at_pos = preset_name.find('@'); + std::string alias = preset_name.substr(0, at_pos); + size_t first = alias.find_first_not_of(' '); + if (first == std::string::npos) return ""; + size_t last = alias.find_last_not_of(' '); + return alias.substr(first, last - first + 1); + }; + + bool with_same_volume_type = std::all_of(volume_type_opt->values.begin(), volume_type_opt->values.end(), + [first_value = volume_type_opt->values[0]](int value) { return value == first_value; }); + + std::set selected_filament_alias; + for (auto &filament_preset : filament_presets) { selected_filament_alias.insert(get_filament_alias(filament_preset)); } + + auto get_incompatible_selected = [&](const NozzleVolumeType volume_type) -> std::set { + std::vector incompatible_filaments = Print::get_incompatible_filaments_by_nozzle(nozzle_diameter, volume_type); + std::set ret; + for (auto &filament : selected_filament_alias) { + if (std::find(incompatible_filaments.begin(), incompatible_filaments.end(), filament) != incompatible_filaments.end()) ret.insert(filament); + } + return ret; + }; + + auto get_nozzle_msg = [](const float nozzle_diameter, const NozzleVolumeType volume_type) -> std::string { + std::ostringstream oss; + oss << std::fixed << std::setprecision(1) << nozzle_diameter; + std::string nozzle_msg = oss.str(); + ((nozzle_msg += "mm ") += _u8L(get_nozzle_volume_type_string(volume_type))) += _u8L(" nozzle"); + return nozzle_msg; + }; + + auto get_incompatible_filament_msg = [](const std::set &incompatible_selected_filaments) -> std::string { + std::string filament_str; + size_t idx = 0; + for (const auto &filament : incompatible_selected_filaments) { + if (idx > 0) filament_str += ','; + filament_str += filament; + ++idx; + } + return filament_str; + }; + + error_msg.clear(); + + std::set nozzle_volumes(volume_type_opt->values.begin(), volume_type_opt->values.end()); + std::map> incompatible_selected_map; + + for (auto volume_type_value : nozzle_volumes) { + NozzleVolumeType volume_type = static_cast(volume_type_value); + auto incompatible_selected = get_incompatible_selected(volume_type); + if (!incompatible_selected.empty()) incompatible_selected_map[volume_type] = incompatible_selected; + } + + if (incompatible_selected_map.empty()) return true; + + if (incompatible_selected_map.size() == 1) { + auto elem = incompatible_selected_map.begin(); + NozzleVolumeType volume_type = elem->first; + auto incompatible_selected = elem->second; + error_msg = GUI::format(_L("It is not recommended to print the following filament(s) with %1%: %2%\n"), get_nozzle_msg(nozzle_diameter, volume_type), + get_incompatible_filament_msg(incompatible_selected)); + } else { + std::string warning_msg = _u8L("It is not recommended to use the following nozzle and filament combinations:\n"); + for (auto &elem : incompatible_selected_map) { + NozzleVolumeType volume_type = elem.first; + auto incompatible_selected = elem.second; + warning_msg += GUI::format(_L("%1% with %2%\n"),get_nozzle_msg(nozzle_diameter, volume_type), get_incompatible_filament_msg(incompatible_selected)); + } + error_msg = warning_msg; + } + return false; +} + /*Vec3d PartPlate::calculate_wipe_tower_size(const DynamicPrintConfig &config, const double w, const double wipe_volume, int plate_extruder_size, bool use_global_objects) const { Vec3d wipe_tower_size; diff --git a/src/slic3r/GUI/PartPlate.hpp b/src/slic3r/GUI/PartPlate.hpp index 0b3ac7c276..3836eef6f7 100644 --- a/src/slic3r/GUI/PartPlate.hpp +++ b/src/slic3r/GUI/PartPlate.hpp @@ -335,6 +335,7 @@ public: bool check_filament_printable(const DynamicPrintConfig & config, wxString& error_message); bool check_tpu_printable_status(const DynamicPrintConfig & config, const std::vector &tpu_filaments); bool check_mixture_of_pla_and_petg(const DynamicPrintConfig & config); + bool check_compatible_of_nozzle_and_filament(const DynamicPrintConfig & config, const std::vector& filament_presets, std::string& error_msg); /* instance related operations*/ //judge whether instance is bound in plate or not