From d44525d916ca3472b81a4d2f7f62ad7413087353 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Sun, 5 Dec 2021 08:43:06 +0100 Subject: [PATCH 01/31] Fix of Upload and simulate wrong label (Issue #7424) --- src/slic3r/GUI/PrintHostDialogs.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 2cea580be3..09ba48b584 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -98,8 +98,9 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo } if (post_actions.has(PrintHostPostUploadAction::StartSimulation)) { - auto* btn_print = add_button(wxID_YES, false, _L("Upload and Simulate")); - btn_print->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) { + // Using wxID_MORE as a button identifier to be different from the other buttons, wxID_MORE has no other meaning here. + auto* btn_simulate = add_button(wxID_MORE, false, _L("Upload and Simulate")); + btn_simulate->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) { if (validate_path(txt_filename->GetValue())) { post_upload_action = PrintHostPostUploadAction::StartSimulation; EndDialog(wxID_OK); From bfce4f69019d2ddd2ffd4dbbacc8ef5d4f32f015 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Sun, 5 Dec 2021 09:09:34 +0100 Subject: [PATCH 02/31] Follow-up to 3622f06bed6175a337d2e20b5dc6ae9d770cb3cf Work around 3D scene focus after de-activation of the main window without having to resort to CallAfter(), which breaks on Linux with some window managers that follow mouser cursor. Fixes #5620 #6870 #6992 3622f06bed6175a337d2e20b5dc6ae9d770cb3cf was not a correct solution, it broke focus for non-modal windows. Fixes #7419 The actual issue seems to be caused by wxProgressDialog not playing well with modal dialogs closed just before wxProgressDialog opens. If wxProgressDialog parent was not a main frame, keyboard focus was not restored correctly after the wxProgressDialog closed. --- src/slic3r/GUI/GCodeViewer.cpp | 2 +- src/slic3r/GUI/GUI_ObjectList.cpp | 6 +++--- src/slic3r/GUI/MainFrame.cpp | 15 --------------- src/slic3r/GUI/MainFrame.hpp | 11 +---------- src/slic3r/GUI/Plater.cpp | 29 +---------------------------- src/slic3r/GUI/Plater.hpp | 10 ---------- 6 files changed, 6 insertions(+), 67 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index cdc9c33a4f..b86bd018d3 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1447,7 +1447,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) static const unsigned int progress_threshold = 1000; wxProgressDialog* progress_dialog = wxGetApp().is_gcode_viewer() ? new wxProgressDialog(_L("Generating toolpaths"), "...", - 100, wxGetApp().plater(), wxPD_AUTO_HIDE | wxPD_APP_MODAL) : nullptr; + 100, wxGetApp().mainframe, wxPD_AUTO_HIDE | wxPD_APP_MODAL) : nullptr; wxBusyCursor busy; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 7d689ff6a2..f4d27b0b27 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1446,7 +1446,7 @@ void ObjectList::load_part(ModelObject& model_object, std::vector& else wxGetApp().import_model(parent, input_files); - wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().plater(), wxPD_AUTO_HIDE); + wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().mainframe wxPD_AUTO_HIDE); wxBusyCursor busy; for (size_t i = 0; i < input_files.size(); ++i) { @@ -1506,7 +1506,7 @@ void ObjectList::load_modifier(ModelObject& model_object, std::vectorplater()->restore_keyboard_focus(); - return retval; -} -#endif - // // Called after the Preferences dialog is closed and the program settings are saved. // Update the UI based on the current preferences. diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 9e09bf0884..951ed70a1d 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -148,15 +148,6 @@ public: void update_title(); -#if defined(__linux__) || defined(_WIN32) - // wxWidgets callback to enable / disable window and all its children windows. - // called by wxWindowDisabler when entering / leaving modal dialog loop. - // Unfortunately the wxWindowDisabler calls Enable(true) after the wxEVT_ACTIVATE event is processed - // while MainFrame is not yet enabled, thus restoring focus in OnActivate() handler fails - // and we need to do it now. - bool Enable(bool enable = true) override; -#endif - void init_tabpanel(); void create_preset_tabs(); void add_created_tab(Tab* panel, const std::string& bmp_name = ""); @@ -211,7 +202,7 @@ public: SettingsDialog m_settings_dialog; DiffPresetDialog diff_dialog; wxWindow* m_plater_page{ nullptr }; - wxProgressDialog* m_progress_dialog { nullptr }; +// wxProgressDialog* m_progress_dialog { nullptr }; PrintHostQueueDialog* m_printhost_queue_dlg; // std::shared_ptr m_statusbar; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index dd1ccb91f1..0485b00753 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2351,7 +2351,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ } const auto loading = _L("Loading") + dots; - wxProgressDialog dlg(loading, "", 100, q, wxPD_AUTO_HIDE); + wxProgressDialog dlg(loading, "", 100, find_toplevel_parent(q), wxPD_AUTO_HIDE); wxBusyCursor busy; auto *new_model = (!load_model || one_by_one) ? nullptr : new Slic3r::Model(); @@ -6306,36 +6306,9 @@ void Plater::force_print_bed_update() void Plater::on_activate() { -#if defined(__linux__) || defined(_WIN32) - this->restore_keyboard_focus(); -#endif this->p->show_delayed_error_message(); } -#if defined(__linux__) || defined(_WIN32) -// wxWidgets callback to enable / disable window and all its children windows. -// called by wxProgressDialog when entering / leaving modal dialog loop. -// Unfortunately the wxProgressDialog calls Enable(true) after the wxEVT_ACTIVATE event is processed -// while MainFrame is not yet enabled, thus restoring focus in OnActivate() handler fails -// and we need to do it now. -bool Plater::Enable(bool enable) -{ - bool retval = wxPanel::Enable(enable); - if (enable && retval) - this->restore_keyboard_focus(); - return retval; -} -void Plater::restore_keyboard_focus() -{ - // Activating the main frame, and no window has keyboard focus. - // Set the keyboard focus to the visible Canvas3D. - if (this->p->view3D->IsShown() && wxWindow::FindFocus() != this->p->view3D->get_wxglcanvas()) - this->p->view3D->get_wxglcanvas()->SetFocus(); - else if (this->p->preview->IsShown() && wxWindow::FindFocus() != this->p->view3D->get_wxglcanvas()) - this->p->preview->get_wxglcanvas()->SetFocus(); -} -#endif // Linux or Windows - // Get vector of extruder colors considering filament color, if extruder color is undefined. std::vector Plater::get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result) const { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 6d742a832c..d3eead4ede 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -149,16 +149,6 @@ public: void render_project_state_debug_window() const; #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW -#if defined(__linux__) || defined(_WIN32) - // wxWidgets callback to enable / disable window and all its children windows. - // called by wxProgressDialog when entering / leaving modal dialog loop. - // Unfortunately the wxProgressDialog calls Enable(true) after the wxEVT_ACTIVATE event is processed - // while MainFrame is not yet enabled, thus restoring focus in OnActivate() handler fails - // and we need to do it now. - bool Enable(bool enable) override; - void restore_keyboard_focus(); -#endif - Sidebar& sidebar(); const Model& model() const; Model& model(); From d4d558ebee2323df3c6855fade17f3e748948071 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 6 Dec 2021 08:23:19 +0100 Subject: [PATCH 03/31] Fix of GCodeViewer crashes when opening app preferences #7430 --- src/slic3r/GUI/Preferences.cpp | 30 ++++++++++++++++++------------ src/slic3r/GUI/Preferences.hpp | 2 ++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 40ae0cb8c3..6820a42840 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -9,7 +9,6 @@ #include "Notebook.hpp" #include "ButtonsDescription.hpp" #include "OG_CustomCtrl.hpp" -#include namespace Slic3r { @@ -519,21 +518,29 @@ void PreferencesDialog::build(size_t selected_tab) this->CenterOnParent(); } -void PreferencesDialog::update_ctrls_alignment() +std::vector PreferencesDialog::optgroups() { - int max_ctrl_width{ 0 }; - std::initializer_list og_list = { m_optgroup_general.get(), m_optgroup_camera.get(), m_optgroup_gui.get() + std::vector out; + out.reserve(4); + for (ConfigOptionsGroup* opt : { m_optgroup_general.get(), m_optgroup_camera.get(), m_optgroup_gui.get() #ifdef _WIN32 , m_optgroup_dark_mode.get() #endif // _WIN32 - }; - for (auto og : og_list) { + }) + if (opt) + out.emplace_back(opt); + return out; +} + +void PreferencesDialog::update_ctrls_alignment() +{ + int max_ctrl_width{ 0 }; + for (ConfigOptionsGroup* og : this->optgroups()) if (int max = og->custom_ctrl->get_max_win_width(); max_ctrl_width < max) max_ctrl_width = max; - } if (max_ctrl_width) - for (auto og : og_list) + for (ConfigOptionsGroup* og : this->optgroups()) og->custom_ctrl->set_max_win_width(max_ctrl_width); } @@ -622,9 +629,8 @@ void PreferencesDialog::accept(wxEvent&) void PreferencesDialog::on_dpi_changed(const wxRect &suggested_rect) { - m_optgroup_general->msw_rescale(); - m_optgroup_camera->msw_rescale(); - m_optgroup_gui->msw_rescale(); + for (ConfigOptionsGroup* og : this->optgroups()) + og->msw_rescale(); msw_buttons_rescale(this, em_unit(), { wxID_OK, wxID_CANCEL }); @@ -788,7 +794,7 @@ void PreferencesDialog::init_highlighter(const t_config_option_key& opt_key) }); std::pair ctrl = { nullptr, nullptr }; - for (auto opt_group : { m_optgroup_general, m_optgroup_camera, m_optgroup_gui }) { + for (ConfigOptionsGroup* opt_group : this->optgroups()) { ctrl = opt_group->get_custom_ctrl_with_blinking_ptr(opt_key, -1); if (ctrl.first && ctrl.second) { m_highlighter.init(ctrl); diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 210b04d4fd..6c80121950 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -6,6 +6,7 @@ #include #include +#include #include class wxColourPickerCtrl; @@ -61,6 +62,7 @@ protected: void create_settings_mode_widget(); void create_settings_text_color_widget(); void init_highlighter(const t_config_option_key& opt_key); + std::vector optgroups(); struct PreferencesHighlighter { From c7a4f61238b70e09a48629fb67831b67bfe176f5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 6 Dec 2021 11:39:10 +0100 Subject: [PATCH 04/31] #7415 - GCodeViewer::refresh_render_paths() - Remove empty render paths to avoid calling glMultiDrawElements() with empty data while rendering toolpaths. --- src/slic3r/GUI/GCodeViewer.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index b86bd018d3..9fcef6a65b 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2433,6 +2433,18 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool #endif } + // removes empty render paths + for (size_t b = 0; b < m_buffers.size(); ++b) { + TBuffer* buffer = const_cast(&m_buffers[b]); + std::set::iterator it = buffer->render_paths.begin(); + while (it != buffer->render_paths.end()) { + if (it->sizes.empty() || it->offsets.empty()) + it = buffer->render_paths.erase(it); + else + ++it; + } + } + // second pass: for buffers using instanced and batched models, update the instances render ranges for (size_t b = 0; b < m_buffers.size(); ++b) { TBuffer& buffer = const_cast(m_buffers[b]); From f5dc746e9450bb94396ba3fcbeac18a6a9a6039a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 6 Dec 2021 11:39:28 +0100 Subject: [PATCH 05/31] Fix for #7419 - Non-modal settings window loses focus after accepting dialog (MSW specific issue) MessageDialog is used instead of wxMessageDialog on MSW for supporting of the Light/Dark color mode. But a constructor of the MsgDialog replaces a parent which is equal to nullptr with the MainFrame . That is why non-modal dialog with Preset Settings loses a focus after close of the MessageDialog. "m_msg_dlg_parent" is added to ConfigManipulation class. ConfigManipulation's instance owed by Tab will use the Tab as a parent for MessageDialogs. => The MessageDialog with information about configuration incompatibility will always appear over related SettingsTab and a non-modal dialog with Preset Settings will not lose the focus. --- src/slic3r/GUI/ConfigManipulation.cpp | 26 +++++++++----------------- src/slic3r/GUI/ConfigManipulation.hpp | 5 ++++- src/slic3r/GUI/Tab.cpp | 2 +- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 0b2af37b8e..fe108b1a6b 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -47,8 +47,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con if (config->opt_float("layer_height") < EPSILON) { const wxString msg_text = _(L("Layer height is not valid.\n\nThe layer height will be reset to 0.01.")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); - MessageDialog dialog(nullptr, msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); DynamicPrintConfig new_conf = *config; is_msg_dlg_already_exist = true; dialog.ShowModal(); @@ -60,8 +59,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con if (config->option("first_layer_height")->value < EPSILON) { const wxString msg_text = _(L("First layer height is not valid.\n\nThe first layer height will be reset to 0.01.")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); - MessageDialog dialog(nullptr, msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); DynamicPrintConfig new_conf = *config; is_msg_dlg_already_exist = true; dialog.ShowModal(); @@ -90,8 +88,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con "- Detect thin walls disabled")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable Spiral Vase?")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Spiral Vase")), - MessageDialog dialog(nullptr, msg_text, _(L("Spiral Vase")), + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Spiral Vase")), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); @@ -126,8 +123,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con "(both support_material_extruder and support_material_interface_extruder need to be set to 0).")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable the Wipe Tower?")); - //wxMessageDialog dialog (nullptr, msg_text, _(L("Wipe Tower")), - MessageDialog dialog (nullptr, msg_text, _(L("Wipe Tower")), + MessageDialog dialog (m_msg_dlg_parent, msg_text, _(L("Wipe Tower")), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); @@ -147,8 +143,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con "need to be synchronized with the object layers.")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I synchronize support layers in order to enable the Wipe Tower?")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Wipe Tower")), - MessageDialog dialog(nullptr, msg_text, _(L("Wipe Tower")), + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Wipe Tower")), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); @@ -169,7 +164,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con "- Detect bridging perimeters")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I adjust those settings for supports?")); - MessageDialog dialog(nullptr, msg_text, _L("Support Generator"), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _L("Support Generator"), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); if (!is_global_config || answer == wxID_YES) { @@ -200,8 +195,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con _(fill_pattern_def->enum_labels[it_pattern - fill_pattern_def->enum_values.begin()])); if (is_global_config) msg_text += "\n\n" + _L("Shall I switch to rectilinear fill pattern?"); - //wxMessageDialog dialog(nullptr, msg_text, _L("Infill"), - MessageDialog dialog(nullptr, msg_text, _L("Infill"), + MessageDialog dialog(m_msg_dlg_parent, msg_text, _L("Infill"), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK) ); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); @@ -331,8 +325,7 @@ void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, con if (head_penetration > head_width) { wxString msg_text = _(L("Head penetration should not be greater than the head width.")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK); - MessageDialog dialog(nullptr, msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK); DynamicPrintConfig new_conf = *config; if (dialog.ShowModal() == wxID_OK) { new_conf.set_key_value("support_head_penetration", new ConfigOptionFloat(head_width)); @@ -345,8 +338,7 @@ void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, con if (pinhead_d > pillar_d) { wxString msg_text = _(L("Pinhead diameter should be smaller than the pillar diameter.")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK); - MessageDialog dialog(nullptr, msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK); DynamicPrintConfig new_conf = *config; if (dialog.ShowModal() == wxID_OK) { diff --git a/src/slic3r/GUI/ConfigManipulation.hpp b/src/slic3r/GUI/ConfigManipulation.hpp index 0e6815753e..32ddb52a91 100644 --- a/src/slic3r/GUI/ConfigManipulation.hpp +++ b/src/slic3r/GUI/ConfigManipulation.hpp @@ -30,15 +30,18 @@ class ConfigManipulation // callback to propagation of changed value, if needed std::function cb_value_change = nullptr; ModelConfig* local_config = nullptr; + wxWindow* m_msg_dlg_parent {nullptr}; public: ConfigManipulation(std::function load_config, std::function cb_toggle_field, std::function cb_value_change, - ModelConfig* local_config = nullptr) : + ModelConfig* local_config = nullptr, + wxWindow* msg_dlg_parent = nullptr) : load_config(load_config), cb_toggle_field(cb_toggle_field), cb_value_change(cb_value_change), + m_msg_dlg_parent(msg_dlg_parent), local_config(local_config) {} ConfigManipulation() {} diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 41794d2943..3172e22c3c 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4481,7 +4481,7 @@ ConfigManipulation Tab::get_config_manipulation() return on_value_change(opt_key, value); }; - return ConfigManipulation(load_config, cb_toggle_field, cb_value_change); + return ConfigManipulation(load_config, cb_toggle_field, cb_value_change, nullptr, this); } From a952fbc31aae9067fbc515cdfe9e548dde0b579e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 6 Dec 2021 11:44:15 +0100 Subject: [PATCH 06/31] Allow a "Dark mode" in Preferences for G-code viewer --- src/slic3r/GUI/Preferences.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 6820a42840..5bc7c981eb 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -465,7 +465,7 @@ void PreferencesDialog::build(size_t selected_tab) #ifdef _WIN32 // Add "Dark Mode" tab - if (is_editor) { + { // Add "Dark Mode" tab m_optgroup_dark_mode = create_options_tab(_L("Dark mode (experimental)"), tabs); m_optgroup_dark_mode->m_on_change = [this](t_config_option_key opt_key, boost::any value) { From 68d5a47121eaad42a390fe8d7b85cbff551cf90e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 6 Dec 2021 11:45:02 +0100 Subject: [PATCH 07/31] Caching of shader program attribute and uniform IDs from strings. --- src/slic3r/GUI/GLShader.cpp | 30 ++++++++++++++++++++++++++++-- src/slic3r/GUI/GLShader.hpp | 2 ++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index 0473e53445..9c1e936525 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -358,12 +358,38 @@ bool GLShaderProgram::set_uniform(const char* name, const Vec3d& value) const int GLShaderProgram::get_attrib_location(const char* name) const { - return (m_id > 0) ? ::glGetAttribLocation(m_id, name) : -1; + assert(m_id > 0); + + if (m_id <= 0) + // Shader program not loaded. This should not happen. + return -1; + + auto it = std::find_if(m_attrib_location_cache.begin(), m_attrib_location_cache.end(), [name](const auto& p) { return p.first == name; }); + if (it != m_attrib_location_cache.end()) + // Attrib ID cached. + return it->second; + + int id = ::glGetAttribLocation(m_id, name); + const_cast(this)->m_attrib_location_cache.push_back({ name, id }); + return id; } int GLShaderProgram::get_uniform_location(const char* name) const { - return (m_id > 0) ? ::glGetUniformLocation(m_id, name) : -1; + assert(m_id > 0); + + if (m_id <= 0) + // Shader program not loaded. This should not happen. + return -1; + + auto it = std::find_if(m_uniform_location_cache.begin(), m_uniform_location_cache.end(), [name](const auto &p) { return p.first == name; }); + if (it != m_uniform_location_cache.end()) + // Uniform ID cached. + return it->second; + + int id = ::glGetUniformLocation(m_id, name); + const_cast(this)->m_uniform_location_cache.push_back({ name, id }); + return id; } } // namespace Slic3r diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index c92ea274aa..d7b92000df 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -29,6 +29,8 @@ public: private: std::string m_name; unsigned int m_id{ 0 }; + std::vector> m_attrib_location_cache; + std::vector> m_uniform_location_cache; public: ~GLShaderProgram(); From c37090a64d41ec58e7a6add5cb8a0c55efbbde9c Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 6 Dec 2021 11:52:19 +0100 Subject: [PATCH 08/31] Optimization of G-code rendering, may improve speed issues mentioned in #7415 --- src/slic3r/GUI/GCodeViewer.cpp | 103 +++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 9fcef6a65b..347f781e76 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2636,12 +2636,7 @@ void GCodeViewer::render_toolpaths() float near_plane_height = camera.get_type() == Camera::EType::Perspective ? static_cast(viewport[3]) / (2.0f * static_cast(2.0 * std::tan(0.5 * Geometry::deg2rad(camera.get_fov())))) : static_cast(viewport[3]) * 0.0005; -#if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_points = [this, zoom, point_size, near_plane_height] -#else - auto render_as_points = [zoom, point_size, near_plane_height] -#endif // ENABLE_GCODE_VIEWER_STATISTICS - (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { + auto shader_init_as_points = [zoom, point_size, near_plane_height](GLShaderProgram& shader) { #if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS shader.set_uniform("use_fixed_screen_size", 1); #else @@ -2652,65 +2647,67 @@ void GCodeViewer::render_toolpaths() shader.set_uniform("percent_center_radius", 0.33f); shader.set_uniform("point_size", point_size); shader.set_uniform("near_plane_height", near_plane_height); + }; + auto render_as_points = [ +#if ENABLE_GCODE_VIEWER_STATISTICS + this +#endif // ENABLE_GCODE_VIEWER_STATISTICS + ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram& shader, int uniform_color) { glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); glsafe(::glEnable(GL_POINT_SPRITE)); - for (const RenderPath& path : buffer.render_paths) { - if (path.ibuffer_id == ibuffer_id) { - shader.set_uniform("uniform_color", path.color); - glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + for (auto it = it_path; it != it_end && (*it_path)->ibuffer_id == (*it)->ibuffer_id; ++ it) { + const RenderPath& path = **it; + glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); + glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_points_calls_count; + ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS - } } glsafe(::glDisable(GL_POINT_SPRITE)); glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); }; -#if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_lines = [this, light_intensity] -#else - auto render_as_lines = [light_intensity] -#endif // ENABLE_GCODE_VIEWER_STATISTICS - (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { + auto shader_init_as_lines = [light_intensity](GLShaderProgram &shader) { shader.set_uniform("light_intensity", light_intensity); - for (const RenderPath& path : buffer.render_paths) { - if (path.ibuffer_id == ibuffer_id) { - shader.set_uniform("uniform_color", path.color); - glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + }; + auto render_as_lines = [ #if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_lines_calls_count; + this +#endif // ENABLE_GCODE_VIEWER_STATISTICS + ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram &shader, int uniform_color) { + for (auto it = it_path; it != it_end && (*it_path)->ibuffer_id == (*it)->ibuffer_id; ++it) { + const RenderPath& path = **it; + glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); + glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_lines_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS - } } }; + auto render_as_triangles = [ #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_triangles = [this] -#else - auto render_as_triangles = [] + this #endif // ENABLE_GCODE_VIEWER_STATISTICS - (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { - for (const RenderPath& path : buffer.render_paths) { - if (path.ibuffer_id == ibuffer_id) { - shader.set_uniform("uniform_color", path.color); - glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); + ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram &shader, int uniform_color) { + for (auto it = it_path; it != it_end && (*it_path)->ibuffer_id == (*it)->ibuffer_id; ++it) { + const RenderPath& path = **it; + glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); + glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_triangles_calls_count; + ++m_statistics.gl_multi_triangles_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS - } } }; + auto render_as_instanced_model = [ #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_instanced_model = [this] -#else - auto render_as_instanced_model = [] + this #endif // ENABLE_GCODE_VIEWER_STATISTICS - (TBuffer& buffer, GLShaderProgram & shader) { + ](TBuffer& buffer, GLShaderProgram & shader) { for (auto& range : buffer.model.instances.render_ranges.ranges) { if (range.vbo == 0 && range.count > 0) { glsafe(::glGenBuffers(1, &range.vbo)); @@ -2815,8 +2812,27 @@ void GCodeViewer::render_toolpaths() shader->set_uniform("emission_factor", 0.0f); } else { - for (size_t j = 0; j < buffer.indices.size(); ++j) { - const IBuffer& i_buffer = buffer.indices[j]; + switch (buffer.render_primitive_type) { + case TBuffer::ERenderPrimitiveType::Point: shader_init_as_points(*shader); break; + case TBuffer::ERenderPrimitiveType::Line: shader_init_as_lines(*shader); break; + default: break; + } + int uniform_color = shader->get_uniform_location("uniform_color"); + // Render paths are sorted by ibuffer_id. + std::vector paths; + paths.reserve(buffer.render_paths.size()); + for (const RenderPath& path : buffer.render_paths) + paths.emplace_back(&path); + std::stable_sort(paths.begin(), paths.end(), [](const auto* l, const auto* r){ return l->ibuffer_id < r->ibuffer_id; }); + //FIXME maybe std::sort would suffice? + auto it_path = paths.begin(); + for (unsigned int ibuffer_id = 0; ibuffer_id < static_cast(buffer.indices.size()); ++ibuffer_id) { + const IBuffer& i_buffer = buffer.indices[ibuffer_id]; + // Skip all paths with ibuffer_id < ibuffer_id. + for (; it_path != paths.end() && (*it_path)->ibuffer_id < ibuffer_id; ++ it_path) ; + if (it_path == paths.end() || (*it_path)->ibuffer_id > ibuffer_id) + // Not found. This shall not happen. + continue; glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); @@ -2829,19 +2845,20 @@ void GCodeViewer::render_toolpaths() glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); + // Render all elements with it_path->ibuffer_id == ibuffer_id, possible with varying colors. switch (buffer.render_primitive_type) { case TBuffer::ERenderPrimitiveType::Point: { - render_as_points(buffer, static_cast(j), *shader); + render_as_points(it_path, paths.end(), *shader, uniform_color); break; } case TBuffer::ERenderPrimitiveType::Line: { glsafe(::glLineWidth(static_cast(line_width(zoom)))); - render_as_lines(buffer, static_cast(j), *shader); + render_as_lines(it_path, paths.end(), *shader, uniform_color); break; } case TBuffer::ERenderPrimitiveType::Triangle: { - render_as_triangles(buffer, static_cast(j), *shader); + render_as_triangles(it_path, paths.end(), *shader, uniform_color); break; } default: { break; } From 2ed57d1ba5091bade713e7924d9cd5d5b4f5b867 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 6 Dec 2021 13:21:34 +0100 Subject: [PATCH 09/31] Further optimization of G-code viewer: Replaced std::set with std::vector. --- src/slic3r/GUI/GCodeViewer.cpp | 29 +++++++++++++---------------- src/slic3r/GUI/GCodeViewer.hpp | 12 +----------- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 347f781e76..f2bd748822 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -977,8 +977,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const for (const RenderPath& path : t_buffer.render_paths) { colors.push_back(path.color); } - std::sort(colors.begin(), colors.end()); - colors.erase(std::unique(colors.begin(), colors.end()), colors.end()); + sort_remove_duplicates(colors); // save materials file boost::filesystem::path mat_filename(filename); @@ -2020,13 +2019,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) } // roles -> remove duplicates - std::sort(m_roles.begin(), m_roles.end()); - m_roles.erase(std::unique(m_roles.begin(), m_roles.end()), m_roles.end()); + sort_remove_duplicates(m_roles); m_roles.shrink_to_fit(); // extruder ids -> remove duplicates - std::sort(m_extruder_ids.begin(), m_extruder_ids.end()); - m_extruder_ids.erase(std::unique(m_extruder_ids.begin(), m_extruder_ids.end()), m_extruder_ids.end()); + sort_remove_duplicates(m_extruder_ids); m_extruder_ids.shrink_to_fit(); // set layers z range @@ -2374,8 +2371,10 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool } RenderPath key{ tbuffer_id, color, static_cast(ibuffer_id), path_id }; - if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key)) - render_path = const_cast(&(*buffer.render_paths.emplace(key).first)); + if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key)) { + buffer.render_paths.emplace_back(key); + render_path = const_cast(&buffer.render_paths.back()); + } unsigned int delta_1st = 0; if (sub_path.first.s_id < m_sequential_view.current.first && m_sequential_view.current.first <= sub_path.last.s_id) @@ -2433,16 +2432,14 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool #endif } - // removes empty render paths + // Removes empty render paths and sort. for (size_t b = 0; b < m_buffers.size(); ++b) { TBuffer* buffer = const_cast(&m_buffers[b]); - std::set::iterator it = buffer->render_paths.begin(); - while (it != buffer->render_paths.end()) { - if (it->sizes.empty() || it->offsets.empty()) - it = buffer->render_paths.erase(it); - else - ++it; - } + buffer->render_paths.erase(std::remove_if(buffer->render_paths.begin(), buffer->render_paths.end(), + [](const auto &path){ return path.sizes.empty() || path.offsets.empty(); }), + buffer->render_paths.end()); + //FIXME is this sorting needed at all? + std::sort(buffer->render_paths.begin(), buffer->render_paths.end(), RenderPathPropertyLower{}); } // second pass: for buffers using instanced and batched models, update the instances render ranges diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 9147eec84d..c56e88c880 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -259,14 +259,6 @@ class GCodeViewer return false; } }; -// // for unordered_set implementation of render_paths -// struct RenderPathPropertyHash { -// size_t operator() (const RenderPath &p) const { -// // Convert the RGB value to an integer hash. -//// return (size_t(int(p.color[0] * 255) + 255 * int(p.color[1] * 255) + (255 * 255) * int(p.color[2] * 255)) * 7919) ^ size_t(p.ibuffer_id); -// return size_t(int(p.color[0] * 255) + 255 * int(p.color[1] * 255) + (255 * 255) * int(p.color[2] * 255)) ^ size_t(p.ibuffer_id); -// } -// }; struct RenderPathPropertyLower { bool operator() (const RenderPath &l, const RenderPath &r) const { if (l.tbuffer_id < r.tbuffer_id) @@ -319,9 +311,7 @@ class GCodeViewer std::string shader; std::vector paths; - // std::set seems to perform significantly better, at least on Windows. -// std::unordered_set render_paths; - std::set render_paths; + std::vector render_paths; bool visible{ false }; void reset(); From d6bb8eead91a4b852e362e2cb4d67f6c3402fc87 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 6 Dec 2021 14:20:52 +0100 Subject: [PATCH 10/31] Fixed 3mf unit tests to pass on ARM 64bit --- tests/libslic3r/test_3mf.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index 0ebe47a07c..3602d6972d 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -112,7 +112,17 @@ SCENARIO("2D convex hull of sinking object", "[3mf]") { { -91501496, 4243 } }; - bool res = hull_2d.points == result; + // Allow 1um error due to floating point rounding. + bool res = hull_2d.points.size() == result.size(); + if (res) + for (size_t i = 0; i < result.size(); ++ i) { + const Point &p1 = result[i]; + const Point &p2 = hull_2d.points[i]; + if (std::abs(p1.x() - p2.x()) > 1 || std::abs(p1.y() - p2.y()) > 1) { + res = false; + break; + } + } THEN("2D convex hull should match with reference") { REQUIRE(res); From 00c86b2b7f2d572f002063c2ce3f19119dac3b6c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 6 Dec 2021 14:25:17 +0100 Subject: [PATCH 11/31] Follow-up of 2ed57d1ba5091bade713e7924d9cd5d5b4f5b867 - Removed unneeded sort --- src/slic3r/GUI/GCodeViewer.cpp | 39 +++++++++++++--------------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index f2bd748822..574ba6ec53 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2438,8 +2438,6 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool buffer->render_paths.erase(std::remove_if(buffer->render_paths.begin(), buffer->render_paths.end(), [](const auto &path){ return path.sizes.empty() || path.offsets.empty(); }), buffer->render_paths.end()); - //FIXME is this sorting needed at all? - std::sort(buffer->render_paths.begin(), buffer->render_paths.end(), RenderPathPropertyLower{}); } // second pass: for buffers using instanced and batched models, update the instances render ranges @@ -2650,12 +2648,12 @@ void GCodeViewer::render_toolpaths() #if ENABLE_GCODE_VIEWER_STATISTICS this #endif // ENABLE_GCODE_VIEWER_STATISTICS - ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram& shader, int uniform_color) { + ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram& shader, int uniform_color) { glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); glsafe(::glEnable(GL_POINT_SPRITE)); - for (auto it = it_path; it != it_end && (*it_path)->ibuffer_id == (*it)->ibuffer_id; ++ it) { - const RenderPath& path = **it; + for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) { + const RenderPath& path = *it; glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -2674,9 +2672,9 @@ void GCodeViewer::render_toolpaths() #if ENABLE_GCODE_VIEWER_STATISTICS this #endif // ENABLE_GCODE_VIEWER_STATISTICS - ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram &shader, int uniform_color) { - for (auto it = it_path; it != it_end && (*it_path)->ibuffer_id == (*it)->ibuffer_id; ++it) { - const RenderPath& path = **it; + ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram& shader, int uniform_color) { + for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) { + const RenderPath& path = *it; glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -2689,9 +2687,9 @@ void GCodeViewer::render_toolpaths() #if ENABLE_GCODE_VIEWER_STATISTICS this #endif // ENABLE_GCODE_VIEWER_STATISTICS - ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram &shader, int uniform_color) { - for (auto it = it_path; it != it_end && (*it_path)->ibuffer_id == (*it)->ibuffer_id; ++it) { - const RenderPath& path = **it; + ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram& shader, int uniform_color) { + for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) { + const RenderPath& path = *it; glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -2815,19 +2813,12 @@ void GCodeViewer::render_toolpaths() default: break; } int uniform_color = shader->get_uniform_location("uniform_color"); - // Render paths are sorted by ibuffer_id. - std::vector paths; - paths.reserve(buffer.render_paths.size()); - for (const RenderPath& path : buffer.render_paths) - paths.emplace_back(&path); - std::stable_sort(paths.begin(), paths.end(), [](const auto* l, const auto* r){ return l->ibuffer_id < r->ibuffer_id; }); - //FIXME maybe std::sort would suffice? - auto it_path = paths.begin(); + auto it_path = buffer.render_paths.begin(); for (unsigned int ibuffer_id = 0; ibuffer_id < static_cast(buffer.indices.size()); ++ibuffer_id) { const IBuffer& i_buffer = buffer.indices[ibuffer_id]; // Skip all paths with ibuffer_id < ibuffer_id. - for (; it_path != paths.end() && (*it_path)->ibuffer_id < ibuffer_id; ++ it_path) ; - if (it_path == paths.end() || (*it_path)->ibuffer_id > ibuffer_id) + for (; it_path != buffer.render_paths.end() && it_path->ibuffer_id < ibuffer_id; ++ it_path) ; + if (it_path == buffer.render_paths.end() || it_path->ibuffer_id > ibuffer_id) // Not found. This shall not happen. continue; @@ -2846,16 +2837,16 @@ void GCodeViewer::render_toolpaths() switch (buffer.render_primitive_type) { case TBuffer::ERenderPrimitiveType::Point: { - render_as_points(it_path, paths.end(), *shader, uniform_color); + render_as_points(it_path, buffer.render_paths.end(), *shader, uniform_color); break; } case TBuffer::ERenderPrimitiveType::Line: { glsafe(::glLineWidth(static_cast(line_width(zoom)))); - render_as_lines(it_path, paths.end(), *shader, uniform_color); + render_as_lines(it_path, buffer.render_paths.end(), *shader, uniform_color); break; } case TBuffer::ERenderPrimitiveType::Triangle: { - render_as_triangles(it_path, paths.end(), *shader, uniform_color); + render_as_triangles(it_path, buffer.render_paths.end(), *shader, uniform_color); break; } default: { break; } From 6490456ec401f05036289897f14dc82e27714ceb Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 6 Dec 2021 17:15:22 +0100 Subject: [PATCH 12/31] Do not perform config check if conf folder is redefined --- src/slic3r/GUI/GUI_App.cpp | 6 ++++++ src/slic3r/GUI/GUI_App.hpp | 1 + 2 files changed, 7 insertions(+) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index c7654fcd65..20a4505d73 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -883,6 +883,8 @@ void GUI_App::init_app_config() dir = wxFileName::GetHomeDir() + wxS("/.config"); set_data_dir((dir + "/" + GetAppName()).ToUTF8().data()); #endif + } else { + m_config_folder_redefined = true; } if (!app_config) @@ -915,6 +917,10 @@ void GUI_App::init_app_config() // returns true if found newer version and user agreed to use it bool GUI_App::check_older_app_config(Semver current_version, bool backup) { + // If the config folder is redefined - do not check + if (m_config_folder_redefined) + return false; + // find other version app config (alpha / beta / release) std::string config_path = app_config->config_path(); boost::filesystem::path parent_file_path(config_path); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 82feb32827..4cfc968168 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -352,6 +352,7 @@ private: void check_updates(const bool verbose); bool m_init_app_config_from_older { false }; + bool m_config_folder_redefined { false }; std::string m_older_data_dir_path; boost::optional m_last_config_version; }; From 091585076c5009b1173ee240cf0fef7ecba876dc Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 6 Dec 2021 17:24:54 +0100 Subject: [PATCH 13/31] Followup on 6490456ec401f05036289897f14dc82e27714ceb Rename variable --- src/slic3r/GUI/GUI_App.cpp | 4 ++-- src/slic3r/GUI/GUI_App.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 20a4505d73..bda7394ed6 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -884,7 +884,7 @@ void GUI_App::init_app_config() set_data_dir((dir + "/" + GetAppName()).ToUTF8().data()); #endif } else { - m_config_folder_redefined = true; + m_datadir_redefined = true; } if (!app_config) @@ -918,7 +918,7 @@ void GUI_App::init_app_config() bool GUI_App::check_older_app_config(Semver current_version, bool backup) { // If the config folder is redefined - do not check - if (m_config_folder_redefined) + if (m_datadir_redefined) return false; // find other version app config (alpha / beta / release) diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 4cfc968168..b1b0a77862 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -352,7 +352,7 @@ private: void check_updates(const bool verbose); bool m_init_app_config_from_older { false }; - bool m_config_folder_redefined { false }; + bool m_datadir_redefined { false }; std::string m_older_data_dir_path; boost::optional m_last_config_version; }; From d8ecc191da71f34b584681ee21f4ebae96016241 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 7 Dec 2021 09:17:45 +0100 Subject: [PATCH 14/31] Added a check that first layer height is not expressed as a percentage (related to https://github.com/prusa3d/PrusaSlicer/issues/7418) first_layer_height cannot be changed to ConfigOptionFloat, that would break loading of old 3MFs. The relative values from 3MFs should already be converted to absolute in `Preset::normalize`, what is missing is the UI check. + Code refactoring for OptionsGroup::create_single_option_line(): Don't clear label value in an Option. This value is used in Field::get_value_by_opt_type() to show error "%s doesn't support percentage". => At functions OG_CustomCtrl::CtrlLine::render() and OG_CustomCtrl::CtrlLine::get_pos() added check if current line has more than one option. => Draw option's label only when line has several options. --- src/libslic3r/PrintConfig.cpp | 1 + src/slic3r/GUI/Field.cpp | 10 ++++++++++ src/slic3r/GUI/OG_CustomCtrl.cpp | 6 ++++-- src/slic3r/GUI/OptionsGroup.cpp | 8 +++----- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 011539aa4e..11e741ec61 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1210,6 +1210,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("When printing with very low layer heights, you might still want to print a thicker " "bottom layer to improve adhesion and tolerance for non perfect build plates."); def->sidetext = L("mm"); + def->min = 0; def->ratio_over = "layer_height"; def->set_default_value(new ConfigOptionFloatOrPercent(0.35, false)); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 67f78d26ed..5ec622b872 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -291,6 +291,16 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true case coString: case coStrings: case coFloatOrPercent: { + if (m_opt.type == coFloatOrPercent && m_opt.opt_key == "first_layer_height" && !str.IsEmpty() && str.Last() == '%') { + // Workaroud to avoid of using of the % for first layer height + // see https://github.com/prusa3d/PrusaSlicer/issues/7418 + wxString label = m_opt.full_label.empty() ? _(m_opt.label) : _(m_opt.full_label); + show_error(m_parent, from_u8((boost::format(_utf8(L("%s doesn't support percentage"))) % label).str())); + const wxString stVal = double_to_string(0.01, 2); + set_value(stVal, true); + m_value = into_u8(stVal);; + break; + } if (m_opt.type == coFloatOrPercent && !str.IsEmpty() && str.Last() != '%') { double val = 0.; diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index e9153c70f4..4516e49933 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -167,13 +167,14 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/) break; } + bool is_multioption_line = option_set.size() > 1; for (auto opt : option_set) { Field* field = opt_group->get_field(opt.opt_id); correct_line_height(ctrl_line.height, field->getWindow()); ConfigOptionDef option = opt.opt; // add label if any - if (!option.label.empty()) { + if (is_multioption_line && !option.label.empty()) { //! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1 label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? _CTX(option.label, "Layers") : _(option.label); @@ -595,11 +596,12 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord v_pos) } size_t bmp_rect_id = 0; + bool is_multioption_line = option_set.size() > 1; for (const Option& opt : option_set) { field = ctrl->opt_group->get_field(opt.opt_id); ConfigOptionDef option = opt.opt; // add label if any - if (!option.label.empty()) { + if (is_multioption_line && !option.label.empty()) { //! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1 label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? _CTX(option.label, "Layers") : _(option.label); diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 23c9051be2..1b6e419596 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -507,15 +507,13 @@ void OptionsGroup::clear(bool destroy_custom_ctrl) m_fields.clear(); } -Line OptionsGroup::create_single_option_line(const Option& option, const std::string& path/* = std::string()*/) const { -// Line retval{ _(option.opt.label), _(option.opt.tooltip) }; +Line OptionsGroup::create_single_option_line(const Option& option, const std::string& path/* = std::string()*/) const +{ wxString tooltip = _(option.opt.tooltip); edit_tooltip(tooltip); Line retval{ _(option.opt.label), tooltip }; retval.label_path = path; - Option tmp(option); - tmp.opt.label = std::string(""); - retval.append_option(tmp); + retval.append_option(option); return retval; } From 940690ecdbe37c1a7898e78f449fbb0399dae60c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 7 Dec 2021 10:55:27 +0100 Subject: [PATCH 15/31] Follow-up https://github.com/prusa3d/PrusaSlicer/commit/d8ecc191da71f34b584681ee21f4ebae96016241 - Remove from code a condition which has opposite effect effect now. + OptionsGroup::activate_line():Add same as at OG_CustomCtrl::CtrlLine::render() condition for the drawing of a option label. It means that we draw option labels only, if line has more than one option. --- src/slic3r/GUI/OG_CustomCtrl.cpp | 3 --- src/slic3r/GUI/OptionsGroup.cpp | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index 4516e49933..ee19e5da54 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -79,7 +79,6 @@ void OG_CustomCtrl::init_ctrl_lines() // if we have a single option with no label, no sidetext just add it directly to sizer if (option_set.size() == 1 && opt_group->label_width == 0 && option_set.front().opt.full_width && - option_set.front().opt.label.empty() && option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { @@ -157,7 +156,6 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/) // If we have a single option with no sidetext const std::vector