diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index bd6fb3d457..ad80156e01 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -230,37 +230,64 @@ double Flow::mm3_per_mm() const return res; } +static float min_nozzle_diameter(const PrintObject &print_object) +{ + const ConfigOptionFloats &nozzle_diameters = print_object.print()->config().nozzle_diameter; + float min_nozzle_diameter = std::numeric_limits::max(); + + for (const double nozzle_diameter : nozzle_diameters.values) { + min_nozzle_diameter = std::min(min_nozzle_diameter, static_cast(nozzle_diameter)); + } + + return min_nozzle_diameter; +} + Flow support_material_flow(const PrintObject *object, float layer_height) { + const PrintConfig &print_config = object->print()->config(); + const int extruder = object->config().support_material_extruder - 1; + + // If object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), use the smallest nozzle diameter. + const float nozzle_diameter = extruder >= 0 ? static_cast(print_config.nozzle_diameter.get_at(extruder)) : min_nozzle_diameter(*object); + return Flow::new_from_config_width( frSupportMaterial, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width, - // if object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), + nozzle_diameter, (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value)); } Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) { const PrintConfig &print_config = object->print()->config(); - const auto &width = (print_config.first_layer_extrusion_width.value > 0) ? print_config.first_layer_extrusion_width : object->config().support_material_extrusion_width; + const int extruder = object->config().support_material_extruder - 1; + + // If object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), use the smallest nozzle diameter. + const float nozzle_diameter = extruder >= 0 ? static_cast(print_config.nozzle_diameter.get_at(extruder)) : min_nozzle_diameter(*object); + const auto &width = (print_config.first_layer_extrusion_width.value > 0) ? print_config.first_layer_extrusion_width : object->config().support_material_extrusion_width; + return Flow::new_from_config_width( frSupportMaterial, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (width.value > 0) ? width : object->config().extrusion_width, - float(print_config.nozzle_diameter.get_at(object->config().support_material_extruder-1)), + nozzle_diameter, (layer_height > 0.f) ? layer_height : float(print_config.first_layer_height.get_abs_value(object->config().layer_height.value))); } Flow support_material_interface_flow(const PrintObject *object, float layer_height) { + const PrintConfig &print_config = object->print()->config(); + const int extruder = object->config().support_material_interface_extruder - 1; + + // If object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), use the smallest nozzle diameter. + const float nozzle_diameter = extruder >= 0 ? static_cast(print_config.nozzle_diameter.get_at(extruder)) : min_nozzle_diameter(*object); + return Flow::new_from_config_width( frSupportMaterialInterface, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (object->config().support_material_extrusion_width > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width, - // if object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_interface_extruder-1)), + nozzle_diameter, (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value)); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 92c8d49628..7097576f20 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -744,13 +744,11 @@ std::string Print::validate(std::vector* warnings) const }; for (PrintObject *object : m_objects) { if (object->has_support_material()) { - if ((object->config().support_material_extruder == 0 || object->config().support_material_interface_extruder == 0) && max_nozzle_diameter - min_nozzle_diameter > EPSILON) { + if (warnings != nullptr && (object->config().support_material_extruder == 0 || object->config().support_material_interface_extruder == 0) && max_nozzle_diameter - min_nozzle_diameter > EPSILON) { // The object has some form of support and either support_material_extruder or support_material_interface_extruder - // will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles - // are of the same diameter. - return _u8L("Printing with multiple extruders of differing nozzle diameters. " - "If support is to be printed with the current extruder (support_material_extruder == 0 or support_material_interface_extruder == 0), " - "all nozzles have to be of the same diameter."); + // will be printed with the current tool without a forced tool change. + // Notify the user that printing supports with different nozzle diameters is experimental and requires caution. + warnings->emplace_back("_SUPPORT_NOZZLE_DIAMETER_DIFFER"); } if (this->has_wipe_tower() && object->config().support_material_style != smsOrganic) { if (object->config().support_material_contact_distance == 0) { diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 99021fa8ab..9674b2f471 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -133,7 +133,7 @@ void NotificationManager::NotificationIDProvider::release_id(int) {} #endif //------PopNotification-------- -NotificationManager::PopNotification::PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler) : +NotificationManager::PopNotification::PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler, const bool multiline) : m_data (n) , m_id_provider (id_provider) , m_text1 (n.text1) @@ -141,6 +141,7 @@ NotificationManager::PopNotification::PopNotification(const NotificationData &n, , m_text2 (n.text2) , m_evt_handler (evt_handler) , m_notification_start (GLCanvas3D::timestamp_now()) + , m_multiline (multiline) {} void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) @@ -2157,10 +2158,11 @@ void NotificationManager::push_notification(NotificationType type, const std::string& hypertext, std::function callback, const std::string& text_after, - int timestamp) + const int timestamp, + const bool multiline) { int duration = get_standard_duration(level); - push_notification_data({ type, level, duration, text, hypertext, callback, text_after }, timestamp); + push_notification_data({ type, level, duration, text, hypertext, callback, text_after }, timestamp, multiline); } void NotificationManager::push_delayed_notification(const NotificationType type, std::function condition_callback, int64_t initial_delay, int64_t delay_interval) @@ -2839,9 +2841,9 @@ void NotificationManager::push_updated_item_info_notification(InfoItemType type) } } -bool NotificationManager::push_notification_data(const NotificationData& notification_data, int timestamp) +bool NotificationManager::push_notification_data(const NotificationData& notification_data, int timestamp, const bool multiline) { - return push_notification_data(std::make_unique(notification_data, m_id_provider, m_evt_handler), timestamp); + return push_notification_data(std::make_unique(notification_data, m_id_provider, m_evt_handler, multiline), timestamp); } bool NotificationManager::push_notification_data(std::unique_ptr notification, int timestamp) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 255a35b7d1..57ecbf4719 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -142,6 +142,8 @@ enum class NotificationType ShrinkageCompensationsDiffer, // Notification about using wipe tower with different nozzle diameters. WipeTowerNozzleDiameterDiffer, + // Notification about using supports with different nozzle diameters. + SupportNozzleDiameterDiffer, }; class NotificationManager @@ -181,7 +183,7 @@ public: // Push a NotificationType::CustomNotification with provided notification level and 10s for RegularNotificationLevel. // ErrorNotificationLevel are never faded out. void push_notification(NotificationType type, NotificationLevel level, const std::string& text, const std::string& hypertext = "", - std::function callback = std::function(), const std::string& text_after = "", int timestamp = 0); + std::function callback = std::function(), const std::string& text_after = "", int timestamp = 0, bool multiline = false); // Pushes basic_notification with delay. See push_delayed_notification_data. void push_delayed_notification(const NotificationType type, std::function condition_callback, int64_t initial_delay, int64_t delay_interval); // Removes all notifications of type from m_waiting_notifications @@ -344,7 +346,7 @@ private: Paused }; - PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler); + PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler, bool multiline = false); virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); } virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width); // close will dissapear notification on next render @@ -855,7 +857,7 @@ private: //pushes notification into the queue of notifications that are rendered //can be used to create custom notification - bool push_notification_data(const NotificationData& notification_data, int timestamp); + bool push_notification_data(const NotificationData& notification_data, int timestamp, bool multiline = false); bool push_notification_data(std::unique_ptr notification, int timestamp); // Delayed notifications goes first to the m_waiting_notifications vector and only after remaining time is <= 0 // and condition callback is success, notification is regular pushed from update function. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3985e77989..adf299b97d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2098,13 +2098,16 @@ void Plater::priv::process_validation_warning(const std::vector& wa if (warnings.empty()) notification_manager->close_notification_of_type(NotificationType::ValidateWarning); - // Always close warnings BedTemperaturesDiffer, ShrinkageCompensationsDiffer and WipeTowerNozzleDiameterDiffer before next processing. + // Always close warnings BedTemperaturesDiffer, ShrinkageCompensationsDiffer, WipeTowerNozzleDiameterDiffer and SupportNozzleDiameterDiffer before next processing. notification_manager->close_notification_of_type(NotificationType::BedTemperaturesDiffer); notification_manager->close_notification_of_type(NotificationType::ShrinkageCompensationsDiffer); notification_manager->close_notification_of_type(NotificationType::WipeTowerNozzleDiameterDiffer); + notification_manager->close_notification_of_type(NotificationType::SupportNozzleDiameterDiffer); for (std::string text : warnings) { std::string hypertext = ""; + std::string text_after = ""; + bool multiline = false; NotificationType notification_type = NotificationType::ValidateWarning; std::function action_fn = [](wxEvtHandler*){ return false; }; @@ -2133,12 +2136,22 @@ void Plater::priv::process_validation_warning(const std::vector& wa text = _u8L("Using the wipe tower for extruders with different nozzle diameters " "is experimental, so proceed with caution."); notification_type = NotificationType::WipeTowerNozzleDiameterDiffer; + } else if (text == "_SUPPORT_NOZZLE_DIAMETER_DIFFER") { + text = _u8L("Printing supports with different nozzle diameters " + "is experimental. For best results, switch to Organic supports and"); + hypertext = _u8L("assign a specific extruder for supports."); + multiline = true; + notification_type = NotificationType::SupportNozzleDiameterDiffer; + action_fn = [](wxEvtHandler*) { + GUI::wxGetApp().jump_to_option("support_material_extruder", Preset::Type::TYPE_PRINT, boost::nowide::widen("Multiple Extruders")); + return false; + }; } notification_manager->push_notification( notification_type, NotificationManager::NotificationLevel::WarningNotificationLevel, - _u8L("WARNING:") + "\n" + text, hypertext, action_fn + _u8L("WARNING:") + "\n" + text, hypertext, action_fn, text_after, 0, multiline ); } }