From 10d530a57e5db078a38722daa16d85eb4f21a0bb Mon Sep 17 00:00:00 2001 From: YuSanka <yusanka@gmail.com> Date: Mon, 4 May 2020 22:30:38 +0200 Subject: [PATCH 01/13] New Layouts: There 3 mode of a layout of the settings tabpanel : 1. Old mode, as it was always. 2. New mode, when Settings Tabs are hidden on the Plater. Use "cog" icon for the switching to the settings tabs 3. Dlg mode, when Settings Tabs are extracted to the Settings dialog. Use "cog" icon for Show or Focus the Settings Dialog The "Collapse sidebar" toolbar appearance is set in the Preferences. --- src/slic3r/GUI/GLCanvas3D.cpp | 71 ++++------------ src/slic3r/GUI/GLToolbar.cpp | 2 +- src/slic3r/GUI/GUI_App.cpp | 5 +- src/slic3r/GUI/MainFrame.cpp | 143 ++++++++++++++++++++++++-------- src/slic3r/GUI/MainFrame.hpp | 30 ++++++- src/slic3r/GUI/OptionsGroup.cpp | 1 + src/slic3r/GUI/Plater.cpp | 9 +- src/slic3r/GUI/Preferences.cpp | 80 ++++++++++++++++-- src/slic3r/GUI/Preferences.hpp | 1 + 9 files changed, 241 insertions(+), 101 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e78525d21..cd8ab6694 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4362,6 +4362,9 @@ void GLCanvas3D::update_ui_from_settings() _refresh_if_shown_on_screen(); } #endif + + bool enable_collapse = wxGetApp().app_config->get("show_collapse_button") == "1"; + enable_collapse_toolbar(enable_collapse); } @@ -5062,10 +5065,23 @@ bool GLCanvas3D::_init_main_toolbar() if (!m_main_toolbar.add_separator()) return false; + item.name = "settings"; + item.icon_filename = "cog.svg"; + item.tooltip = _utf8(L("Switch to Settings")); + item.sprite_id = 10; + item.enabling_callback = GLToolbarItem::Default_Enabling_Callback; + item.visibility_callback = [this]() { return wxGetApp().app_config->get("old_settings_layout_mode") != "1"; }; + item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(); }; + if (!m_main_toolbar.add_item(item)) + return false; + + if (!m_main_toolbar.add_separator()) + return false; + item.name = "layersediting"; item.icon_filename = "layers_white.svg"; item.tooltip = _utf8(L("Variable layer height")); - item.sprite_id = 10; + item.sprite_id = /*10*/11; item.left.toggable = true; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; item.visibility_callback = [this]()->bool @@ -5087,7 +5103,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "search"; item.icon_filename = "search_.svg"; item.tooltip = _utf8(L("Search")) + " [" + GUI::shortkey_ctrl_prefix() + "F]"; - item.sprite_id = 11; + item.sprite_id = /*11*/12; item.left.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) { @@ -5264,61 +5280,10 @@ bool GLCanvas3D::_init_collapse_toolbar() wxGetApp().plater()->collapse_sidebar(!wxGetApp().plater()->is_sidebar_collapsed()); }; - if (!m_collapse_toolbar.add_item(item)) - return false; - - if (!m_collapse_toolbar.add_separator()) - return false; - - item.name = "print"; - item.icon_filename = "cog.svg"; - item.tooltip = _utf8(L("Switch to Print Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "2]"; - item.sprite_id = 1; - item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*0*/1); }; - - if (!m_collapse_toolbar.add_item(item)) - return false; - - item.name = "filament"; - item.icon_filename = "spool.svg"; - item.tooltip = _utf8(L("Switch to Filament Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]"; - item.sprite_id = 2; - item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*1*/2); }; - item.visibility_callback = [this]() { return wxGetApp().plater()->printer_technology() == ptFFF; }; - - if (!m_collapse_toolbar.add_item(item)) - return false; - - item.name = "printer"; - item.icon_filename = "printer.svg"; - item.tooltip = _utf8(L("Switch to Printer Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "4]"; - item.sprite_id = 3; - item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*2*/3); }; - - if (!m_collapse_toolbar.add_item(item)) - return false; - - item.name = "resin"; - item.icon_filename = "resin.svg"; - item.tooltip = _utf8(L("Switch to SLA Material Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]"; - item.sprite_id = 4; - item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*1*/2); }; - item.visibility_callback = [this]() { return m_process->current_printer_technology() == ptSLA; }; - - if (!m_collapse_toolbar.add_item(item)) - return false; - - item.name = "sla_printer"; - item.icon_filename = "sla_printer.svg"; - item.tooltip = _utf8(L("Switch to Printer Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "4]"; - item.sprite_id = 5; - item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*2*/3); }; - if (!m_collapse_toolbar.add_item(item)) return false; return true; - } bool GLCanvas3D::_set_current() diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 0bd087814..88ed1d476 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -252,7 +252,7 @@ bool GLToolbar::is_enabled() const void GLToolbar::set_enabled(bool enable) { - m_enabled = true; + m_enabled = enable;//true; etFIXME } bool GLToolbar::add_item(const GLToolbarItem::Data& data) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 0774f3208..6790c4c8a 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -413,7 +413,8 @@ bool GUI_App::on_init_inner() if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) wxImage::AddHandler(new wxPNGHandler()); mainframe = new MainFrame(); - mainframe->switch_to(true); // hide settings tabs after first Layout + // hide settings tabs after first Layout + mainframe->select_tab(0); sidebar().obj_list()->init_objects(); // propagate model objects to object list // update_mode(); // !!! do that later @@ -602,6 +603,8 @@ void GUI_App::recreate_GUI() MainFrame *old_main_frame = mainframe; mainframe = new MainFrame(); + // hide settings tabs after first Layout + mainframe->select_tab(0); // Propagate model objects to object list. sidebar().obj_list()->init_objects(); SetTopWindow(mainframe); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index d3bd2720e..5d72dcc8b 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -90,10 +90,12 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S // initialize layout auto sizer = new wxBoxSizer(wxVERTICAL); - if (m_plater) + if (m_plater && m_layout != slOld) sizer->Add(m_plater, 1, wxEXPAND); - if (m_tabpanel) + + if (m_tabpanel && m_layout != slDlg) sizer->Add(m_tabpanel, 1, wxEXPAND); + sizer->SetSizeHints(this); SetSizer(sizer); Fit(); @@ -289,12 +291,22 @@ void MainFrame::update_title() void MainFrame::init_tabpanel() { - // wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10 - // with multiple high resolution displays connected. - m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME); + m_layout = wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? slOld : + wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? slNew : + wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? slDlg : slOld; + + if (m_layout == slDlg) { + m_settings_dialog = new SettingsDialog(); + m_tabpanel = m_settings_dialog->get_tabpanel(); + } + else { + // wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10 + // with multiple high resolution displays connected. + m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME); #ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList - m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font()); + m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font()); #endif + } m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxEvent&) { auto panel = m_tabpanel->GetCurrentPage(); @@ -307,17 +319,22 @@ void MainFrame::init_tabpanel() // On GTK, the wxEVT_NOTEBOOK_PAGE_CHANGED event is triggered // before the MainFrame is fully set up. static_cast<Tab*>(panel)->OnActivate(); + m_last_selected_tab = m_tabpanel->GetSelection(); } else select_tab(0); }); -//! m_plater = new Slic3r::GUI::Plater(m_tabpanel, this); - m_plater = new Plater(this, this); - + if (m_layout == slOld) { + m_plater = new Plater(m_tabpanel, this); + m_tabpanel->AddPage(m_plater, _L("Plater")); + } + else { + m_plater = new Plater(this, this); + if (m_layout == slNew) + m_tabpanel->AddPage(new wxPanel(m_tabpanel), _L("Plater")); // empty panel just for Plater tab + } wxGetApp().plater_ = m_plater; -// m_tabpanel->AddPage(m_plater, _(L("Plater"))); - m_tabpanel->AddPage(new wxPanel(m_tabpanel), _L("Plater")); // empty panel just for Plater tab wxGetApp().obj_list()->create_popup_menus(); @@ -343,13 +360,6 @@ void MainFrame::init_tabpanel() } } -void MainFrame::switch_to(bool plater) -{ - this->m_plater->Show(plater); - this->m_tabpanel->Show(!plater); - this->Layout(); -} - void MainFrame::create_preset_tabs() { wxGetApp().update_label_colours_from_appconfig(); @@ -763,25 +773,21 @@ void MainFrame::init_menubar() // Window menu auto windowMenu = new wxMenu(); { -//! size_t tab_offset = 0; if (m_plater) { append_menu_item(windowMenu, wxID_HIGHEST + 1, _(L("&Plater Tab")) + "\tCtrl+1", _(L("Show the plater")), - [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*(size_t)(-1)*/0); }, "plater", nullptr, + [this](wxCommandEvent&) { select_tab(0); }, "plater", nullptr, [this]() {return true; }, this); -//! tab_offset += 1; -//! } -//! if (tab_offset > 0) { windowMenu->AppendSeparator(); } append_menu_item(windowMenu, wxID_HIGHEST + 2, _(L("P&rint Settings Tab")) + "\tCtrl+2", _(L("Show the print settings")), - [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + 0*/1); }, "cog", nullptr, + [this/*, tab_offset*/](wxCommandEvent&) { select_tab(1); }, "cog", nullptr, [this]() {return true; }, this); wxMenuItem* item_material_tab = append_menu_item(windowMenu, wxID_HIGHEST + 3, _(L("&Filament Settings Tab")) + "\tCtrl+3", _(L("Show the filament settings")), - [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + 1*/2); }, "spool", nullptr, + [this/*, tab_offset*/](wxCommandEvent&) { select_tab(2); }, "spool", nullptr, [this]() {return true; }, this); m_changeable_menu_items.push_back(item_material_tab); wxMenuItem* item_printer_tab = append_menu_item(windowMenu, wxID_HIGHEST + 4, _(L("Print&er Settings Tab")) + "\tCtrl+4", _(L("Show the printer settings")), - [this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + 2*/3); }, "printer", nullptr, + [this/*, tab_offset*/](wxCommandEvent&) { select_tab(3); }, "printer", nullptr, [this]() {return true; }, this); m_changeable_menu_items.push_back(item_printer_tab); if (m_plater) { @@ -1245,17 +1251,25 @@ void MainFrame::load_config(const DynamicPrintConfig& config) #endif } -void MainFrame::select_tab(size_t tab) +void MainFrame::select_tab(size_t tab/* = size_t(-1)*/) { - if (tab == /*(size_t)(-1)*/0) { - if (m_plater && !m_plater->IsShown()) - this->switch_to(true); + if (m_layout == slDlg) { + if (tab==0) + return; + // Show/Activate Settings Dialog + if (m_settings_dialog->IsShown()) + m_settings_dialog->SetFocus(); + else + m_settings_dialog->Show(); } - else { - if (m_plater && m_plater->IsShown()) - switch_to(false); - m_tabpanel->SetSelection(tab); + else if (m_layout == slNew) { + m_plater->Show(tab == 0); + m_tabpanel->Show(tab != 0); + Layout(); } + + // when tab == -1, it means we should to show the last selected tab + m_tabpanel->SetSelection(tab == (size_t)(-1) ? m_last_selected_tab : (m_layout == slDlg && tab != 0) ? tab-1 : tab); } // Set a camera direction, zoom to all objects. @@ -1360,5 +1374,66 @@ std::string MainFrame::get_dir_name(const wxString &full_name) const return boost::filesystem::path(full_name.wx_str()).parent_path().string(); } + +// ---------------------------------------------------------------------------- +// SettingsDialog +// ---------------------------------------------------------------------------- + +SettingsDialog::SettingsDialog() +: DPIDialog(nullptr, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings")) +{ + this->SetFont(wxGetApp().normal_font()); + + wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + this->SetBackgroundColour(bgr_clr); + + // Load the icon either from the exe, or from the ico file. +#if _WIN32 + { + TCHAR szExeFileName[MAX_PATH]; + GetModuleFileName(nullptr, szExeFileName, MAX_PATH); + SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO)); + } +#else + SetIcon(wxIcon(var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG)); +#endif // _WIN32 + + // wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10 + // with multiple high resolution displays connected. + m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME); +#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList + m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font()); +#endif + + // initialize layout + auto sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(m_tabpanel, 1, wxEXPAND); + sizer->SetSizeHints(this); + SetSizer(sizer); + Fit(); + + const wxSize min_size = wxSize(85 * em_unit(), 50 * em_unit()); +#ifdef __APPLE__ + // Using SetMinSize() on Mac messes up the window position in some cases + // cf. https://groups.google.com/forum/#!topic/wx-users/yUKPBBfXWO0 + SetSize(min_size); +#else + SetMinSize(min_size); + SetSize(GetMinSize()); +#endif + Layout(); +} + +void SettingsDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + const int& em = em_unit(); + const wxSize& size = wxSize(85 * em, 50 * em); + + SetMinSize(size); + Fit(); + Refresh(); +} + + } // GUI } // Slic3r diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 8fc0ed1f2..536191700 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -43,6 +43,22 @@ struct PresetTab { PrinterTechnology technology; }; +// ---------------------------------------------------------------------------- +// SettingsDialog +// ---------------------------------------------------------------------------- + +class SettingsDialog : public DPIDialog +{ + wxNotebook* m_tabpanel { nullptr }; +public: + SettingsDialog(); + ~SettingsDialog() {} + wxNotebook* get_tabpanel() { return m_tabpanel; } + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; +}; + class MainFrame : public DPIFrame { bool m_loaded {false}; @@ -57,6 +73,8 @@ class MainFrame : public DPIFrame PrintHostQueueDialog *m_printhost_queue_dlg; + size_t m_last_selected_tab {1}; + std::string get_base_name(const wxString &full_name, const char *extension = nullptr) const; std::string get_dir_name(const wxString &full_name) const; @@ -94,6 +112,12 @@ class MainFrame : public DPIFrame wxFileHistory m_recent_projects; + enum SettingsLayout { + slOld = 0, + slNew, + slDlg, + } m_layout; + protected: virtual void on_dpi_changed(const wxRect &suggested_rect); @@ -109,7 +133,6 @@ public: void update_title(); void init_tabpanel(); - void switch_to(bool plater); void create_preset_tabs(); void add_created_tab(Tab* panel); void init_menubar(); @@ -130,7 +153,9 @@ public: void export_configbundle(); void load_configbundle(wxString file = wxEmptyString); void load_config(const DynamicPrintConfig& config); - void select_tab(size_t tab); + // Select tab in m_tabpanel + // When tab == -1, will be selected last selected tab + void select_tab(size_t tab = size_t(-1)); void select_view(const std::string& direction); // Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig void on_config_changed(DynamicPrintConfig* cfg) const ; @@ -141,6 +166,7 @@ public: Plater* m_plater { nullptr }; wxNotebook* m_tabpanel { nullptr }; + SettingsDialog* m_settings_dialog { nullptr }; wxProgressDialog* m_progress_dialog { nullptr }; std::shared_ptr<ProgressStatusBar> m_statusbar; diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 83eb6c76f..5b2b5703b 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -105,6 +105,7 @@ void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& fiel if (!m_show_modified_btns) { field->m_Undo_btn->set_as_hidden(); field->m_Undo_to_sys_btn->set_as_hidden(); + field->m_blinking_bmp->Hide(); return; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 02baaa17e..a6430e472 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -355,10 +355,10 @@ PresetBitmapComboBox(parent, wxSize(15 * wxGetApp().em_unit(), -1)), if (page_id == wxNOT_FOUND) return; - wxGetApp().tab_panel()->ChangeSelection(page_id); + wxGetApp().tab_panel()->SetSelection(page_id); // Switch to Settings NotePad - wxGetApp().mainframe->switch_to(false); + wxGetApp().mainframe->select_tab(); /* In a case of a multi-material printing, for editing another Filament Preset * it's needed to select this preset for the "Filament settings" Tab @@ -1100,9 +1100,8 @@ void Sidebar::jump_to_option(size_t selected) const Search::Option& opt = p->searcher.get_option(selected); wxGetApp().get_tab(opt.type)->activate_option(opt.opt_key, opt.category); - // Switch to the Settings NotePad, if plater is shown - if (p->plater->IsShown()) - wxGetApp().mainframe->switch_to(false); + // Switch to the Settings NotePad + wxGetApp().mainframe->select_tab(); } ObjectManipulation* Sidebar::obj_manipul() diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index e2bccb1c7..5963a1637 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -117,6 +117,13 @@ void PreferencesDialog::build() m_optgroup_general->append_single_option_line(option); #endif + def.label = L("Show the button for the collapse sidebar"); + def.type = coBool; + def.tooltip = L("If enabled, the button for the collapse sidebar will be appeared in top right corner of the 3D Scene"); + def.set_default_value(new ConfigOptionBool{ app_config->get("show_collapse_button") == "1" }); + option = Option(def, "show_collapse_button"); + m_optgroup_general->append_single_option_line(option); + m_optgroup_camera = std::make_shared<ConfigOptionsGroup>(this, _(L("Camera"))); m_optgroup_camera->label_width = 40; m_optgroup_camera->m_on_change = [this](t_config_option_key opt_key, boost::any value) { @@ -157,6 +164,8 @@ void PreferencesDialog::build() create_icon_size_slider(); m_icon_size_sizer->ShowItems(app_config->get("use_custom_toolbar_size") == "1"); + create_settings_mode_widget(); + auto sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(m_optgroup_general->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); sizer->Add(m_optgroup_camera->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); @@ -167,7 +176,7 @@ void PreferencesDialog::build() auto buttons = CreateStdDialogButtonSizer(wxOK | wxCANCEL); wxButton* btn = static_cast<wxButton*>(FindWindowById(wxID_OK, this)); btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); }); - sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); + sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 5); SetSizer(sizer); sizer->SetSizeHints(this); @@ -179,8 +188,33 @@ void PreferencesDialog::accept() warning_catcher(this, wxString::Format(_(L("You need to restart %s to make the changes effective.")), SLIC3R_APP_NAME)); } - auto app_config = get_app_config(); - for (std::map<std::string, std::string>::iterator it = m_values.begin(); it != m_values.end(); ++it) { + bool settings_layout_changed = m_values.find("old_settings_layout_mode") != m_values.end() || + m_values.find("new_settings_layout_mode") != m_values.end() || + m_values.find("dlg_settings_layout_mode") != m_values.end(); + + bool canceled = false; + if (settings_layout_changed) { + // the dialog needs to be destroyed before the call to recreate_gui() + // or sometimes the application crashes into wxDialogBase() destructor + // so we put it into an inner scope + wxMessageDialog dialog(nullptr, + _L("Switching the settings layout mode will trigger application restart.\n" + "You will lose content of the plater.") + "\n\n" + + _L("Do you want to proceed?"), + wxString(SLIC3R_APP_NAME) + " - " + _L("Switching the settings layout mode"), + wxICON_QUESTION | wxOK | wxCANCEL); + + if (dialog.ShowModal() == wxID_CANCEL) + canceled = true; + } + + auto app_config = get_app_config(); + for (std::map<std::string, std::string>::iterator it = m_values.begin(); it != m_values.end(); ++it) + { + if (canceled && (it->first == "old_settings_layout_mode" || + it->first == "new_settings_layout_mode" || + it->first == "dlg_settings_layout_mode" )) + continue; app_config->set(it->first, it->second); } @@ -188,8 +222,12 @@ void PreferencesDialog::accept() EndModal(wxID_OK); - // Nothify the UI to update itself from the ini file. - wxGetApp().update_ui_from_settings(); + if (settings_layout_changed && !canceled) + // recreate application, if settings layout was changed + wxGetApp().recreate_GUI(); + else + // Nothify the UI to update itself from the ini file. + wxGetApp().update_ui_from_settings(); } void PreferencesDialog::on_dpi_changed(const wxRect &suggested_rect) @@ -272,6 +310,38 @@ void PreferencesDialog::create_icon_size_slider() m_optgroup_gui->sizer->Add(m_icon_size_sizer, 0, wxEXPAND | wxALL, em); } +void PreferencesDialog::create_settings_mode_widget() +{ + wxString choices[] = { _L("Old regular layout with tab bar"), + _L("New layout without the tab bar on the platter"), + _L("Settings will be shown in non-modal dialog") }; + + auto app_config = get_app_config(); + int selection = app_config->get("old_settings_layout_mode") == "1" ? 0 : + app_config->get("new_settings_layout_mode") == "1" ? 1 : + app_config->get("dlg_settings_layout_mode") == "1" ? 2 : 0; + + wxWindow* parent = m_optgroup_gui->ctrl_parent(); + + wxRadioBox* box = new wxRadioBox(parent, wxID_ANY, _L("Settings layout mode"), wxDefaultPosition, wxDefaultSize, WXSIZEOF(choices), choices, + 3, wxRA_SPECIFY_ROWS); + box->SetButtonFont(wxGetApp().normal_font()); + box->SetSelection(selection); + + box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) { + int selection = e.GetSelection(); + + m_values["old_settings_layout_mode"] = boost::any_cast<bool>(selection == 0) ? "1" : "0"; + m_values["new_settings_layout_mode"] = boost::any_cast<bool>(selection == 1) ? "1" : "0"; + m_values["dlg_settings_layout_mode"] = boost::any_cast<bool>(selection == 2) ? "1" : "0"; + }); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(box, 1, wxALIGN_CENTER_VERTICAL); + + m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND | wxALL, em_unit()); +} + } // GUI } // Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 35bf2b8c5..7a6de8377 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -31,6 +31,7 @@ protected: void on_dpi_changed(const wxRect &suggested_rect) override; void layout(); void create_icon_size_slider(); + void create_settings_mode_widget(); }; } // GUI From 43be7a51aa4823d9a32c4bdeb834b0379762ad71 Mon Sep 17 00:00:00 2001 From: YuSanka <yusanka@gmail.com> Date: Tue, 5 May 2020 09:35:28 +0200 Subject: [PATCH 02/13] Fixed OSX/Linux build Destroy Settings Dialog when close application --- src/slic3r/GUI/MainFrame.cpp | 6 ++++++ src/slic3r/GUI/MainFrame.hpp | 2 +- src/slic3r/GUI/Preferences.cpp | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 5d72dcc8b..417afdfbd 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -231,6 +231,9 @@ void MainFrame::shutdown() // In addition, there were some crashes due to the Paint events sent to already destructed windows. this->Show(false); + if (m_settings_dialog) + m_settings_dialog->Destroy(); + // Stop the background thread (Windows and Linux). // Disconnect from a 3DConnextion driver (OSX). m_plater->get_mouse3d_controller().shutdown(); @@ -295,6 +298,9 @@ void MainFrame::init_tabpanel() wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? slNew : wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? slDlg : slOld; + // From the very beginning the Print settings should be selected + m_last_selected_tab = m_layout == slDlg ? 0 : 1; + if (m_layout == slDlg) { m_settings_dialog = new SettingsDialog(); m_tabpanel = m_settings_dialog->get_tabpanel(); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 536191700..5c8bd2f86 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -73,7 +73,7 @@ class MainFrame : public DPIFrame PrintHostQueueDialog *m_printhost_queue_dlg; - size_t m_last_selected_tab {1}; + size_t m_last_selected_tab; std::string get_base_name(const wxString &full_name, const char *extension = nullptr) const; std::string get_dir_name(const wxString &full_name) const; diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 5963a1637..f8fa1a233 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -325,7 +325,7 @@ void PreferencesDialog::create_settings_mode_widget() wxRadioBox* box = new wxRadioBox(parent, wxID_ANY, _L("Settings layout mode"), wxDefaultPosition, wxDefaultSize, WXSIZEOF(choices), choices, 3, wxRA_SPECIFY_ROWS); - box->SetButtonFont(wxGetApp().normal_font()); + box->SetFont(wxGetApp().normal_font()); box->SetSelection(selection); box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) { @@ -339,7 +339,7 @@ void PreferencesDialog::create_settings_mode_widget() auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(box, 1, wxALIGN_CENTER_VERTICAL); - m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND | wxALL, em_unit()); + m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND); } From 181203c2c7d2171bba7fccabb1709d88a8ad874e Mon Sep 17 00:00:00 2001 From: YuSanka <yusanka@gmail.com> Date: Tue, 5 May 2020 17:23:58 +0200 Subject: [PATCH 03/13] New layout: 1. PreferencesDialog: When "Cancel" is selected, don't close the PreferencesDialog, just revert layout mode selection 2. For the tooltip of "Switch to Settings" added information about shortcuts 3. Shortcuts for the switching between tabs work from Settings Dialog now 4. Fixed the collapse button show after preference's change --- src/slic3r/GUI/GLCanvas3D.cpp | 16 ++++++++++++++-- src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/MainFrame.cpp | 24 ++++++++++++++++++++---- src/slic3r/GUI/MainFrame.hpp | 3 ++- src/slic3r/GUI/Plater.cpp | 16 +++++++++++++++- src/slic3r/GUI/Preferences.cpp | 32 ++++++++++++++++---------------- src/slic3r/GUI/Preferences.hpp | 1 + 7 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index cd8ab6694..f94e8f147 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4422,6 +4422,16 @@ void GLCanvas3D::msw_rescale() m_warning_texture.msw_rescale(*this); } +void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar() +{ + std::string new_tooltip = _u8L("Switch to Settings") + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (m_process->current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ; + + m_main_toolbar.set_tooltip(get_main_toolbar_item_id("settings"), new_tooltip); +} + bool GLCanvas3D::has_toolpaths_to_export() const { return m_volumes.has_toolpaths_to_export(); @@ -5067,7 +5077,9 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "settings"; item.icon_filename = "cog.svg"; - item.tooltip = _utf8(L("Switch to Settings")); + item.tooltip = _u8L("Switch to Settings") + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (m_process->current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) + + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ; item.sprite_id = 10; item.enabling_callback = GLToolbarItem::Default_Enabling_Callback; item.visibility_callback = [this]() { return wxGetApp().app_config->get("old_settings_layout_mode") != "1"; }; @@ -5239,7 +5251,7 @@ bool GLCanvas3D::_init_view_toolbar() bool GLCanvas3D::_init_collapse_toolbar() { - if (!m_collapse_toolbar.is_enabled()) + if (!m_collapse_toolbar.is_enabled() && m_collapse_toolbar.get_items_count() > 0) return true; BackgroundTexture::Metadata background_data; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 15aa8c456..fa1c070a2 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -724,6 +724,7 @@ public: int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); } void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); } void force_main_toolbar_right_action(int item_id) { m_main_toolbar.force_right_action(item_id, *this); } + void update_tooltip_for_settings_item_in_main_toolbar(); bool has_toolpaths_to_export() const; void export_toolpaths_to_obj(const char* filename) const; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 417afdfbd..e603ce341 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -302,7 +302,7 @@ void MainFrame::init_tabpanel() m_last_selected_tab = m_layout == slDlg ? 0 : 1; if (m_layout == slDlg) { - m_settings_dialog = new SettingsDialog(); + m_settings_dialog = new SettingsDialog(this); m_tabpanel = m_settings_dialog->get_tabpanel(); } else { @@ -1260,8 +1260,11 @@ void MainFrame::load_config(const DynamicPrintConfig& config) void MainFrame::select_tab(size_t tab/* = size_t(-1)*/) { if (m_layout == slDlg) { - if (tab==0) + if (tab==0) { + if (m_settings_dialog->IsShown()) + this->SetFocus(); return; + } // Show/Activate Settings Dialog if (m_settings_dialog->IsShown()) m_settings_dialog->SetFocus(); @@ -1385,8 +1388,9 @@ std::string MainFrame::get_dir_name(const wxString &full_name) const // SettingsDialog // ---------------------------------------------------------------------------- -SettingsDialog::SettingsDialog() -: DPIDialog(nullptr, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings")) +SettingsDialog::SettingsDialog(MainFrame* mainframe) +: DPIDialog(nullptr, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings")), + m_main_frame(mainframe) { this->SetFont(wxGetApp().normal_font()); @@ -1411,6 +1415,18 @@ SettingsDialog::SettingsDialog() m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font()); #endif + m_tabpanel->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& evt) { + if ((evt.GetModifiers() & wxMOD_CONTROL) != 0) { + switch (evt.GetKeyCode()) { + case '1': { m_main_frame->select_tab(0); break; } + case '2': { m_main_frame->select_tab(1); break; } + case '3': { m_main_frame->select_tab(2); break; } + case '4': { m_main_frame->select_tab(3); break; } + default:break; + } + } + }); + // initialize layout auto sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(m_tabpanel, 1, wxEXPAND); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 5c8bd2f86..219f68319 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -50,8 +50,9 @@ struct PresetTab { class SettingsDialog : public DPIDialog { wxNotebook* m_tabpanel { nullptr }; + MainFrame* m_main_frame {nullptr }; public: - SettingsDialog(); + SettingsDialog(MainFrame* mainframe); ~SettingsDialog() {} wxNotebook* get_tabpanel() { return m_tabpanel; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f95e12db0..66afbfa99 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1640,6 +1640,7 @@ struct Plater::priv void reset_all_gizmos(); void update_ui_from_settings(); + void update_main_toolbar_tooltips(); std::shared_ptr<ProgressStatusBar> statusbar(); std::string get_config(const std::string &key) const; BoundingBoxf bed_shape_bb() const; @@ -2041,7 +2042,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // collapse sidebar according to saved value - sidebar->collapse(wxGetApp().app_config->get("collapsed_sidebar") == "1"); + bool is_collapsed = wxGetApp().app_config->get("collapsed_sidebar") == "1"; + sidebar->collapse(is_collapsed); + // Update an enable of the collapse_toolbar: if sidebar is collapsed, then collapse_toolbar should be visible + if (is_collapsed) + wxGetApp().app_config->set("show_collapse_button", "1"); } Plater::priv::~priv() @@ -2116,6 +2121,13 @@ void Plater::priv::update_ui_from_settings() preview->get_canvas3d()->update_ui_from_settings(); } +// Called after the print technology was changed. +// Update the tooltips for "Switch to Settings" button in maintoolbar +void Plater::priv::update_main_toolbar_tooltips() +{ + view3D->get_canvas3d()->update_tooltip_for_settings_item_in_main_toolbar(); +} + std::shared_ptr<ProgressStatusBar> Plater::priv::statusbar() { return main_frame->m_statusbar; @@ -5278,6 +5290,8 @@ void Plater::set_printer_technology(PrinterTechnology printer_technology) if (wxGetApp().mainframe) wxGetApp().mainframe->update_menubar(); + + p->update_main_toolbar_tooltips(); } void Plater::changed_object(int obj_idx) diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index f8fa1a233..88c643add 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -188,11 +188,12 @@ void PreferencesDialog::accept() warning_catcher(this, wxString::Format(_(L("You need to restart %s to make the changes effective.")), SLIC3R_APP_NAME)); } + auto app_config = get_app_config(); + bool settings_layout_changed = m_values.find("old_settings_layout_mode") != m_values.end() || m_values.find("new_settings_layout_mode") != m_values.end() || m_values.find("dlg_settings_layout_mode") != m_values.end(); - bool canceled = false; if (settings_layout_changed) { // the dialog needs to be destroyed before the call to recreate_gui() // or sometimes the application crashes into wxDialogBase() destructor @@ -205,24 +206,23 @@ void PreferencesDialog::accept() wxICON_QUESTION | wxOK | wxCANCEL); if (dialog.ShowModal() == wxID_CANCEL) - canceled = true; + { + int selection = app_config->get("old_settings_layout_mode") == "1" ? 0 : + app_config->get("new_settings_layout_mode") == "1" ? 1 : + app_config->get("dlg_settings_layout_mode") == "1" ? 2 : 0; + + m_layout_mode_box->SetSelection(selection); + return; + } } - auto app_config = get_app_config(); for (std::map<std::string, std::string>::iterator it = m_values.begin(); it != m_values.end(); ++it) - { - if (canceled && (it->first == "old_settings_layout_mode" || - it->first == "new_settings_layout_mode" || - it->first == "dlg_settings_layout_mode" )) - continue; app_config->set(it->first, it->second); - } app_config->save(); - EndModal(wxID_OK); - if (settings_layout_changed && !canceled) + if (settings_layout_changed) // recreate application, if settings layout was changed wxGetApp().recreate_GUI(); else @@ -323,12 +323,12 @@ void PreferencesDialog::create_settings_mode_widget() wxWindow* parent = m_optgroup_gui->ctrl_parent(); - wxRadioBox* box = new wxRadioBox(parent, wxID_ANY, _L("Settings layout mode"), wxDefaultPosition, wxDefaultSize, WXSIZEOF(choices), choices, + m_layout_mode_box = new wxRadioBox(parent, wxID_ANY, _L("Settings layout mode"), wxDefaultPosition, wxDefaultSize, WXSIZEOF(choices), choices, 3, wxRA_SPECIFY_ROWS); - box->SetFont(wxGetApp().normal_font()); - box->SetSelection(selection); + m_layout_mode_box->SetFont(wxGetApp().normal_font()); + m_layout_mode_box->SetSelection(selection); - box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) { + m_layout_mode_box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) { int selection = e.GetSelection(); m_values["old_settings_layout_mode"] = boost::any_cast<bool>(selection == 0) ? "1" : "0"; @@ -337,7 +337,7 @@ void PreferencesDialog::create_settings_mode_widget() }); auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(box, 1, wxALIGN_CENTER_VERTICAL); + sizer->Add(m_layout_mode_box, 1, wxALIGN_CENTER_VERTICAL); m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND); } diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 7a6de8377..ec35bc2a4 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -19,6 +19,7 @@ class PreferencesDialog : public DPIDialog std::shared_ptr<ConfigOptionsGroup> m_optgroup_camera; std::shared_ptr<ConfigOptionsGroup> m_optgroup_gui; wxSizer* m_icon_size_sizer; + wxRadioBox* m_layout_mode_box; bool isOSX {false}; public: PreferencesDialog(wxWindow* parent); From 0098aee00295ca34f1ecf20a2248f276d1c33b7b Mon Sep 17 00:00:00 2001 From: YuSanka <yusanka@gmail.com> Date: Tue, 5 May 2020 20:36:00 +0200 Subject: [PATCH 04/13] Fixed OSX build --- src/slic3r/GUI/Preferences.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index ec35bc2a4..738e805b2 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -7,6 +7,8 @@ #include <wx/dialog.h> #include <map> +class wxRadioBox; + namespace Slic3r { namespace GUI { From 0940db7b2ea32d295b84e610fcffbbc9b6e6f178 Mon Sep 17 00:00:00 2001 From: bubnikv <bubnikv@gmail.com> Date: Wed, 6 May 2020 18:27:25 +0200 Subject: [PATCH 05/13] Improvements of the monotonous infill ordering: Calculate the initial path length and set the initial pheromone level accordingly. Implemented a stopping criterion to ant colony optimization. Fixed some compilation warnings. --- src/libslic3r/Fill/FillRectilinear2.cpp | 253 +++++++++++++++++++++--- 1 file changed, 228 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index b88520b55..d83249939 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -1361,7 +1361,6 @@ static void traverse_graph_generate_polylines( continue; } - dont_connect: // No way to continue the current polyline. Take the rest of the line up to the outer contour. // This will finish the polyline, starting another polyline at a new point. going_up ? ++ it : -- it; @@ -1442,6 +1441,8 @@ struct MonotonousRegionLink AntPath *next_flipped; }; +// Matrix of paths (AntPath) connecting ends of MontonousRegions. +// AntPath lengths and their derived visibilities refer to the length of the perimeter line if such perimeter segment exists. class AntPathMatrix { public: @@ -1456,6 +1457,12 @@ public: // From end of one region to the start of another region, both flipped or not flipped. m_matrix(regions.size() * regions.size() * 4, AntPath{ -1., -1., initial_pheromone}) {} + void update_inital_pheromone(float initial_pheromone) + { + for (AntPath &ap : m_matrix) + ap.pheromone = initial_pheromone; + } + AntPath& operator()(const MonotonousRegion ®ion_from, bool flipped_from, const MonotonousRegion ®ion_to, bool flipped_to) { int row = 2 * int(®ion_from - m_regions.data()) + flipped_from; @@ -1478,7 +1485,7 @@ public: // Just apply the Eucledian distance of the end points. path.length = unscale<float>(Vec2f(vline_to.pos - vline_from.pos, vline_to.intersections[i_to].pos() - vline_from.intersections[i_from].pos()).norm()); } - path.visibility = 1. / (path.length + EPSILON); + path.visibility = 1.f / (path.length + float(EPSILON)); } return path; } @@ -1497,7 +1504,7 @@ private: const ExPolygonWithOffset &m_poly_with_offset; const std::vector<SegmentedIntersectionLine> &m_segs; // From end of one region to the start of another region, both flipped or not flipped. - //FIXME one may possibly use sparse representation of the matrix. + //FIXME one may possibly use sparse representation of the matrix, likely using hashing. std::vector<AntPath> m_matrix; }; @@ -1724,7 +1731,98 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm return monotonous_regions; } -static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, std::vector<SegmentedIntersectionLine> &segs) +// Traverse path, calculate length of the draw for the purpose of optimization. +// This function is very similar to polylines_from_paths() in the way how it traverses the path, but +// polylines_from_paths() emits a path, while this function just calculates the path length. +static float montonous_region_path_length(const MonotonousRegion ®ion, bool dir, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs) +{ + // From the initial point (i_vline, i_intersection), follow a path. + int i_intersection = region.left_intersection_point(dir); + int i_vline = region.left.vline; + float total_length = 0.; + bool no_perimeter = false; + Vec2f last_point; + + for (;;) { + const SegmentedIntersectionLine &vline = segs[i_vline]; + const SegmentIntersection *it = &vline.intersections[i_intersection]; + const bool going_up = it->is_low(); + + if (no_perimeter) + total_length += (last_point - Vec2f(vline.pos, (it + (going_up ? - 1 : 1))->pos())).norm(); + + int iright = it->right_horizontal(); + if (going_up) { + // Traverse the complete vertical segment up to the inner contour. + for (;;) { + do { + ++ it; + iright = std::max(iright, it->right_horizontal()); + assert(it->is_inner()); + } while (it->type != SegmentIntersection::INNER_HIGH || (it + 1)->type != SegmentIntersection::OUTER_HIGH); + int inext = it->vertical_up(); + if (inext == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid) + break; + assert(it->iContour == vline.intersections[inext].iContour); + it = vline.intersections.data() + inext; + } + } else { + // Going down. + assert(it->is_high()); + assert(i_intersection > 0); + for (;;) { + do { + -- it; + if (int iright_new = it->right_horizontal(); iright_new != -1) + iright = iright_new; + assert(it->is_inner()); + } while (it->type != SegmentIntersection::INNER_LOW || (it - 1)->type != SegmentIntersection::OUTER_LOW); + int inext = it->vertical_down(); + if (inext == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid) + break; + assert(it->iContour == vline.intersections[inext].iContour); + it = vline.intersections.data() + inext; + } + } + + if (i_vline == region.right.vline) + break; + + int inext = it->right_horizontal(); + if (inext != -1 && it->next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) { + // Summarize length of the connection line along the perimeter. + //FIXME should it be weighted with a lower weight than non-extruding connection line? What weight? + // Taking half of the length. + total_length += 0.5f * float(measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline, it - vline.intersections.data(), inext)); + // Don't add distance to the next vertical line start to the total length. + no_perimeter = false; + i_intersection = inext; + } else { + // Finish the current vertical line, + going_up ? ++ it : -- it; + assert(it->is_outer()); + assert(it->is_high() == going_up); + // Mark the end of this vertical line. + last_point = Vec2f(vline.pos, it->pos()); + // Remember to add distance to the last point. + no_perimeter = true; + if (inext == -1) { + // Find the end of the next overlapping vertical segment. + const SegmentedIntersectionLine &vline_right = segs[i_vline + 1]; + const SegmentIntersection *right = going_up ? + &vertical_run_top(vline_right, vline_right.intersections[iright]) : &vertical_run_bottom(vline_right, vline_right.intersections[iright]); + i_intersection = int(right - vline_right.intersections.data()); + } else + i_intersection = inext; + } + + ++ i_vline; + } + + return unscale<float>(total_length); +} + +static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, const ExPolygonWithOffset &poly_with_offset, std::vector<SegmentedIntersectionLine> &segs) { // Map from low intersection to left / right side of a monotonous region. using MapType = std::pair<SegmentIntersection*, MonotonousRegion*>; @@ -1816,6 +1914,20 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, s } } #endif /* NDEBUG */ + + // Fill in sum length of connecting lines of a region. This length is used for optimizing the infill path for minimum length. + for (MonotonousRegion ®ion : regions) { + region.len1 = montonous_region_path_length(region, false, poly_with_offset, segs); + region.len2 = montonous_region_path_length(region, true, poly_with_offset, segs); + // Subtract the smaller length from the longer one, so we will optimize just with the positive difference of the two. + if (region.len1 > region.len2) { + region.len1 -= region.len2; + region.len2 = 0; + } else { + region.len2 -= region.len1; + region.len1 = 0; + } + } } // Raad Salman: Algorithms for the Precedence Constrained Generalized Travelling Salesperson Problem @@ -1851,6 +1963,7 @@ inline void print_ant(const std::string& fmt, TArgs&&... args) { } // Find a run through monotonous infill blocks using an 'Ant colony" optimization method. +// http://www.scholarpedia.org/article/Ant_colony_optimization static std::vector<MonotonousRegionLink> chain_monotonous_regions( std::vector<MonotonousRegion> ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs, std::mt19937_64 &rng) { @@ -1940,14 +2053,18 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions( }; #endif /* NDEBUG */ - // How many times to repeat the ant simulation. - constexpr int num_rounds = 10; + // How many times to repeat the ant simulation (number of ant generations). + constexpr int num_rounds = 25; + // After how many rounds without an improvement to exit? + constexpr int num_rounds_no_change_exit = 8; // With how many ants each of the run will be performed? - constexpr int num_ants = 10; - // Base (initial) pheromone level. - constexpr float pheromone_initial_deposit = 0.5f; + const int num_ants = std::min<int>(regions.size(), 10); + // Base (initial) pheromone level. This value will be adjusted based on the length of the first greedy path found. + float pheromone_initial_deposit = 0.5f; // Evaporation rate of pheromones. constexpr float pheromone_evaporation = 0.1f; + // Evaporation rate to diversify paths taken by individual ants. + constexpr float pheromone_diversification = 0.1f; // Probability at which to take the next best path. Otherwise take the the path based on the cost distribution. constexpr float probability_take_best = 0.9f; // Exponents of the cost function. @@ -1956,6 +2073,73 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions( AntPathMatrix path_matrix(regions, poly_with_offset, segs, pheromone_initial_deposit); + // Find an initial path in a greedy way, set the initial pheromone value to 10% of the cost of the greedy path. + { + // Construct the first path in a greedy way to calculate an initial value of the pheromone value. + queue = queue_initial; + left_neighbors_unprocessed = left_neighbors_unprocessed_initial; + assert(validate_unprocessed()); + // Pick the last of the queue. + MonotonousRegionLink path_end { queue.back(), false }; + queue.pop_back(); + -- left_neighbors_unprocessed[path_end.region - regions.data()]; + + float total_length = path_end.region->length(false); + while (! queue.empty() || ! path_end.region->right_neighbors.empty()) { + // Chain. + MonotonousRegion ®ion = *path_end.region; + bool dir = path_end.flipped; + NextCandidate next_candidate; + next_candidate.probability = 0; + for (MonotonousRegion *next : region.right_neighbors) { + int &unprocessed = left_neighbors_unprocessed[next - regions.data()]; + assert(unprocessed > 1); + if (left_neighbors_unprocessed[next - regions.data()] == 2) { + // Dependencies of the successive blocks are satisfied. + AntPath &path1 = path_matrix(region, dir, *next, false); + AntPath &path2 = path_matrix(region, dir, *next, true); + if (path1.visibility > next_candidate.probability) + next_candidate = { next, &path1, &path1, path1.visibility, false }; + if (path2.visibility > next_candidate.probability) + next_candidate = { next, &path2, &path2, path2.visibility, true }; + } + } + bool from_queue = next_candidate.probability == 0; + if (from_queue) { + for (MonotonousRegion *next : queue) { + AntPath &path1 = path_matrix(region, dir, *next, false); + AntPath &path2 = path_matrix(region, dir, *next, true); + if (path1.visibility > next_candidate.probability) + next_candidate = { next, &path1, &path1, path1.visibility, false }; + if (path2.visibility > next_candidate.probability) + next_candidate = { next, &path2, &path2, path2.visibility, true }; + } + } + // Move the other right neighbors with satisified constraints to the queue. + for (MonotonousRegion *next : region.right_neighbors) + if (-- left_neighbors_unprocessed[next - regions.data()] == 1 && next_candidate.region != next) + queue.emplace_back(next); + if (from_queue) { + // Remove the selected path from the queue. + auto it = std::find(queue.begin(), queue.end(), next_candidate.region); + assert(it != queue.end()); + *it = queue.back(); + queue.pop_back(); + } + // Extend the path. + MonotonousRegion *next_region = next_candidate.region; + bool next_dir = next_candidate.dir; + total_length += next_region->length(next_dir) + path_matrix(*path_end.region, path_end.flipped, *next_region, next_dir).length; + path_end = { next_region, next_dir }; + assert(left_neighbors_unprocessed[next_region - regions.data()] == 1); + left_neighbors_unprocessed[next_region - regions.data()] = 0; + } + + // Set an initial pheromone value to 10% of the greedy path's value. + pheromone_initial_deposit = 0.1 / total_length; + path_matrix.update_inital_pheromone(pheromone_initial_deposit); + } + // Probability (unnormalized) of traversing a link between two monotonous regions. auto path_probability = [pheromone_alpha, pheromone_beta](AntPath &path) { return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta); @@ -1966,8 +2150,10 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions( ++ irun; #endif /* SLIC3R_DEBUG_ANTS */ - for (int round = 0; round < num_rounds; ++ round) + int num_rounds_no_change = 0; + for (int round = 0; round < num_rounds && num_rounds_no_change < num_rounds_no_change_exit; ++ round) { + bool improved = false; for (int ant = 0; ant < num_ants; ++ ant) { // Find a new path following the pheromones deposited by the previous ants. @@ -1977,6 +2163,8 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions( left_neighbors_unprocessed = left_neighbors_unprocessed_initial; assert(validate_unprocessed()); // Pick randomly the first from the queue at random orientation. + //FIXME picking the 1st monotonous region should likely be done based on accumulated pheromone level as well, + // but the inefficiency caused by the random pick of the 1st monotonous region is likely insignificant. int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng); path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 }); *(queue.begin() + first_idx) = std::move(queue.back()); @@ -2051,7 +2239,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions( for (std::vector<NextCandidate>::iterator it_next_candidate = next_candidates.begin(); it_next_candidate != next_candidates.begin() + num_direct_neighbors; ++ it_next_candidate) if ((queue.empty() || it_next_candidate->region != queue.back()) && it_next_candidate->region != take_path->region) queue.emplace_back(it_next_candidate->region); - if (take_path - next_candidates.begin() >= num_direct_neighbors) { + if (size_t(take_path - next_candidates.begin()) >= num_direct_neighbors) { // Remove the selected path from the queue. auto it = std::find(queue.begin(), queue.end(), take_path->region); assert(it != queue.end()); @@ -2083,8 +2271,10 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions( path.back().flipped == path.back().region->flips ? path.back().region->right.high : path.back().region->right.low, path.back().flipped == path.back().region->flips ? path.back().region->right.low : path.back().region->right.high); - // Update pheromones along this link. - take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit; + // Update pheromones along this link, see Ant Colony System (ACS) update rule. + // http://www.scholarpedia.org/article/Ant_colony_optimization + // The goal here is to lower the pheromone trace for paths taken to diversify the next path picked in the same batch of ants. + take_path->link->pheromone = (1.f - pheromone_diversification) * take_path->link->pheromone + pheromone_diversification * pheromone_initial_deposit; assert(validate_unprocessed()); } @@ -2104,18 +2294,33 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions( if (path_length < best_path_length) { best_path_length = path_length; std::swap(best_path, path); +#if 0 // #if ! defined(SLIC3R_DEBUG_ANTS) && ! defined(ndebug) + if (round == 0 && ant == 0) + std::cout << std::endl; + std::cout << Slic3r::format("round %1% ant %2% path length %3%", round, ant, path_length) << std::endl; +#endif + if (path_length == 0) + // Perfect path found. + goto end; + improved = true; } } - // Reinforce the path feromones with the best path. - float total_cost = best_path_length + EPSILON; + // Reinforce the path pheromones with the best path. + float total_cost = best_path_length + float(EPSILON); for (size_t i = 0; i + 1 < path.size(); ++ i) { MonotonousRegionLink &link = path[i]; link.next->pheromone = (1.f - pheromone_evaporation) * link.next->pheromone + pheromone_evaporation / total_cost; } + + if (improved) + num_rounds_no_change = 0; + else + ++ num_rounds_no_change; } - return best_path; +end: + return best_path; } // Traverse path, produce polylines. @@ -2195,7 +2400,6 @@ static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path, int inext = it->vertical_up(); if (inext == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid) break; - const Polygon &poly = poly_with_offset.contour(it->iContour); assert(it->iContour == vline.intersections[inext].iContour); emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, it->has_left_vertical_up()); it = vline.intersections.data() + inext; @@ -2215,7 +2419,6 @@ static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path, int inext = it->vertical_down(); if (inext == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid) break; - const Polygon &poly = poly_with_offset.contour(it->iContour); assert(it->iContour == vline.intersections[inext].iContour); emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, it->has_right_vertical_down()); it = vline.intersections.data() + inext; @@ -2285,8 +2488,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP ExPolygonWithOffset poly_with_offset( surface->expolygon, - rotate_vector.first, - scale_(this->overlap - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing), - scale_(this->overlap - 0.5 * this->spacing)); + float(scale_(this->overlap - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing)), + float(scale_(this->overlap - 0.5 * this->spacing))); if (poly_with_offset.n_contours_inner == 0) { // Not a single infill line fits. //FIXME maybe one shall trigger the gap fill here? @@ -2317,7 +2520,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP size_t n_vlines = (bounding_box.max(0) - bounding_box.min(0) + line_spacing - 1) / line_spacing; coord_t x0 = bounding_box.min(0); if (params.full_infill()) - x0 += (line_spacing + SCALED_EPSILON) / 2; + x0 += (line_spacing + coord_t(SCALED_EPSILON)) / 2; #ifdef SLIC3R_DEBUG static int iRun = 0; @@ -2359,7 +2562,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP bool monotonous_infill = params.monotonous; // || params.density > 0.99; if (monotonous_infill) { std::vector<MonotonousRegion> regions = generate_montonous_regions(segs); - connect_monotonous_regions(regions, segs); + connect_monotonous_regions(regions, poly_with_offset, segs); if (! regions.empty()) { std::mt19937_64 rng; std::vector<MonotonousRegionLink> path = chain_monotonous_regions(regions, poly_with_offset, segs, rng); @@ -2478,10 +2681,10 @@ Polylines FillCubic::fill_surface(const Surface *surface, const FillParams ¶ params3.dont_connect = true; Polylines polylines_out; coordf_t dx = sqrt(0.5) * z; - if (! fill_surface_by_lines(surface, params2, 0.f, dx, polylines_out) || - ! fill_surface_by_lines(surface, params2, float(M_PI / 3.), - dx, polylines_out) || + if (! fill_surface_by_lines(surface, params2, 0.f, float(dx), polylines_out) || + ! fill_surface_by_lines(surface, params2, float(M_PI / 3.), - float(dx), polylines_out) || // Rotated by PI*2/3 + PI to achieve reverse sloping wall. - ! fill_surface_by_lines(surface, params3, float(M_PI * 2. / 3.), dx, polylines_out)) { + ! fill_surface_by_lines(surface, params3, float(M_PI * 2. / 3.), float(dx), polylines_out)) { printf("FillCubic::fill_surface() failed to fill a region.\n"); } return polylines_out; From b73e675aaab26f1187c331c4bd11ad735b9741f0 Mon Sep 17 00:00:00 2001 From: bubnikv <bubnikv@gmail.com> Date: Wed, 6 May 2020 18:27:51 +0200 Subject: [PATCH 06/13] Changed the Ironing G-code preview color based on Jindra's feedback. --- src/libslic3r/GCode/PreviewData.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index 3aae15748..551c13345 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -117,7 +117,8 @@ const Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[erCount] Color(1.0f, 1.0f, 0.0f, 1.0f), // erInternalInfill Color(1.0f, 0.0f, 1.0f, 1.0f), // erSolidInfill Color(0.0f, 1.0f, 1.0f, 1.0f), // erTopSolidInfill - Color(0.0f, 1.0f, 1.0f, 1.0f), // erIroning +// Color(1.0f, 0.7f, 0.61f, 1.0f), // erIroning + Color(1.0f, 0.55f, 0.41f, 1.0f), // erIroning Color(0.5f, 0.5f, 0.5f, 1.0f), // erBridgeInfill Color(1.0f, 1.0f, 1.0f, 1.0f), // erGapFill Color(0.5f, 0.0f, 0.0f, 1.0f), // erSkirt From a94c952b40d36b1505fb77b87c0dd739e1034659 Mon Sep 17 00:00:00 2001 From: YuSanka <yusanka@gmail.com> Date: Wed, 6 May 2020 20:45:57 +0200 Subject: [PATCH 07/13] Search: 1. To the imGui Dialog added icons instead of settings type 2. Fixed a search trough the options with # symbol in opt_key 3. Deleted "Type" checkbox from the Search Windows and added "Search in English" checkbox (but without implementation for this moment) Layouts: 1. Fixed a visibility of the "Switch to Settings" toolbar icon for the old layout mode --- src/imgui/imconfig.h | 7 +++ src/slic3r/GUI/GLCanvas3D.cpp | 7 +-- src/slic3r/GUI/ImGuiWrapper.cpp | 89 ++++++++++++++++++++++++++++++++- src/slic3r/GUI/ImGuiWrapper.hpp | 1 + src/slic3r/GUI/Plater.cpp | 2 + src/slic3r/GUI/Search.cpp | 61 ++++++++++++---------- src/slic3r/GUI/Search.hpp | 7 ++- 7 files changed, 142 insertions(+), 32 deletions(-) diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index c425bbef2..eb47459ea 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -107,6 +107,13 @@ namespace ImGui const char ColorMarkerStart = 0x2; // STX const char ColorMarkerEnd = 0x3; // ETX + // Special ASCII characters are used here as a ikons markers + const char PrintIconMarker = 0x4; + const char PrinterIconMarker = 0x5; + const char PrinterSlaIconMarker = 0x6; + const char FilamentIconMarker = 0x7; + const char MaterialIconMarker = 0x8; + // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6e397aef7..1abe6bf9f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4909,7 +4909,8 @@ bool GLCanvas3D::_init_main_toolbar() "\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ; item.sprite_id = 10; item.enabling_callback = GLToolbarItem::Default_Enabling_Callback; - item.visibility_callback = [this]() { return wxGetApp().app_config->get("old_settings_layout_mode") != "1"; }; + item.visibility_callback = [this]() { return (wxGetApp().app_config->get("new_settings_layout_mode") == "1" || + wxGetApp().app_config->get("dlg_settings_layout_mode") == "1"); }; item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(); }; if (!m_main_toolbar.add_item(item)) return false; @@ -4920,7 +4921,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "layersediting"; item.icon_filename = "layers_white.svg"; item.tooltip = _utf8(L("Variable layer height")); - item.sprite_id = /*10*/11; + item.sprite_id = 11; item.left.toggable = true; item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; item.visibility_callback = [this]()->bool @@ -4942,7 +4943,7 @@ bool GLCanvas3D::_init_main_toolbar() item.name = "search"; item.icon_filename = "search_.svg"; item.tooltip = _utf8(L("Search")) + " [" + GUI::shortkey_ctrl_prefix() + "F]"; - item.sprite_id = /*11*/12; + item.sprite_id = 12; item.left.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) { diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index ea8b2afd3..f4788693e 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -7,6 +7,7 @@ #include <boost/format.hpp> #include <boost/log/trivial.hpp> +#include <boost/filesystem.hpp> #include <wx/string.h> #include <wx/event.h> @@ -27,10 +28,21 @@ #include "I18N.hpp" #include "Search.hpp" +#include "nanosvg/nanosvg.h" +#include "nanosvg/nanosvgrast.h" + namespace Slic3r { namespace GUI { +static const std::map<const char, std::string> font_icons = { + {ImGui::PrintIconMarker , "cog" }, + {ImGui::PrinterIconMarker , "printer" }, + {ImGui::PrinterSlaIconMarker, "sla_printer"}, + {ImGui::FilamentIconMarker , "spool" }, + {ImGui::MaterialIconMarker , "resin" } +}; + ImGuiWrapper::ImGuiWrapper() : m_glyph_ranges(nullptr) , m_font_cjk(false) @@ -735,9 +747,9 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co // add checkboxes for show/hide Categories and Groups text(_L("Use for search")+":"); - check_box(_L("Type"), view_params.type); check_box(_L("Category"), view_params.category); check_box(_L("Group"), view_params.group); + check_box(_L("Search in English"), view_params.english); } void ImGuiWrapper::disabled_begin(bool disabled) @@ -791,6 +803,59 @@ static const ImWchar ranges_keyboard_shortcuts[] = }; #endif // __APPLE__ + +std::vector<unsigned char> ImGuiWrapper::load_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height) +{ +#ifdef __APPLE__ + // Note: win->GetContentScaleFactor() is not used anymore here because it tends to + // return bogus results quite often (such as 1.0 on Retina or even 0.0). + // We're using the max scaling factor across all screens because it's very likely to be good enough. + double scale = mac_max_scaling_factor(); +#else + double scale = 1.0; +#endif + std::vector<unsigned char> empty_vector; + +#ifdef __WXMSW__ + std::string folder = "white\\"; +#else + std::string folder = "white/"; +#endif + if (!boost::filesystem::exists(Slic3r::var(folder + bitmap_name + ".svg"))) + folder.clear(); + + NSVGimage* image = ::nsvgParseFromFile(Slic3r::var(folder + bitmap_name + ".svg").c_str(), "px", 96.0f); + if (image == nullptr) + return empty_vector; + + target_height != 0 ? target_height *= scale : target_width *= scale; + + float svg_scale = target_height != 0 ? + (float)target_height / image->height : target_width != 0 ? + (float)target_width / image->width : 1; + + int width = (int)(svg_scale * image->width + 0.5f); + int height = (int)(svg_scale * image->height + 0.5f); + int n_pixels = width * height; + if (n_pixels <= 0) { + ::nsvgDelete(image); + return empty_vector; + } + + NSVGrasterizer* rast = ::nsvgCreateRasterizer(); + if (rast == nullptr) { + ::nsvgDelete(image); + return empty_vector; + } + + std::vector<unsigned char> data(n_pixels * 4, 0); + ::nsvgRasterize(rast, image, 0, 0, svg_scale, data.data(), width, height, width * 4); + ::nsvgDeleteRasterizer(rast); + ::nsvgDelete(image); + + return data; +} + void ImGuiWrapper::init_font(bool compress) { destroy_font(); @@ -829,11 +894,33 @@ void ImGuiWrapper::init_font(bool compress) } #endif + float font_scale = m_font_size/15; + int icon_sz = lround(16 * font_scale); // default size of icon is 16 px + + int rect_id = io.Fonts->CustomRects.Size; // id of the rectangle added next + // add rectangles for the icons to the font atlas + for (auto& icon : font_icons) + io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz); + // Build texture atlas unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + // Fill rectangles from the SVG-icons + for (auto icon : font_icons) { + if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { + std::vector<unsigned char> raw_data = load_svg(icon.second, icon_sz, icon_sz); + const ImU32* pIn = (ImU32*)raw_data.data(); + for (int y = 0; y < icon_sz; y++) { + ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); + for (int x = 0; x < icon_sz; x++) + *pOut++ = *pIn++; + } + } + rect_id++; + } + // Upload texture to graphics system GLint last_texture; glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 6a1e27dcb..efb8acc9a 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -96,6 +96,7 @@ private: void render_draw_data(ImDrawData *draw_data); bool display_initialized() const; void destroy_font(); + std::vector<unsigned char> load_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height); static const char* clipboard_get(void* user_data); static void clipboard_set(void* user_data, const char* text); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index da3cc7f01..fc9e5060b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5275,6 +5275,8 @@ void Plater::set_printer_technology(PrinterTechnology printer_technology) wxGetApp().mainframe->update_menubar(); p->update_main_toolbar_tooltips(); + + p->sidebar->get_searcher().set_printer_technology(printer_technology); } void Plater::changed_object(int obj_idx) diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 844d2244c..33f097362 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -39,6 +39,23 @@ static const std::vector<std::wstring>& NameByType() return data; } +static char marker_by_type(Preset::Type type, PrinterTechnology pt) +{ + switch(type) { + case Preset::TYPE_PRINT: + case Preset::TYPE_SLA_PRINT: + return ImGui::PrintIconMarker; + case Preset::TYPE_FILAMENT: + return ImGui::FilamentIconMarker; + case Preset::TYPE_SLA_MATERIAL: + return ImGui::MaterialIconMarker; + case Preset::TYPE_PRINTER: + return pt == ptSLA ? ImGui::PrinterSlaIconMarker : ImGui::PrinterIconMarker; + default: + return ' '; + } +} + void FoundOption::get_marked_label_and_tooltip(const char** label_, const char** tooltip_) const { *label_ = marked_label.c_str(); @@ -106,17 +123,8 @@ void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type ty emplace(opt_key, label); else for (int i = 0; i < cnt; ++i) - emplace(opt_key + "[" + std::to_string(i) + "]", label); - - /*const GroupAndCategory& gc = groups_and_categories[opt_key]; - if (gc.group.IsEmpty() || gc.category.IsEmpty()) - continue; - - if (!label.IsEmpty()) - options.emplace_back(Option{opt_key, type, - label, _(label), - gc.group, _(gc.group), - gc.category, _(gc.category) });*/ + // ! It's very important to use "#". opt_key#n is a real option key used in GroupAndCategory + emplace(opt_key + "#" + std::to_string(i), label); } } @@ -183,19 +191,19 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) bool full_list = search.empty(); std::wstring sep = L" : "; - const std::vector<std::wstring>& name_by_type = NameByType(); - auto get_label = [this, &name_by_type, &sep](const Option& opt) + auto get_label = [this, &sep](const Option& opt) { std::wstring out; + out += marker_by_type(opt.type, printer_technology); const std::wstring *prev = nullptr; for (const std::wstring * const s : { - view_params.type ? &(name_by_type[opt.type]) : nullptr, view_params.category ? &opt.category_local : nullptr, view_params.group ? &opt.group_local : nullptr, &opt.label_local }) if (s != nullptr && (prev == nullptr || *prev != *s)) { - if (! out.empty()) +// if (! out.empty()) + if (out.size()>2) out += sep; out += *s; prev = s; @@ -203,17 +211,18 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) return out; }; - auto get_label_english = [this, &name_by_type, &sep](const Option& opt) + auto get_label_english = [this, &sep](const Option& opt) { std::wstring out; + out += marker_by_type(opt.type, printer_technology); const std::wstring*prev = nullptr; for (const std::wstring * const s : { - view_params.type ? &name_by_type[opt.type] : nullptr, view_params.category ? &opt.category : nullptr, view_params.group ? &opt.group : nullptr, &opt.label }) if (s != nullptr && (prev == nullptr || *prev != *s)) { - if (! out.empty()) +// if (! out.empty()) + if (out.size()>2) out += sep; out += *s; prev = s; @@ -221,9 +230,9 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) return out; }; - auto get_tooltip = [this, &name_by_type, &sep](const Option& opt) + auto get_tooltip = [this, &sep](const Option& opt) { - return name_by_type[opt.type] + sep + + return marker_by_type(opt.type, printer_technology) + opt.category_local + sep + opt.group_local + sep + opt.label_local; }; @@ -423,15 +432,15 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher) wxBoxSizer* check_sizer = new wxBoxSizer(wxHORIZONTAL); - check_type = new wxCheckBox(this, wxID_ANY, _L("Type")); check_category = new wxCheckBox(this, wxID_ANY, _L("Category")); check_group = new wxCheckBox(this, wxID_ANY, _L("Group")); + check_english = new wxCheckBox(this, wxID_ANY, _L("Search in English")); wxStdDialogButtonSizer* cancel_btn = this->CreateStdDialogButtonSizer(wxCANCEL); - check_sizer->Add(check_type, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border); check_sizer->Add(check_category, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border); check_sizer->Add(check_group, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border); + check_sizer->Add(check_english, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border); check_sizer->AddStretchSpacer(border); check_sizer->Add(cancel_btn, 0, wxALIGN_CENTER_VERTICAL); @@ -450,7 +459,7 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher) search_list->Bind(wxEVT_LEFT_UP, &SearchDialog::OnMouseClick, this); search_list->Bind(wxEVT_KEY_DOWN,&SearchDialog::OnKeyDown, this); - check_type ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this); + check_english ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this); check_category->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this); check_group ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this); @@ -470,9 +479,9 @@ void SearchDialog::Popup(wxPoint position /*= wxDefaultPosition*/) update_list(); const OptionViewParameters& params = searcher->view_params; - check_type->SetValue(params.type); check_category->SetValue(params.category); check_group->SetValue(params.group); + check_english->SetValue(params.english); this->SetPosition(position); this->ShowModal(); @@ -538,7 +547,7 @@ void SearchDialog::update_list() const std::vector<FoundOption>& filters = searcher->found_options(); for (const FoundOption& item : filters) - search_list->Append(from_u8(item.label)); + search_list->Append(from_u8(item.label).Remove(0, 1)); } void SearchDialog::OnKeyDown(wxKeyEvent& event) @@ -570,7 +579,7 @@ void SearchDialog::OnKeyDown(wxKeyEvent& event) void SearchDialog::OnCheck(wxCommandEvent& event) { OptionViewParameters& params = searcher->view_params; - params.type = check_type->GetValue(); + params.english = check_english->GetValue(); params.category = check_category->GetValue(); params.group = check_group->GetValue(); diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index 5fcb58f1e..60ef16c0f 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -66,9 +66,9 @@ struct FoundOption { struct OptionViewParameters { - bool type {false}; bool category {false}; bool group {true }; + bool english {false}; int hovered_id {-1}; }; @@ -77,6 +77,7 @@ class OptionsSearcher { std::string search_line; std::map<std::string, GroupAndCategory> groups_and_categories; + PrinterTechnology printer_technology; std::vector<Option> options {}; std::vector<FoundOption> found {}; @@ -120,6 +121,8 @@ public: const std::vector<FoundOption>& found_options() { return found; } const GroupAndCategory& get_group_and_category (const std::string& opt_key) { return groups_and_categories[opt_key]; } std::string& search_string() { return search_line; } + + void set_printer_technology(PrinterTechnology pt) { printer_technology = pt; } }; @@ -167,9 +170,9 @@ class SearchDialog : public GUI::DPIDialog wxTextCtrl* search_line { nullptr }; wxListBox* search_list { nullptr }; - wxCheckBox* check_type { nullptr }; wxCheckBox* check_category { nullptr }; wxCheckBox* check_group { nullptr }; + wxCheckBox* check_english { nullptr }; OptionsSearcher* searcher; From 3f958acdf9e1bbef6a4e68dca43bef6fcd6fecbe Mon Sep 17 00:00:00 2001 From: YuSanka <yusanka@gmail.com> Date: Wed, 6 May 2020 21:29:51 +0200 Subject: [PATCH 08/13] Added missed include for the OSX build --- src/slic3r/GUI/ImGuiWrapper.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index f4788693e..4385fa276 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -28,6 +28,7 @@ #include "I18N.hpp" #include "Search.hpp" +#include "../Utils/MacDarkMode.hpp" #include "nanosvg/nanosvg.h" #include "nanosvg/nanosvgrast.h" From 74e8e55fbc43a4ac2d90f65a310143a56ac74571 Mon Sep 17 00:00:00 2001 From: tamasmeszaros <meszaros.q@gmail.com> Date: Wed, 6 May 2020 19:59:31 +0200 Subject: [PATCH 09/13] Hotfix for debug builds with new wxWidgets in deps --- src/slic3r/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index b52201c38..49fd93821 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -216,6 +216,12 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES}) endif() +if (SLIC3R_STATIC AND UNIX) + # FIXME: This was previously exported by wx-config but the wxWidgets + # cmake build forgets this and the build fails in debug mode (or on raspberry release) + target_compile_definitions(libslic3r_gui PUBLIC -DwxDEBUG_LEVEL=0) +endif() + if(APPLE) target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY}) endif() From f741872361ba7da74a348659f2a66a72b55c2cb1 Mon Sep 17 00:00:00 2001 From: YuSanka <yusanka@gmail.com> Date: Thu, 7 May 2020 09:13:56 +0200 Subject: [PATCH 10/13] Fixed a can_change_view() function in respect to the new settings layouts --- src/slic3r/GUI/MainFrame.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index a47626dd1..62fffc423 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -476,6 +476,11 @@ bool MainFrame::can_slice() const bool MainFrame::can_change_view() const { + if (m_layout == slNew) + return m_plater->IsShown(); + if (m_layout == slDlg) + return true; + // slOld layout mode int page_id = m_tabpanel->GetSelection(); return page_id != wxNOT_FOUND && dynamic_cast<const Slic3r::GUI::Plater*>(m_tabpanel->GetPage((size_t)page_id)) != nullptr; } From 55e4b4864281daf7488d29034d32cadcd14bb1c7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros <meszaros.q@gmail.com> Date: Thu, 7 May 2020 09:13:20 +0200 Subject: [PATCH 11/13] deps: fix ZLIB being built after its needed --- deps/CMakeLists.txt | 3 +++ deps/deps-windows.cmake | 6 +----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 3251d2c62..06ab0fa65 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -152,6 +152,9 @@ include(MPFR/MPFR.cmake) include(CGAL/CGAL.cmake) include(wxWidgets/wxWidgets.cmake) +add_dependencies(dep_blosc ${ZLIB_PKG}) +add_dependencies(dep_openexr ${ZLIB_PKG}) + if (MSVC) add_custom_target(deps ALL diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 8be9888bc..ac93b4932 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -35,8 +35,6 @@ else () set(DEP_PLATFORM "x64") endif () - - if (${DEP_DEBUG}) set(DEP_BOOST_DEBUG "debug") else () @@ -217,7 +215,6 @@ ExternalProject_Add(dep_blosc #URL_HASH SHA256=7463a1df566704f212263312717ab2c36b45d45cba6cd0dccebf91b2cc4b4da9 GIT_REPOSITORY https://github.com/Blosc/c-blosc.git GIT_TAG e63775855294b50820ef44d1b157f4de1cc38d3e #v1.17.0 - DEPENDS ${ZLIB_PKG} CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS @@ -243,8 +240,7 @@ add_debug_dep(dep_blosc) ExternalProject_Add(dep_openexr EXCLUDE_FROM_ALL 1 GIT_REPOSITORY https://github.com/openexr/openexr.git - GIT_TAG eae0e337c9f5117e78114fd05f7a415819df413a #v2.4.0 - DEPENDS ${ZLIB_PKG} + GIT_TAG eae0e337c9f5117e78114fd05f7a415819df413a #v2.4.0 CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS From 1238b40f143609be19fabb344a7c421c7827c7ba Mon Sep 17 00:00:00 2001 From: tamasmeszaros <meszaros.q@gmail.com> Date: Thu, 7 May 2020 09:42:48 +0200 Subject: [PATCH 12/13] Link errors in debug build affect windows as well --- src/slic3r/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 49fd93821..ac6961990 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -216,7 +216,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES}) endif() -if (SLIC3R_STATIC AND UNIX) +if (SLIC3R_STATIC) # FIXME: This was previously exported by wx-config but the wxWidgets # cmake build forgets this and the build fails in debug mode (or on raspberry release) target_compile_definitions(libslic3r_gui PUBLIC -DwxDEBUG_LEVEL=0) From 34b504d50edbcd30d7ceb91059a3ca1abab17fb7 Mon Sep 17 00:00:00 2001 From: dakkar <dakkar@thenautilus.net> Date: Mon, 6 Apr 2020 12:48:33 +0100 Subject: [PATCH 13/13] avoid calling GetHeight() on non-IsOk() icons probably fixes #3837 --- src/slic3r/GUI/ObjectDataViewModel.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 0905f4915..d9b9af016 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -1710,10 +1710,11 @@ bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state) { dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); xoffset = icon.GetWidth() + 4; + + if (rect.height==0) + rect.height= icon.GetHeight(); } - if (rect.height==0) - rect.height= icon.GetHeight(); RenderText(m_value.GetText(), xoffset, rect, dc, state); return true;