From 0d4123192f1e936cd0a72ff40359cf53d51e37c1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 30 Nov 2020 13:45:17 +0100 Subject: [PATCH 1/5] Removed GLCanvas3D from parameters of NotificationManager methods --- src/slic3r/GUI/GLCanvas3D.cpp | 7 ++- src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/NotificationManager.cpp | 64 ++++++++++++++------------ src/slic3r/GUI/NotificationManager.hpp | 28 +++++------ src/slic3r/GUI/Plater.cpp | 28 +++++------ src/slic3r/Utils/PresetUpdater.cpp | 2 +- 6 files changed, 67 insertions(+), 64 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a8221ce69c..65f261effb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -636,9 +636,9 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool auto ¬ification_manager = *wxGetApp().plater()->get_notification_manager(); if (state) { if(error) - notification_manager.push_plater_error_notification(text,*(wxGetApp().plater()->get_current_canvas3D())); + notification_manager.push_plater_error_notification(text); else - notification_manager.push_plater_warning_notification(text, *(wxGetApp().plater()->get_current_canvas3D())); + notification_manager.push_plater_warning_notification(text); } else { if (error) notification_manager.close_plater_error_notification(text); @@ -1699,8 +1699,7 @@ void GLCanvas3D::render() m_tooltip.render(m_mouse.position, *this); wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this); - - wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width()); + wxGetApp().plater()->get_notification_manager()->render_notifications(get_overlay_window_width()); wxGetApp().imgui()->render(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 7a8b78664a..c0ffb14151 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -820,7 +820,7 @@ bool GUI_App::on_init_inner() app_config->save(); if (this->plater_ != nullptr) { if (*Semver::parse(SLIC3R_VERSION) < *Semver::parse(into_u8(evt.GetString()))) { - this->plater_->get_notification_manager()->push_notification(NotificationType::NewAppAvailable, *(this->plater_->get_current_canvas3D())); + this->plater_->get_notification_manager()->push_notification(NotificationType::NewAppAvailable); } } }); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index e59d61d0a0..15c84af9e7 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -849,19 +849,19 @@ NotificationManager::NotificationManager(wxEvtHandler* evt_handler) : m_evt_handler(evt_handler) { } -void NotificationManager::push_notification(const NotificationType type, GLCanvas3D& canvas, int timestamp) +void NotificationManager::push_notification(const NotificationType type, int timestamp) { auto it = std::find_if(basic_notifications.begin(), basic_notifications.end(), boost::bind(&NotificationData::type, boost::placeholders::_1) == type); assert(it != basic_notifications.end()); if (it != basic_notifications.end()) - push_notification_data( *it, canvas, timestamp); + push_notification_data(*it, timestamp); } -void NotificationManager::push_notification(const std::string& text, GLCanvas3D& canvas, int timestamp) +void NotificationManager::push_notification(const std::string& text, int timestamp) { - push_notification_data({ NotificationType::CustomNotification, NotificationLevel::RegularNotification, 10, text }, canvas, timestamp ); + push_notification_data({ NotificationType::CustomNotification, NotificationLevel::RegularNotification, 10, text }, timestamp); } -void NotificationManager::push_notification(const std::string& text, NotificationManager::NotificationLevel level, GLCanvas3D& canvas, int timestamp) +void NotificationManager::push_notification(const std::string& text, NotificationManager::NotificationLevel level, int timestamp) { int duration = 0; switch (level) { @@ -872,32 +872,32 @@ void NotificationManager::push_notification(const std::string& text, Notificatio assert(false); return; } - push_notification_data({ NotificationType::CustomNotification, level, duration, text }, canvas, timestamp); + push_notification_data({ NotificationType::CustomNotification, level, duration, text }, timestamp); } -void NotificationManager::push_slicing_error_notification(const std::string& text, GLCanvas3D& canvas) +void NotificationManager::push_slicing_error_notification(const std::string& text) { set_all_slicing_errors_gray(false); - push_notification_data({ NotificationType::SlicingError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, canvas, 0); + push_notification_data({ NotificationType::SlicingError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, 0); close_notification_of_type(NotificationType::SlicingComplete); } -void NotificationManager::push_slicing_warning_notification(const std::string& text, bool gray, GLCanvas3D& canvas, ObjectID oid, int warning_step) +void NotificationManager::push_slicing_warning_notification(const std::string& text, bool gray, ObjectID oid, int warning_step) { NotificationData data { NotificationType::SlicingWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }; auto notification = std::make_unique(data, m_id_provider, m_evt_handler); notification->object_id = oid; notification->warning_step = warning_step; - if (push_notification_data(std::move(notification), canvas, 0)) { + if (push_notification_data(std::move(notification), 0)) { m_pop_notifications.back()->set_gray(gray); } } -void NotificationManager::push_plater_error_notification(const std::string& text, GLCanvas3D& canvas) +void NotificationManager::push_plater_error_notification(const std::string& text) { - push_notification_data({ NotificationType::PlaterError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, canvas, 0); + push_notification_data({ NotificationType::PlaterError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, 0); } -void NotificationManager::push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas) +void NotificationManager::push_plater_warning_notification(const std::string& text) { - push_notification_data({ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }, canvas, 0); + push_notification_data({ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }, 0); // dissaper if in preview set_in_preview(m_in_preview); } @@ -951,7 +951,7 @@ void NotificationManager::close_slicing_errors_and_warnings() } } } -void NotificationManager::push_slicing_complete_notification(GLCanvas3D& canvas, int timestamp, bool large) +void NotificationManager::push_slicing_complete_notification(int timestamp, bool large) { std::string hypertext; int time = 10; @@ -963,8 +963,7 @@ void NotificationManager::push_slicing_complete_notification(GLCanvas3D& canvas, } NotificationData data{ NotificationType::SlicingComplete, NotificationLevel::RegularNotification, time, _u8L("Slicing finished."), hypertext, [](wxEvtHandler* evnthndlr){ if (evnthndlr != nullptr) wxPostEvent(evnthndlr, ExportGcodeNotificationClickedEvent(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED)); return true; } }; - push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, large), - canvas, timestamp); + push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, large), timestamp); } void NotificationManager::set_slicing_complete_print_time(const std::string &info) { @@ -1001,37 +1000,36 @@ void NotificationManager::remove_slicing_warnings_of_released_objects(const std: notification->close(); } } -void NotificationManager::push_exporting_finished_notification(GLCanvas3D& canvas, std::string path, std::string dir_path, bool on_removable) +void NotificationManager::push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable) { close_notification_of_type(NotificationType::ExportFinished); - NotificationData data{ NotificationType::ExportFinished, NotificationLevel::RegularNotification, 0, _u8L("Exporting finished.") +"\n"+ path }; - push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, on_removable, path, dir_path), - canvas, 0); + NotificationData data{ NotificationType::ExportFinished, NotificationLevel::RegularNotification, 0, _u8L("Exporting finished.") + "\n" + path }; + push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, on_removable, path, dir_path), 0); } -void NotificationManager::push_progress_bar_notification(const std::string& text, GLCanvas3D& canvas, float percentage) +void NotificationManager::push_progress_bar_notification(const std::string& text, float percentage) { NotificationData data{ NotificationType::ProgressBar, NotificationLevel::ProgressBarNotification, 0, text }; - push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, 0),canvas, 0); + push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, 0), 0); } -void NotificationManager::set_progress_bar_percentage(const std::string& text, float percentage, GLCanvas3D& canvas) +void NotificationManager::set_progress_bar_percentage(const std::string& text, float percentage) { bool found = false; for (std::unique_ptr& notification : m_pop_notifications) { if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) { dynamic_cast(notification.get())->set_percentage(percentage); - canvas.request_extra_frame(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); found = true; } } if (!found) { - push_progress_bar_notification(text, canvas, percentage); + push_progress_bar_notification(text, percentage); } } -bool NotificationManager::push_notification_data(const NotificationData ¬ification_data, GLCanvas3D& canvas, int timestamp) +bool NotificationManager::push_notification_data(const NotificationData& notification_data, int timestamp) { - return push_notification_data(std::make_unique(notification_data, m_id_provider, m_evt_handler), canvas, timestamp); + return push_notification_data(std::make_unique(notification_data, m_id_provider, m_evt_handler), timestamp); } -bool NotificationManager::push_notification_data(std::unique_ptr notification, GLCanvas3D& canvas, int timestamp) +bool NotificationManager::push_notification_data(std::unique_ptr notification, int timestamp) { // if timestamped notif, push only new one if (timestamp != 0) { @@ -1041,6 +1039,9 @@ bool NotificationManager::push_notification_data(std::unique_ptrget_current_canvas3D(); + if (this->activate_existing(notification.get())) { m_pop_notifications.back()->update(notification->get_data()); canvas.request_extra_frame(); @@ -1051,7 +1052,7 @@ bool NotificationManager::push_notification_data(std::unique_ptrget_current_canvas3D(); + // iterate thru notifications and render them / erease them for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { if ((*it)->get_finished()) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index da544391d7..d8ca1e0bdc 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -87,16 +87,16 @@ public: NotificationManager(wxEvtHandler* evt_handler); // Push a prefabricated notification from basic_notifications (see the table at the end of this file). - void push_notification(const NotificationType type, GLCanvas3D& canvas, int timestamp = 0); + void push_notification(const NotificationType type, int timestamp = 0); // Push a NotificationType::CustomNotification with NotificationLevel::RegularNotification and 10s fade out interval. - void push_notification(const std::string& text, GLCanvas3D& canvas, int timestamp = 0); + void push_notification(const std::string& text, int timestamp = 0); // Push a NotificationType::CustomNotification with provided notification level and 10s for RegularNotification. // ErrorNotification and ImportantNotification are never faded out. - void push_notification(const std::string& text, NotificationLevel level, GLCanvas3D& canvas, int timestamp = 0); + void push_notification(const std::string& text, NotificationLevel level, int timestamp = 0); // Creates Slicing Error notification with a custom text and no fade out. - void push_slicing_error_notification(const std::string& text, GLCanvas3D& canvas); + void push_slicing_error_notification(const std::string& text); // Creates Slicing Warning notification with a custom text and no fade out. - void push_slicing_warning_notification(const std::string& text, bool gray, GLCanvas3D& canvas, ObjectID oid, int warning_step); + void push_slicing_warning_notification(const std::string& text, bool gray, ObjectID oid, int warning_step); // marks slicing errors as gray void set_all_slicing_errors_gray(bool g); // marks slicing warings as gray @@ -108,32 +108,32 @@ public: // living_oids is expected to be sorted. void remove_slicing_warnings_of_released_objects(const std::vector& living_oids); // Object partially outside of the printer working space, cannot print. No fade out. - void push_plater_error_notification(const std::string& text, GLCanvas3D& canvas); + void push_plater_error_notification(const std::string& text); // Object fully out of the printer working space and such. No fade out. - void push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas); + void push_plater_warning_notification(const std::string& text); // Closes error or warning of the same text void close_plater_error_notification(const std::string& text); void close_plater_warning_notification(const std::string& text); // Creates special notification slicing complete. // If large = true (Plater side bar is closed), then printing time and export button is shown // at the notification and fade-out is disabled. Otherwise the fade out time is set to 10s. - void push_slicing_complete_notification(GLCanvas3D& canvas, int timestamp, bool large); + void push_slicing_complete_notification(int timestamp, bool large); // Add a print time estimate to an existing SlicingComplete notification. void set_slicing_complete_print_time(const std::string &info); // Called when the side bar changes its visibility, as the "slicing complete" notification supplements // the "slicing info" normally shown at the side bar. void set_slicing_complete_large(bool large); // Exporting finished, show this information with path, button to open containing folder and if ejectable - eject button - void push_exporting_finished_notification(GLCanvas3D& canvas, std::string path, std::string dir_path, bool on_removable); + void push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable); // notification with progress bar - void push_progress_bar_notification(const std::string& text, GLCanvas3D& canvas, float percentage = 0); - void set_progress_bar_percentage(const std::string& text, float percentage, GLCanvas3D& canvas); + void push_progress_bar_notification(const std::string& text, float percentage = 0); + void set_progress_bar_percentage(const std::string& text, float percentage); // Close old notification ExportFinished. void new_export_began(bool on_removable); // finds ExportFinished notification and closes it if it was to removable device void device_ejected(); // renders notifications in queue and deletes expired ones - void render_notifications(GLCanvas3D& canvas, float overlay_width); + void render_notifications(float overlay_width); // finds and closes all notifications of given type void close_notification_of_type(const NotificationType type); // Which view is active? Plater or G-code preview? Hide warnings in G-code preview. @@ -366,8 +366,8 @@ 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, GLCanvas3D& canvas, int timestamp); - bool push_notification_data(std::unique_ptr notification, GLCanvas3D& canvas, int timestamp); + bool push_notification_data(const NotificationData& notification_data, int timestamp); + bool push_notification_data(std::unique_ptr notification, int timestamp); //finds older notification of same type and moves it to the end of queue. returns true if found bool activate_existing(const NotificationManager::PopNotification* notification); // Put the more important notifications to the bottom of the list. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6709800ff8..f24e4d2498 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2106,12 +2106,12 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) if (evt.data.second) { this->show_action_buttons(this->ready_to_slice); notification_manager->close_notification_of_type(NotificationType::ExportFinished); - notification_manager->push_notification(format(_L("Successfully unmounted. The device %s(%s) can now be safely removed from the computer."),evt.data.first.name, evt.data.first.path), - NotificationManager::NotificationLevel::RegularNotification, *q->get_current_canvas3D()); - } else { - notification_manager->push_notification(format(_L("Ejecting of device %s(%s) has failed."), evt.data.first.name, evt.data.first.path), - NotificationManager::NotificationLevel::ErrorNotification, *q->get_current_canvas3D()); - } + notification_manager->push_notification(format(_L("Successfully unmounted. The device %s(%s) can now be safely removed from the computer."), evt.data.first.name, evt.data.first.path), + NotificationManager::NotificationLevel::RegularNotification); + } else { + notification_manager->push_notification(format(_L("Ejecting of device %s(%s) has failed."), evt.data.first.name, evt.data.first.path), + NotificationManager::NotificationLevel::ErrorNotification); + } }); this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this, q](RemovableDrivesChangedEvent &) { this->show_action_buttons(this->ready_to_slice); @@ -2938,7 +2938,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool } else { // The print is not valid. // Show error as notification. - notification_manager->push_slicing_error_notification(err, *q->get_current_canvas3D()); + notification_manager->push_slicing_error_notification(err); return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; } } else if (! this->delayed_error_message.empty()) { @@ -3499,7 +3499,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) this->statusbar()->set_progress(evt.status.percent); this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8("…")); - //notification_manager->set_progress_bar_percentage("Slicing progress", (float)evt.status.percent / 100.0f, *q->get_current_canvas3D()); + //notification_manager->set_progress_bar_percentage("Slicing progress", (float)evt.status.percent / 100.0f); } if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE | PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) { switch (this->printer_technology) { @@ -3541,8 +3541,8 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) // Now process state.warnings. for (auto const& warning : state.warnings) { if (warning.current) { - notification_manager->push_slicing_warning_notification(warning.message, false, *q->get_current_canvas3D(), object_id, warning_step); - add_warning(warning, object_id.id); + notification_manager->push_slicing_warning_notification(warning.message, false, object_id, warning_step); + add_warning(warning, object_id.id); } } } @@ -3550,7 +3550,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) void Plater::priv::on_slicing_completed(wxCommandEvent & evt) { - notification_manager->push_slicing_complete_notification(*q->get_current_canvas3D(), evt.GetInt(), is_sidebar_collapsed()); + notification_manager->push_slicing_complete_notification(evt.GetInt(), is_sidebar_collapsed()); switch (this->printer_technology) { case ptFFF: this->update_fff_scene(); @@ -3644,7 +3644,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) else show_error(q, message); } else - notification_manager->push_slicing_error_notification(message, *q->get_current_canvas3D()); + notification_manager->push_slicing_error_notification(message); this->statusbar()->set_status_text(from_u8(message)); if (evt.invalidate_plater()) { @@ -3690,10 +3690,10 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) // If writing to removable drive was scheduled, show notification with eject button if (exporting_status == ExportingStatus::EXPORTING_TO_REMOVABLE && !this->process_completed_with_error) { show_action_buttons(false); - notification_manager->push_exporting_finished_notification(*q->get_current_canvas3D(), last_output_path, last_output_dir_path, true); + notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, true); wxGetApp().removable_drive_manager()->set_exporting_finished(true); }else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !this->process_completed_with_error) - notification_manager->push_exporting_finished_notification(*q->get_current_canvas3D(), last_output_path, last_output_dir_path, false); + notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, false); } exporting_status = ExportingStatus::NOT_EXPORTING; } diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 10de8b0efe..60dfe05c78 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -827,7 +827,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 } } else { p->set_waiting_updates(updates); - GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::PresetUpdateAvailable, *(GUI::wxGetApp().plater()->get_current_canvas3D())); + GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::PresetUpdateAvailable); } // MsgUpdateConfig will show after the notificaation is clicked From bcb2a4884b1e119907bc6898e01f4b8f094e9137 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Dec 2020 15:12:32 +0100 Subject: [PATCH 2/5] NotificationManager -> Separate notification update from its render to reduce the amount of scene refresh --- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 24 ++- src/slic3r/GUI/NotificationManager.cpp | 242 ++++++++++++++++++++++++- src/slic3r/GUI/NotificationManager.hpp | 61 ++++++- 4 files changed, 315 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 45ca1a7c31..33b13aefbe 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -89,6 +89,7 @@ #define ENABLE_2_3_0_BETA2 1 #define ENABLE_ARROW_KEYS_WITH_SLIDERS (1 && ENABLE_2_3_0_BETA2) +#define ENABLE_NEW_NOTIFICATIONS_FADE_OUT (1 && ENABLE_2_3_0_BETA2) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e2d3a95852..84b17efb7b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1703,7 +1703,7 @@ void GLCanvas3D::render() m_tooltip.render(m_mouse.position, *this); wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this); - wxGetApp().plater()->get_notification_manager()->render_notifications(get_overlay_window_width()); + wxGetApp().plater()->get_notification_manager()->render_notifications(get_overlay_window_width()); wxGetApp().imgui()->render(); @@ -2358,6 +2358,14 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) if (!m_initialized) return; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + NotificationManager* notification_mgr = wxGetApp().plater()->get_notification_manager(); + if (notification_mgr->requires_update()) + notification_mgr->update_notifications(); + + m_dirty |= notification_mgr->requires_render(); +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + m_dirty |= m_main_toolbar.update_items_state(); m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state(); @@ -2365,12 +2373,24 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera()); m_dirty |= mouse3d_controller_applied; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + if (!m_dirty) { + if (notification_mgr->requires_update()) + evt.RequestMore(); + return; + } +#else if (!m_dirty) return; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT _refresh_if_shown_on_screen(); +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + if (m_extra_frame_requested || mouse3d_controller_applied || notification_mgr->requires_update()) { +#else if (m_extra_frame_requested || mouse3d_controller_applied) { +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT m_dirty = true; m_extra_frame_requested = false; evt.RequestMore(); @@ -2505,7 +2525,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case WXK_BACK: case WXK_DELETE: post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); break; - default: evt.Skip(); + default: evt.Skip(); } } else if ((evt.GetModifiers() & shiftMask) != 0) { diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 15c84af9e7..bf4142b9c5 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -18,6 +18,9 @@ static constexpr float GAP_WIDTH = 10.0f; static constexpr float SPACE_RIGHT_PANEL = 10.0f; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT +static constexpr float FADING_OUT_DURATION = 2.0f; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT namespace Slic3r { namespace GUI { @@ -134,6 +137,96 @@ NotificationManager::PopNotification::PopNotification(const NotificationData &n, { //init(); } +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT +void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) +{ + if (m_hidden) { + m_top_y = initial_y - GAP_WIDTH; + return; + } + + Size cnv_size = canvas.get_canvas_size(); + ImGuiWrapper& imgui = *wxGetApp().imgui(); + ImVec2 mouse_pos = ImGui::GetMousePos(); + float right_gap = SPACE_RIGHT_PANEL + (move_from_overlay ? overlay_width + m_line_height * 5 : 0); + + if (m_line_height != ImGui::CalcTextSize("A").y) + init(); + + set_next_window_size(imgui); + + // top y of window + m_top_y = initial_y + m_window_height; + + ImVec2 win_pos(1.0f * (float)cnv_size.get_width() - right_gap, 1.0f * (float)cnv_size.get_height() - m_top_y); + imgui.set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f); + imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always); + + // find if hovered + m_hovered = false; + if (mouse_pos.x < win_pos.x && mouse_pos.x > win_pos.x - m_window_width && mouse_pos.y > win_pos.y && mouse_pos.y < win_pos.y + m_window_height) { + ImGui::SetNextWindowFocus(); + m_hovered = true; + } + + // color change based on fading out + bool fading_pop = false; + if (m_fading_out) { + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), m_fading_out, m_current_fade_opacity); + fading_pop = true; + } + + // background color + if (m_is_gray) { + ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f); + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity); + } + else if (m_data.level == NotificationLevel::ErrorNotification) { + ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); + backcolor.x += 0.3f; + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity); + } + else if (m_data.level == NotificationLevel::WarningNotification) { + ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); + backcolor.x += 0.3f; + backcolor.y += 0.15f; + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity); + } + + // name of window - probably indentifies window and is shown so last_end add whitespaces according to id + if (m_id == 0) + m_id = m_id_provider.allocate_id(); + std::string name = "!!Ntfctn" + std::to_string(m_id); + if (imgui.begin(name, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar)) { + ImVec2 win_size = ImGui::GetWindowSize(); + + //FIXME: dont forget to us this for texts + //GUI::format(_utf8(L())); + + /* + //countdown numbers + ImGui::SetCursorPosX(15); + ImGui::SetCursorPosY(15); + imgui.text(std::to_string(m_remaining_time).c_str()); + */ + + render_left_sign(imgui); + render_text(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); + render_close_button(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); + m_minimize_b_visible = false; + if (m_multiline && m_lines_count > 3) + render_minimize_button(imgui, win_pos.x, win_pos.y); + } + imgui.end(); + + if (m_is_gray || m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) + ImGui::PopStyleColor(); + + if (fading_pop) + ImGui::PopStyleColor(2); +} +#else NotificationManager::PopNotification::RenderResult NotificationManager::PopNotification::render(GLCanvas3D& canvas, const float& initial_y, bool move_from_overlay, float overlay_width) { if (!m_initialized) { @@ -268,6 +361,7 @@ NotificationManager::PopNotification::RenderResult NotificationManager::PopNotif ImGui::PopStyleColor(); return ret_val; } +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT void NotificationManager::PopNotification::count_spaces() { //determine line width @@ -528,6 +622,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img ImGui::PopStyleColor(); ImGui::PopStyleColor(); } +#if !ENABLE_NEW_NOTIFICATIONS_FADE_OUT void NotificationManager::PopNotification::render_countdown(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { /* @@ -575,6 +670,7 @@ void NotificationManager::PopNotification::render_countdown(ImGuiWrapper& imgui, m_countdown_frame++; */ } +#endif // !ENABLE_NEW_NOTIFICATIONS_FADE_OUT void NotificationManager::PopNotification::render_left_sign(ImGuiWrapper& imgui) { if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) { @@ -643,6 +739,52 @@ bool NotificationManager::PopNotification::compare_text(const std::string& text) return false; } +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT +void NotificationManager::PopNotification::update_state() +{ + if (!m_initialized) + init(); + + if (m_hidden) { + m_state = EState::Static; + return; + } + + if (m_hovered) { + // reset fading + m_fading_out = false; + m_current_fade_opacity = 1.0f; + m_remaining_time = m_data.duration; + } + + if (m_counting_down) { + if (m_fading_out && m_current_fade_opacity <= 0.0f) + m_finished = true; + else if (!m_fading_out && m_remaining_time == 0) { + m_fading_out = true; + m_fading_start = wxGetLocalTimeMillis(); + } + } + + if (m_finished) { + m_state = EState::Finished; + return; + } + if (m_close_pending) { + m_finished = true; + m_state = EState::ClosePending; + return; + } + if (m_fading_out) { + if (!m_paused) { + wxMilliClock_t curr_time = wxGetLocalTimeMillis() - m_fading_start; + m_current_fade_opacity = std::clamp(1.0f - 0.001f * static_cast(curr_time.GetValue()) / FADING_OUT_DURATION, 0.0f, 1.0f); + } + m_state = EState::FadingOut; + } +} +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool large) : NotificationManager::PopNotification(n, id_provider, evt_handler) { @@ -1031,6 +1173,10 @@ bool NotificationManager::push_notification_data(const NotificationData& notific } bool NotificationManager::push_notification_data(std::unique_ptr notification, int timestamp) { +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + m_requires_update = true; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + // if timestamped notif, push only new one if (timestamp != 0) { if (m_used_timestamps.find(timestamp) == m_used_timestamps.end()) { @@ -1052,6 +1198,21 @@ bool NotificationManager::push_notification_data(std::unique_ptrget_current_canvas3D(); + float last_y = 0.0f; + + for (const auto& notification : m_pop_notifications) { + notification->render(canvas, last_y, m_move_from_overlay && !m_in_preview, overlay_width); + if (notification->get_state() != PopNotification::EState::Finished) + last_y = notification->get_top() + GAP_WIDTH; + } +} +#else void NotificationManager::render_notifications(float overlay_width) { float last_x = 0.0f; @@ -1063,9 +1224,9 @@ void NotificationManager::render_notifications(float overlay_width) GLCanvas3D& canvas = *wxGetApp().plater()->get_current_canvas3D(); - // iterate thru notifications and render them / erease them + // iterate thru notifications and render them / erase them for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { - if ((*it)->get_finished()) { + if ((*it)->is_finished()) { it = m_pop_notifications.erase(it); } else { (*it)->set_paused(m_hovered); @@ -1115,6 +1276,7 @@ void NotificationManager::render_notifications(float overlay_width) // If any of the notifications is fading out, 100% of the CPU/GPU is consumed. canvas.request_extra_frame(); } +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT void NotificationManager::sort_notifications() { @@ -1122,7 +1284,7 @@ void NotificationManager::sort_notifications() std::stable_sort(m_pop_notifications.begin(), m_pop_notifications.end(), [](const std::unique_ptr &n1, const std::unique_ptr &n2) { int n1l = (int)n1->get_data().level; int n2l = (int)n2->get_data().level; - if (n1l == n2l && n1->get_is_gray() && !n2->get_is_gray()) + if (n1l == n2l && n1->is_gray() && !n2->is_gray()) return true; return (n1l < n2l); }); @@ -1133,7 +1295,7 @@ bool NotificationManager::activate_existing(const NotificationManager::PopNotifi NotificationType new_type = notification->get_type(); const std::string &new_text = notification->get_data().text1; for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end(); ++it) { - if ((*it)->get_type() == new_type && !(*it)->get_finished()) { + if ((*it)->get_type() == new_type && !(*it)->is_finished()) { if (new_type == NotificationType::CustomNotification || new_type == NotificationType::PlaterWarning) { if (!(*it)->compare_text(new_text)) continue; @@ -1166,6 +1328,78 @@ void NotificationManager::set_in_preview(bool preview) } } +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT +void NotificationManager::update_notifications() +{ + static size_t last_size = 0; + + for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { + std::unique_ptr& notification = *it; + if (notification->get_state() == PopNotification::EState::Finished) + it = m_pop_notifications.erase(it); + else { + notification->set_paused(m_hovered); + notification->update_state(); + ++it; + } + } + + m_requires_update = false; + for (const std::unique_ptr& notification : m_pop_notifications) { + if (notification->requires_update()) { + m_requires_update = true; + break; + } + } + + // update hovering state + m_hovered = false; + for (const std::unique_ptr& notification : m_pop_notifications) { + if (notification->is_hovered()) { + m_hovered = true; + break; + } + } + + size_t curr_size = m_pop_notifications.size(); + m_requires_render = m_hovered || (last_size != curr_size); + last_size = curr_size; + + if (!m_requires_render) { + for (const std::unique_ptr& notification : m_pop_notifications) { + if (notification->requires_render()) { + m_requires_render = true; + break; + } + } + } + + // actualizate timers + wxWindow* p = dynamic_cast(wxGetApp().plater()); + while (p->GetParent() != nullptr) + p = p->GetParent(); + wxTopLevelWindow* top_level_wnd = dynamic_cast(p); + if (!top_level_wnd->IsActive()) + return; + + { + // Control the fade-out. + // time in seconds + long now = wxGetLocalTime(); + // Pausing fade-out when the mouse is over some notification. + if (!m_hovered && m_last_time < now) { + if (now - m_last_time >= 1) { + for (auto& notification : m_pop_notifications) { + if (notification->get_state() != PopNotification::EState::Static) + notification->substract_remaining_time(); + } + } + m_last_time = now; + } + } +} +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + bool NotificationManager::has_slicing_error_notification() { return std::any_of(m_pop_notifications.begin(), m_pop_notifications.end(), [](auto &n) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index d8ca1e0bdc..81c57eccb3 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -140,7 +140,13 @@ public: void set_in_preview(bool preview); // Move to left to avoid colision with variable layer height gizmo. void set_move_from_overlay(bool move) { m_move_from_overlay = move; } - + +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + void update_notifications(); + bool requires_update() const { return m_requires_update; } + bool requires_render() const { return m_requires_render; } +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + private: // duration 0 means not disapearing struct NotificationData { @@ -175,6 +181,17 @@ private: class PopNotification { public: +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + enum class EState + { + Unknown, + Static, + Countdown, + FadingOut, + ClosePending, + Finished + }; +#else enum class RenderResult { Finished, @@ -183,27 +200,41 @@ private: Countdown, Hovered }; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler); virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); } +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width); +#else RenderResult render(GLCanvas3D& canvas, const float& initial_y, bool move_from_overlay, float overlay_width); +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT // close will dissapear notification on next render void close() { m_close_pending = true; } // data from newer notification of same type void update(const NotificationData& n); - bool get_finished() const { return m_finished || m_close_pending; } + bool is_finished() const { return m_finished || m_close_pending; } +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + bool is_hovered() const { return m_hovered; } +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT // returns top after movement float get_top() const { return m_top_y; } //returns top in actual frame float get_current_top() const { return m_top_y; } const NotificationType get_type() const { return m_data.type; } - const NotificationData get_data() const { return m_data; } - const bool get_is_gray() const { return m_is_gray; } + const NotificationData get_data() const { return m_data; } + const bool is_gray() const { return m_is_gray; } // Call equals one second down void substract_remaining_time() { m_remaining_time--; } void set_gray(bool g) { m_is_gray = g; } void set_paused(bool p) { m_paused = p; } bool compare_text(const std::string& text); void hide(bool h) { m_hidden = h; } +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + void update_state(); + bool requires_render() const { return m_fading_out || m_close_pending || m_finished; } + bool requires_update() const { return m_state != EState::Static; } + EState get_state() const { return m_state; } +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT protected: // Call after every size change @@ -218,9 +249,11 @@ private: virtual void render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x , const float win_pos_y); +#if !ENABLE_NEW_NOTIFICATIONS_FADE_OUT void render_countdown(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x , const float win_pos_y); +#endif // !ENABLE_NEW_NOTIFICATIONS_FADE_OUT virtual void render_hypertext(ImGuiWrapper& imgui, const float text_x, const float text_y, const std::string text, @@ -237,7 +270,10 @@ private: // For reusing ImGUI windows. NotificationIDProvider &m_id_provider; - int m_id { 0 }; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + EState m_state { EState::Unknown }; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + int m_id { 0 }; bool m_initialized { false }; // Main text std::string m_text1; @@ -252,15 +288,22 @@ private: bool m_paused { false }; int m_countdown_frame { 0 }; bool m_fading_out { false }; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + wxMilliClock_t m_fading_start { 0LL }; +#else // total time left when fading beggins - float m_fading_time { 0.0f }; - float m_current_fade_opacity { 1.f }; + float m_fading_time{ 0.0f }; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + float m_current_fade_opacity { 1.0f }; // If hidden the notif is alive but not visible to user bool m_hidden { false }; // m_finished = true - does not render, marked to delete bool m_finished { false }; // Will go to m_finished next render bool m_close_pending { false }; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + bool m_hovered { false }; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT // variables to count positions correctly // all space without text float m_window_width_offset; @@ -390,6 +433,10 @@ private: bool m_in_preview { false }; // True if the layer editing is enabled in Plater, so that the notifications are shifted left of it. bool m_move_from_overlay { false }; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + bool m_requires_update{ false }; + bool m_requires_render{ false }; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT //prepared (basic) notifications const std::vector basic_notifications = { From 6835c5ad354995c1e074c7b26843c6dcc4d17271 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Dec 2020 15:40:52 +0100 Subject: [PATCH 3/5] Fixed build on MAC --- src/slic3r/GUI/Mouse3DController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 24da199dbd..57748af6f0 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -602,7 +602,7 @@ void Mouse3DController::disconnected() m_params_by_device[m_device_str] = m_params_ui; m_device_str.clear(); m_connected = false; - wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::Mouse3dDisconnected, *(wxGetApp().plater()->get_current_canvas3D())); + wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::Mouse3dDisconnected); wxGetApp().plater()->CallAfter([]() { Plater *plater = wxGetApp().plater(); From e42e25c933d3b6eb865d1d360c800dbe58b70f95 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 4 Dec 2020 10:48:44 +0100 Subject: [PATCH 4/5] 1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode. 2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode. The physical printer is only activated if it references the printer_settings_id loaded from the same file. 3) When loading the presets from 3MF, AMF, GCode, the "external" profiles are no more created for profiles which differ from the local profiles the loaded profiles reference. Instead, the referenced profile is activated and modified with the loaded preset. If the referenced profile does not exist, but the profile refers to a system profile with the "inherits" fileds, the system profile is loaded and modified instead. This works for all profiles with the exception of multi-extruder printer with multiple filament profiles modified. In that case the first modified filament profile will be loaded as modified, while the other modified profiles will be loaded as "external". This should fix Physical printer + 3mf file, wrong preset used to generate gcode #5178 and possibly https://github.com/prusa3d/PrusaSlicer/issues/5272 --- src/libslic3r/Preset.cpp | 69 ++++++++++++++++++++++++--------- src/libslic3r/Preset.hpp | 15 ++++++- src/libslic3r/PresetBundle.cpp | 55 +++++++++++++++----------- src/libslic3r/PresetBundle.hpp | 2 +- src/libslic3r/Print.cpp | 14 ++++--- src/libslic3r/PrintConfig.cpp | 4 ++ src/libslic3r/SLAPrint.cpp | 14 ++++--- src/slic3r/GUI/ConfigWizard.cpp | 2 +- 8 files changed, 118 insertions(+), 57 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index c7e0c50409..8d3d6b5f23 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -731,8 +731,9 @@ static bool profile_print_params_same(const DynamicPrintConfig &cfg_old, const D // Load a preset from an already parsed config file, insert it into the sorted sequence of presets // and select it, losing previous modifications. -// In case -Preset& PresetCollection::load_external_preset( +// Only a single profile could be edited at at the same time, which introduces complexity when loading +// filament profiles for multi-extruder printers. +std::pair PresetCollection::load_external_preset( // Path to the profile source file (a G-code, an AMF or 3MF file, a config file) const std::string &path, // Name of the profile, derived from the source file name. @@ -742,14 +743,23 @@ Preset& PresetCollection::load_external_preset( // Config to initialize the preset from. const DynamicPrintConfig &config, // Select the preset after loading? - bool select) + LoadAndSelect select) { // Load the preset over a default preset, so that the missing fields are filled in from the default preset. DynamicPrintConfig cfg(this->default_preset_for(config).config); cfg.apply_only(config, cfg.keys(), true); + std::string &inherits = Preset::inherits(cfg); + if (select == LoadAndSelect::Never) { + // Some filament profile has been selected and modified already. + // Check whether this profile is equal to the modified edited profile. + const Preset &edited = this->get_edited_preset(); + if ((edited.name == original_name || edited.name == inherits) && profile_print_params_same(edited.config, cfg)) + // Just point to that already selected and edited profile. + return std::make_pair(&(*this->find_preset_internal(edited.name)), false); + } // Is there a preset already loaded with the name stored inside the config? - std::deque::iterator it = this->find_preset_internal(original_name); - bool found = it != m_presets.end() && it->name == original_name; + std::deque::iterator it = this->find_preset_internal(original_name); + bool found = it != m_presets.end() && it->name == original_name; if (! found) { // Try to match the original_name against the "renamed_from" profile names of loaded system profiles. it = this->find_preset_renamed(original_name); @@ -757,19 +767,40 @@ Preset& PresetCollection::load_external_preset( } if (found && profile_print_params_same(it->config, cfg)) { // The preset exists and it matches the values stored inside config. - if (select) + if (select == LoadAndSelect::Always) this->select_preset(it - m_presets.begin()); - return *it; + return std::make_pair(&(*it), false); } - // Update the "inherits" field. - std::string &inherits = Preset::inherits(cfg); - if (found && inherits.empty()) { - // There is a profile with the same name already loaded. Should we update the "inherits" field? - if (it->vendor == nullptr) - inherits = it->inherits(); - else - inherits = it->name; + if (! found && select != LoadAndSelect::Never && ! inherits.empty()) { + // Try to use a system profile as a base to select the system profile + // and override its settings with the loaded ones. + assert(it == m_presets.end()); + it = this->find_preset_internal(inherits); + found = it != m_presets.end() && it->name == inherits; + if (found && profile_print_params_same(it->config, cfg)) { + // The system preset exists and it matches the values stored inside config. + if (select == LoadAndSelect::Always) + this->select_preset(it - m_presets.begin()); + return std::make_pair(&(*it), false); + } } + if (found) { + if (select != LoadAndSelect::Never) { + // Select the existing preset and override it with new values, so that + // the differences will be shown in the preset editor against the referenced profile. + this->select_preset(it - m_presets.begin()); + this->get_edited_preset().config.apply(config); + this->update_dirty(); + assert(this->get_edited_preset().is_dirty); + return std::make_pair(&(*it), this->get_edited_preset().is_dirty); + } + if (inherits.empty()) { + // Update the "inherits" field. + // There is a profile with the same name already loaded. Should we update the "inherits" field? + inherits = it->vendor ? it->name : it->inherits(); + } + } + // The external preset does not match an internal preset, load the external preset. std::string new_name; for (size_t idx = 0;; ++ idx) { @@ -790,19 +821,19 @@ Preset& PresetCollection::load_external_preset( break; if (profile_print_params_same(it->config, cfg)) { // The preset exists and it matches the values stored inside config. - if (select) + if (select == LoadAndSelect::Always) this->select_preset(it - m_presets.begin()); - return *it; + return std::make_pair(&(*it), false); } // Form another profile name. } // Insert a new profile. - Preset &preset = this->load_preset(path, new_name, std::move(cfg), select); + Preset &preset = this->load_preset(path, new_name, std::move(cfg), select == LoadAndSelect::Always); preset.is_external = true; if (&this->get_selected_preset() == &preset) this->get_edited_preset().is_external = true; - return preset; + return std::make_pair(&preset, false); } Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select) diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 6409eee7ca..c54dd3fd87 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -287,7 +287,18 @@ public: Preset& load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select = true); Preset& load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select = true); - Preset& load_external_preset( + // Returns a loaded preset, returns true if an existing preset was selected AND modified from config. + // In that case the successive filament loaded for a multi material printer should not be modified, but + // an external preset should be created instead. + enum class LoadAndSelect { + // Never select + Never, + // Always select + Always, + // Select a profile only if it was modified. + OnlyIfModified, + }; + std::pair load_external_preset( // Path to the profile source file (a G-code, an AMF or 3MF file, a config file) const std::string &path, // Name of the profile, derived from the source file name. @@ -297,7 +308,7 @@ public: // Config to initialize the preset from. const DynamicPrintConfig &config, // Select the preset after loading? - bool select = true); + LoadAndSelect select = LoadAndSelect::Always); // Save the preset under a new name. If the name is different from the old one, // a new preset is stored into the list of presets. diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 44df000c3b..b020f874ed 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -49,7 +49,7 @@ PresetBundle::PresetBundle() : // initialized based on PrintConfigDef(), but to empty values (zeros, empty vectors, empty strings). // // "compatible_printers", "compatible_printers_condition", "inherits", - // "print_settings_id", "filament_settings_id", "printer_settings_id", + // "print_settings_id", "filament_settings_id", "printer_settings_id", "printer_settings_id" // "printer_vendor", "printer_model", "printer_variant", "default_print_profile", "default_filament_profile" // Create the ID config keys, as they are not part of the Static print config classes. @@ -586,6 +586,7 @@ DynamicPrintConfig PresetBundle::full_fff_config() const out.option("print_settings_id", true)->value = this->prints.get_selected_preset_name(); out.option("filament_settings_id", true)->values = this->filament_presets; out.option("printer_settings_id", true)->value = this->printers.get_selected_preset_name(); + out.option("physical_printer_settings_id", true)->value = this->physical_printers.get_selected_printer_name(); // Serialize the collected "compatible_printers_condition" and "inherits" fields. // There will be 1 + num_exturders fields for "inherits" and 2 + num_extruders for "compatible_printers_condition" stored. @@ -637,6 +638,7 @@ DynamicPrintConfig PresetBundle::full_sla_config() const out.option("sla_print_settings_id", true)->value = this->sla_prints.get_selected_preset_name(); out.option("sla_material_settings_id", true)->value = this->sla_materials.get_selected_preset_name(); out.option("printer_settings_id", true)->value = this->printers.get_selected_preset_name(); + out.option("physical_printer_settings_id", true)->value = this->physical_printers.get_selected_printer_name(); // Serialize the collected "compatible_printers_condition" and "inherits" fields. // There will be 1 + num_exturders fields for "inherits" and 2 + num_extruders for "compatible_printers_condition" stored. @@ -712,6 +714,7 @@ void PresetBundle::load_config_file(const std::string &path) } // Load a config file from a boost property_tree. This is a private method called from load_config_file. +// is_external == false on if called from ConfigWizard void PresetBundle::load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config) { PrinterTechnology printer_technology = Preset::printer_technology(config); @@ -798,14 +801,17 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool compatible_prints_condition = compatible_prints_condition_values.front(); Preset *loaded = nullptr; if (is_external) { - loaded = &this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config); + auto [aloaded, modified] = this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config); + loaded = aloaded; } else { - loaded = &this->filaments.load_preset(this->filaments.path_from_name(name), name, config); + // called from Config Wizard. + loaded= &this->filaments.load_preset(this->filaments.path_from_name(name), name, config); loaded->save(); } this->filament_presets.clear(); this->filament_presets.emplace_back(loaded->name); } else { + assert(is_external); // Split the filament presets, load each of them separately. std::vector configs(num_extruders, this->filaments.default_preset().config); // loop through options and scatter them into configs. @@ -826,6 +832,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool // To avoid incorrect selection of the first filament preset (means a value of Preset->m_idx_selected) // in a case when next added preset take a place of previosly selected preset, // we should add presets from last to first + bool any_modified = false; for (int i = (int)configs.size()-1; i >= 0; i--) { DynamicPrintConfig &cfg = configs[i]; // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets. @@ -833,24 +840,15 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool cfg.opt_string("compatible_prints_condition", true) = compatible_prints_condition_values[i]; cfg.opt_string("inherits", true) = inherits_values[i + 1]; // Load all filament presets, but only select the first one in the preset dialog. - Preset *loaded = nullptr; - if (is_external) - loaded = &this->filaments.load_external_preset(name_or_path, name, - (i < int(old_filament_profile_names->values.size())) ? old_filament_profile_names->values[i] : "", - std::move(cfg), i == 0); - else { - // Used by the config wizard when creating a custom setup. - // Therefore this block should only be called for a single extruder. - char suffix[64]; - if (i == 0) - suffix[0] = 0; - else - sprintf(suffix, "%d", (int)i); - std::string new_name = name + suffix; - loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name), - new_name, std::move(cfg), i == 0); - loaded->save(); - } + auto [loaded, modified] = this->filaments.load_external_preset(name_or_path, name, + (i < int(old_filament_profile_names->values.size())) ? old_filament_profile_names->values[i] : "", + std::move(cfg), + i == 0 ? + PresetCollection::LoadAndSelect::Always : + any_modified ? + PresetCollection::LoadAndSelect::Never : + PresetCollection::LoadAndSelect::OnlyIfModified); + any_modified |= modified; this->filament_presets[i] = loaded->name; } } @@ -864,10 +862,23 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool load_preset(this->sla_materials, 1, "sla_material_settings_id"); load_preset(this->printers, 2, "printer_settings_id"); break; - default: break; + default: + break; } this->update_compatible(PresetSelectCompatibleType::Never); + + const std::string &physical_printer = config.option("physical_printer_settings_id", true)->value; + if (this->printers.get_edited_preset().is_external || physical_printer.empty()) { + this->physical_printers.unselect_printer(); + } else { + // Activate the physical printer profile if possible. + PhysicalPrinter *pp = this->physical_printers.find_printer(physical_printer, true); + if (pp != nullptr && std::find(pp->preset_names.begin(), pp->preset_names.end(), this->printers.get_edited_preset().name) != pp->preset_names.end()) + this->physical_printers.select_printer(*pp); + else + this->physical_printers.unselect_printer(); + } } // Load the active configuration of a config bundle from a boost property_tree. This is a private method called from load_config_file. diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 609e25e2c2..5d7cc84ba2 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -70,7 +70,7 @@ public: // Load user configuration and store it into the user profiles. // This method is called by the configuration wizard. - void load_config(const std::string &name, DynamicPrintConfig config) + void load_config_from_wizard(const std::string &name, DynamicPrintConfig config) { this->load_config_file_config(name, false, std::move(config)); } // Load configuration that comes from a model file containing configuration, such as 3MF et al. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a8d4b2ea05..e71890852e 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -596,9 +596,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ #endif /* _DEBUG */ // Normalize the config. - new_full_config.option("print_settings_id", true); - new_full_config.option("filament_settings_id", true); - new_full_config.option("printer_settings_id", true); + new_full_config.option("print_settings_id", true); + new_full_config.option("filament_settings_id", true); + new_full_config.option("printer_settings_id", true); + new_full_config.option("physical_printer_settings_id", true); new_full_config.normalize_fdm(); // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles. @@ -627,9 +628,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ if (! full_config_diff.empty()) { update_apply_status(this->invalidate_step(psGCodeExport)); // Set the profile aliases for the PrintBase::output_filename() - m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone()); - m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone()); - m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone()); + m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone()); + m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone()); + m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone()); + m_placeholder_parser.set("physical_printer_preset", new_full_config.option("physical_printer_settings_id")->clone()); // We want the filament overrides to be applied over their respective extruder parameters by the PlaceholderParser. // see "Placeholders do not respect filament overrides." GH issue #3649 m_placeholder_parser.apply_config(filament_overrides); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index fe64e8aa62..42edcafde5 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1680,6 +1680,10 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionString("")); def->cli = ConfigOptionDef::nocli; + def = this->add("physical_printer_settings_id", coString); + def->set_default_value(new ConfigOptionString("")); + def->cli = ConfigOptionDef::nocli; + def = this->add("raft_layers", coInt); def->label = L("Raft layers"); def->category = L("Support material"); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index f36e48aa66..65fac73f34 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -193,9 +193,10 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con #endif /* _DEBUG */ // Normalize the config. - config.option("sla_print_settings_id", true); - config.option("sla_material_settings_id", true); - config.option("printer_settings_id", true); + config.option("sla_print_settings_id", true); + config.option("sla_material_settings_id", true); + config.option("printer_settings_id", true); + config.option("physical_printer_settings_id", true); // Collect changes to print config. t_config_option_keys print_diff = m_print_config.diff(config); t_config_option_keys printer_diff = m_printer_config.diff(config); @@ -228,9 +229,10 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con // update_apply_status(this->invalidate_step(slapsRasterize)); m_placeholder_parser.apply_config(config); // Set the profile aliases for the PrintBase::output_filename() - m_placeholder_parser.set("print_preset", config.option("sla_print_settings_id")->clone()); - m_placeholder_parser.set("material_preset", config.option("sla_material_settings_id")->clone()); - m_placeholder_parser.set("printer_preset", config.option("printer_settings_id")->clone()); + m_placeholder_parser.set("print_preset", config.option("sla_print_settings_id")->clone()); + m_placeholder_parser.set("material_preset", config.option("sla_material_settings_id")->clone()); + m_placeholder_parser.set("printer_preset", config.option("printer_settings_id")->clone()); + m_placeholder_parser.set("physical_printer_preset", config.option("physical_printer_settings_id")->clone()); } // It is also safe to change m_config now after this->invalidate_state_by_config_options() call. diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 18e89f1146..c0bfbe1412 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -2457,7 +2457,7 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese page_temps->apply_custom_config(*custom_config); const std::string profile_name = page_custom->profile_name(); - preset_bundle->load_config(profile_name, *custom_config); + preset_bundle->load_config_from_wizard(profile_name, *custom_config); } // Update the selections from the compatibilty. From e1fc0b17a28b119e3f2634d41db87d23da8f5033 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 4 Dec 2020 11:53:02 +0100 Subject: [PATCH 5/5] Ramp up layer cooling fan over X layers #848 Fan speed will be ramped up linearly from zero at layer disable_fan_first_layers to maximum at layer full_fan_speed_layer. full_fan_speed_layer will be ignored if lower than disable_fan_first_layers, in which case the fan will be running at maximum allowed speed at layer disable_fan_first_layers + 1.; WIP: The cooling PresetHints are likely not finalized yet. --- src/libslic3r/GCode/CoolingBuffer.cpp | 14 +++++++- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/Print.cpp | 1 + src/libslic3r/PrintConfig.cpp | 11 ++++++ src/libslic3r/PrintConfig.hpp | 2 ++ src/slic3r/GUI/PresetHints.cpp | 49 ++++++++++++++------------- src/slic3r/GUI/Tab.cpp | 3 +- 7 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 6815ea73a0..07ab197f27 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -682,7 +682,8 @@ std::string CoolingBuffer::apply_layer_cooldown( #define EXTRUDER_CONFIG(OPT) config.OPT.get_at(m_current_extruder) int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed); int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0; - if (layer_id >= (size_t)EXTRUDER_CONFIG(disable_fan_first_layers)) { + int disable_fan_first_layers = EXTRUDER_CONFIG(disable_fan_first_layers); + if (int(layer_id) >= disable_fan_first_layers) { int max_fan_speed = EXTRUDER_CONFIG(max_fan_speed); float slowdown_below_layer_time = float(EXTRUDER_CONFIG(slowdown_below_layer_time)); float fan_below_layer_time = float(EXTRUDER_CONFIG(fan_below_layer_time)); @@ -698,6 +699,17 @@ std::string CoolingBuffer::apply_layer_cooldown( } } bridge_fan_speed = EXTRUDER_CONFIG(bridge_fan_speed); + // Is the fan speed ramp enabled? + int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer); + // When ramping up fan speed from disable_fan_first_layers to full_fan_speed_layer, force disable_fan_first_layers above zero, + // so there will be a zero fan speed at least at the 1st layer. + disable_fan_first_layers = std::max(disable_fan_first_layers, 1); + if (int(layer_id) >= disable_fan_first_layers && int(layer_id) + 1 < full_fan_speed_layer) { + // Ramp up the fan speed from disable_fan_first_layers to full_fan_speed_layer. + float factor = float(int(layer_id + 1) - disable_fan_first_layers) / float(full_fan_speed_layer - disable_fan_first_layers); + fan_speed_new = clamp(0, 255, int(float(fan_speed_new ) * factor + 0.5f)); + bridge_fan_speed = clamp(0, 255, int(float(bridge_fan_speed) * factor + 0.5f)); + } #undef EXTRUDER_CONFIG bridge_fan_control = bridge_fan_speed > fan_speed_new; } else { diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index c7e0c50409..396a14631b 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -443,7 +443,7 @@ const std::vector& Preset::filament_options() "filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower", "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", - "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", + "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", "end_filament_gcode", // Retract overrides "filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a8d4b2ea05..a0c6620a9f 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -93,6 +93,7 @@ bool Print::invalidate_state_by_config_options(const std::vectormax = max_temp; def->set_default_value(new ConfigOptionInts { 200 }); + def = this->add("full_fan_speed_layer", coInts); + def->label = L("Full fan speed at layer"); + def->tooltip = L("Fan speed will be ramped up linearly from zero at layer \"disable_fan_first_layers\" " + "to maximum at layer \"full_fan_speed_layer\". " + "\"full_fan_speed_layer\" will be ignored if lower than \"disable_fan_first_layers\", in which case " + "the fan will be running at maximum allowed speed at layer \"disable_fan_first_layers\" + 1."); + def->min = 0; + def->max = 1000; + def->mode = comExpert; + def->set_default_value(new ConfigOptionInts { 0 }); + def = this->add("gap_fill_speed", coFloat); def->label = L("Gap fill"); def->category = L("Speed"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index aa7b159d03..9eb79d5fa2 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -852,6 +852,7 @@ public: ConfigOptionFloatOrPercent first_layer_extrusion_width; ConfigOptionFloatOrPercent first_layer_speed; ConfigOptionInts first_layer_temperature; + ConfigOptionInts full_fan_speed_layer; ConfigOptionFloat infill_acceleration; ConfigOptionBool infill_first; ConfigOptionInts max_fan_speed; @@ -925,6 +926,7 @@ protected: OPT_PTR(first_layer_extrusion_width); OPT_PTR(first_layer_speed); OPT_PTR(first_layer_temperature); + OPT_PTR(full_fan_speed_layer); OPT_PTR(infill_acceleration); OPT_PTR(infill_first); OPT_PTR(max_fan_speed); diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index c40c4c6acb..9f40ffe5bd 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -18,40 +18,43 @@ std::string PresetHints::cooling_description(const Preset &preset) { std::string out; - if (preset.config.opt_bool("cooling", 0)) { + bool cooling = preset.config.opt_bool("cooling", 0); + int fan_below_layer_time = preset.config.opt_int("fan_below_layer_time", 0); + int full_fan_speed_layer = preset.config.opt_int("full_fan_speed_layer", 0); + + if (cooling) { int slowdown_below_layer_time = preset.config.opt_int("slowdown_below_layer_time", 0); int min_fan_speed = preset.config.opt_int("min_fan_speed", 0); int max_fan_speed = preset.config.opt_int("max_fan_speed", 0); int min_print_speed = int(preset.config.opt_float("min_print_speed", 0) + 0.5); - int fan_below_layer_time = preset.config.opt_int("fan_below_layer_time", 0); - out += (boost::format(_utf8(L("If estimated layer time is below ~%1%s, " - "fan will run at %2%%% and print speed will be reduced " - "so that no less than %3%s are spent on that layer " - "(however, speed will never be reduced below %4%mm/s)."))) - % slowdown_below_layer_time % max_fan_speed % slowdown_below_layer_time % min_print_speed).str(); - - if (fan_below_layer_time > slowdown_below_layer_time) { - out += "\n" + (boost::format(_utf8(L("If estimated layer time is greater, but still below ~%1%s, " - "fan will run at a proportionally decreasing speed between %2%%% and %3%%%."))) - % fan_below_layer_time % max_fan_speed % min_fan_speed).str(); - } - out += "\n" + _utf8(L("During the other layers, fan")) + " "; - } else { - out = _utf8(L("Fan")) + " "; + out += GUI::format(_L("If estimated layer time is below ~%1%s, " + "fan will run at %2%%% and print speed will be reduced " + "so that no less than %3%s are spent on that layer " + "(however, speed will never be reduced below %4%mm/s)."), + slowdown_below_layer_time, max_fan_speed, slowdown_below_layer_time, min_print_speed); + if (fan_below_layer_time > slowdown_below_layer_time) + out += "\n" + + GUI::format(_L("If estimated layer time is greater, but still below ~%1%s, " + "fan will run at a proportionally decreasing speed between %2%%% and %3%%%."), + fan_below_layer_time, max_fan_speed, min_fan_speed); + out += "\n"; } if (preset.config.opt_bool("fan_always_on", 0)) { int disable_fan_first_layers = preset.config.opt_int("disable_fan_first_layers", 0); int min_fan_speed = preset.config.opt_int("min_fan_speed", 0); - out += (boost::format(_utf8(L("will always run at %1%%%"))) % min_fan_speed).str() + " "; - - if (disable_fan_first_layers > 1) - out += (boost::format(_utf8(L("except for the first %1% layers."))) % disable_fan_first_layers).str(); - else if (disable_fan_first_layers == 1) - out += _utf8(L("except for the first layer.")); + if (full_fan_speed_layer > fan_below_layer_time + 1) + out += GUI::format(_L("Fan speed will be ramped from zero at layer %1% to %2%%% at layer %3%."), disable_fan_first_layers, min_fan_speed, full_fan_speed_layer); + else { + out += GUI::format(cooling ? _L("During the other layers, fan will always run at %1%%%") : _L("Fan will always run at %1%%%"), min_fan_speed) + " "; + if (disable_fan_first_layers > 1) + out += GUI::format(_L("except for the first %1% layers."), disable_fan_first_layers); + else if (disable_fan_first_layers == 1) + out += GUI::format(_L("except for the first layer.")); + } } else - out += _utf8(L("will be turned off.")); + out += cooling ? _u8L("During the other layers, fan will be turned off.") : _u8L("Fan will be turned off."); return out; } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d125f8da68..57883e909a 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1847,6 +1847,7 @@ void TabFilament::build() optgroup->append_single_option_line("bridge_fan_speed", category_path + "fan-settings"); optgroup->append_single_option_line("disable_fan_first_layers", category_path + "fan-settings"); + optgroup->append_single_option_line("full_fan_speed_layer", category_path + "fan-settings"); optgroup = page->new_optgroup(L("Cooling thresholds"), 25); optgroup->append_single_option_line("fan_below_layer_time", category_path + "cooling-thresholds"); @@ -1999,7 +2000,7 @@ void TabFilament::toggle_options() for (auto el : { "max_fan_speed", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed" }) toggle_option(el, cooling); - for (auto el : { "min_fan_speed", "disable_fan_first_layers" }) + for (auto el : { "min_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer" }) toggle_option(el, fan_always_on); }