From bbcedae5fd2a2765f6315bbe1753cf4e8a86e2ce Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 5 Nov 2021 15:18:10 +0100 Subject: [PATCH 01/16] MSW: DarkMode: Improvements for message Dialogs * A little bit reworked MsgDialog: Checkbox and buttons are placed under the Static line * Implemented wrapper for wxRichMessageDialog + Implemented wrapper for wxStaticLine --- src/slic3r/Config/Snapshot.cpp | 3 +- src/slic3r/GUI/ConfigWizard.cpp | 11 +- src/slic3r/GUI/GUI_App.cpp | 12 +- src/slic3r/GUI/MsgDialog.cpp | 45 +++++--- src/slic3r/GUI/MsgDialog.hpp | 191 +++++++++++++++++++++++++++++--- src/slic3r/GUI/Plater.cpp | 12 +- 6 files changed, 221 insertions(+), 53 deletions(-) diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 23713dd119..77a74bffeb 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -22,6 +22,7 @@ #include "../GUI/GUI_App.hpp" #include "../GUI/I18N.hpp" #include "../GUI/MainFrame.hpp" +#include "../GUI/MsgDialog.hpp" #include @@ -591,7 +592,7 @@ bool take_config_snapshot_cancel_on_error(const AppConfig &app_config, Snapshot: SnapshotDB::singleton().take_snapshot(app_config, reason, comment); return true; } catch (std::exception &err) { - wxRichMessageDialog dlg(static_cast(wxGetApp().mainframe), + RichMessageDialog dlg(static_cast(wxGetApp().mainframe), _L("PrusaSlicer has encountered an error while taking a configuration snapshot.") + "\n\n" + from_u8(err.what()) + "\n\n" + from_u8(message), _L("PrusaSlicer error"), wxYES_NO); diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index b3b00f60c8..762de2cf53 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -555,7 +555,7 @@ PagePrinters::PagePrinters(ConfigWizard *parent, wizard_p()->on_printer_pick(this, evt); }); - append(new wxStaticLine(this)); + append(new StaticLine(this)); append(picker); printer_pickers.push_back(picker); @@ -2800,11 +2800,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent) auto *vsizer = new wxBoxSizer(wxVERTICAL); auto *topsizer = new wxBoxSizer(wxHORIZONTAL); - wxStaticLine* hline = nullptr; -#ifdef _MSW_DARK_MODE - if (!NppDarkMode::IsEnabled()) -#endif //_MSW_DARK_MODE - hline = new wxStaticLine(this); + auto* hline = new StaticLine(this); p->btnsizer = new wxBoxSizer(wxHORIZONTAL); // Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling. @@ -2880,8 +2876,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent) p->index->go_to(size_t{0}); vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN); - if (hline) - vsizer->Add(hline, 0, wxEXPAND); + vsizer->Add(hline, 0, wxEXPAND | wxLEFT | wxRIGHT, VERTICAL_SPACING); vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN); SetSizer(vsizer); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index bab07b4664..025614d4d2 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -411,7 +411,7 @@ bool static check_old_linux_datadir(const wxString& app_name) { "location again.\n\n" "What do you want to do now?")) % SLIC3R_APP_NAME % new_path % old_path).str()); wxString caption = from_u8((boost::format(_u8L("%s - BREAKING CHANGE")) % SLIC3R_APP_NAME).str()); - wxRichMessageDialog dlg(nullptr, msg, caption, wxYES_NO); + RichMessageDialog dlg(nullptr, msg, caption, wxYES_NO); dlg.SetYesNoLabels(_L("Quit, I will move my data now"), _L("Start the application")); if (dlg.ShowModal() != wxID_NO) return false; @@ -846,7 +846,7 @@ bool GUI_App::check_older_app_config(Semver current_version, bool backup) return false; BOOST_LOG_TRIVIAL(info) << "last app config file used: " << m_older_data_dir_path; // ask about using older data folder - wxRichMessageDialog msg(nullptr, backup ? + RichMessageDialog msg(nullptr, backup ? wxString::Format(_L("PrusaSlicer detected another configuration folder at %s." "\nIts version is %s." "\nLast version you used in current configuration folder is %s." @@ -936,7 +936,7 @@ bool GUI_App::on_init_inner() // win32 build on win64 and viceversa #ifdef _WIN64 if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "") { - wxRichMessageDialog dlg(nullptr, + RichMessageDialog dlg(nullptr, _L("You have started PrusaSlicer for 64-bit architecture on 32-bit system." "\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/." "\nDo you wish to continue?"), @@ -946,7 +946,7 @@ bool GUI_App::on_init_inner() } #elif _WIN32 if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "64") { - wxRichMessageDialog dlg(nullptr, + RichMessageDialog dlg(nullptr, _L("You have started PrusaSlicer for 32-bit architecture on 64-bit system." "\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/." "\nDo you wish to continue?"), @@ -991,7 +991,7 @@ bool GUI_App::on_init_inner() bool ssl_accept = app_config->get("tls_cert_store_accepted") == "yes" && ssl_cert_store == Http::tls_system_cert_store(); if (!msg.empty() && !ssl_accept) { - wxRichMessageDialog + RichMessageDialog dlg(nullptr, wxString::Format(_L("%s\nDo you want to continue?"), msg), "PrusaSlicer", wxICON_QUESTION | wxYES_NO); @@ -2855,7 +2855,7 @@ bool GUI_App::open_browser_with_warning_dialog(const wxString& url, int flags/* bool launch = true; if (get_app_config()->get("suppress_hyperlinks").empty()) { - wxRichMessageDialog dialog(nullptr, _L("Should we open this hyperlink in your default browser?"), _L("PrusaSlicer: Open hyperlink"), wxICON_QUESTION | wxYES_NO); + RichMessageDialog dialog(nullptr, _L("Should we open this hyperlink in your default browser?"), _L("PrusaSlicer: Open hyperlink"), wxICON_QUESTION | wxYES_NO); dialog.ShowCheckBox(_L("Remember my choice")); int answer = dialog.ShowModal(); launch = answer == wxID_YES; diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index c4cdde3d90..bc17dd9cec 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -27,7 +27,7 @@ namespace GUI { MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id, wxBitmap bitmap) : wxDialog(parent ? parent : dynamic_cast(wxGetApp().mainframe), wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) - , boldfont(wxGetApp().normal_font()/*wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)*/) + , boldfont(wxGetApp().normal_font()) , content_sizer(new wxBoxSizer(wxVERTICAL)) , btn_sizer(new wxBoxSizer(wxHORIZONTAL)) { @@ -36,6 +36,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he this->SetFont(wxGetApp().normal_font()); this->CenterOnParent(); + auto *main_sizer = new wxBoxSizer(wxVERTICAL); auto *topsizer = new wxBoxSizer(wxHORIZONTAL); auto *rightsizer = new wxBoxSizer(wxVERTICAL); @@ -46,6 +47,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he rightsizer->AddSpacer(VERT_SPACING); rightsizer->Add(content_sizer, 1, wxEXPAND); + btn_sizer->AddStretchSpacer(); if (button_id != wxID_NONE) { auto *button = new wxButton(this, button_id); @@ -53,8 +55,6 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he btn_sizer->Add(button); } - rightsizer->Add(btn_sizer, 0, wxALIGN_RIGHT); - if (! bitmap.IsOk()) { bitmap = create_scaled_bitmap("PrusaSlicer_192px.png", this, 192); } @@ -64,7 +64,11 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he topsizer->Add(logo, 0, wxALL, BORDER); topsizer->Add(rightsizer, 1, wxTOP | wxBOTTOM | wxRIGHT | wxEXPAND, BORDER); - SetSizerAndFit(topsizer); + main_sizer->Add(topsizer, 1, wxEXPAND); + main_sizer->Add(new StaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT, HORIZ_SPACING); + main_sizer->Add(btn_sizer, 0, wxALL | wxEXPAND, VERT_SPACING); + + SetSizerAndFit(main_sizer); } void MsgDialog::add_btn(wxWindowID btn_id, bool set_focus /*= false*/) @@ -72,7 +76,7 @@ void MsgDialog::add_btn(wxWindowID btn_id, bool set_focus /*= false*/) wxButton* btn = new wxButton(this, btn_id); if (set_focus) btn->SetFocus(); - btn_sizer->Add(btn, 0, wxRIGHT, HORIZ_SPACING); + btn_sizer->Add(btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, HORIZ_SPACING); btn->Bind(wxEVT_BUTTON, [this, btn_id](wxCommandEvent&) { this->EndModal(btn_id); }); }; @@ -209,33 +213,38 @@ MessageDialog::MessageDialog(wxWindow* parent, apply_style(style); finalize(); } -#endif -// MessageWithCheckDialog +// RichMessageDialog -MessageWithCheckDialog::MessageWithCheckDialog( wxWindow* parent, - const wxString& message, - const wxString& checkbox_label, - const wxString& caption/* = wxEmptyString*/, - long style/* = wxOK*/) +RichMessageDialog::RichMessageDialog(wxWindow* parent, + const wxString& message, + const wxString& caption/* = wxEmptyString*/, + long style/* = wxOK*/) : MsgDialog(parent, caption.IsEmpty() ? wxString::Format(_L("%s info"), SLIC3R_APP_NAME) : caption, wxEmptyString, wxID_NONE) { add_msg_content(this, content_sizer, message); - m_check = new wxCheckBox(this, wxID_ANY, checkbox_label); - content_sizer->Add(m_check, 0, wxTOP, 10); + m_checkBox = new wxCheckBox(this, wxID_ANY, m_checkBoxText); + m_checkBox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) { m_checkBoxValue = m_checkBox->GetValue(); }); + + btn_sizer->Insert(0, m_checkBox, wxALIGN_CENTER_VERTICAL); apply_style(style); finalize(); } -bool MessageWithCheckDialog::GetCheckVal() +int RichMessageDialog::ShowModal() { - if (m_check) - return m_check->GetValue(); - return false; + if (m_checkBoxText.IsEmpty()) + m_checkBox->Hide(); + else + m_checkBox->SetLabelText(m_checkBoxText); + Layout(); + + return wxDialog::ShowModal(); } +#endif // InfoDialog diff --git a/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp index d3263f970e..22a81579a6 100644 --- a/src/slic3r/GUI/MsgDialog.hpp +++ b/src/slic3r/GUI/MsgDialog.hpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include class wxBoxSizer; class wxCheckBox; @@ -17,7 +19,6 @@ namespace Slic3r { namespace GUI { - // A message / query dialog with a bitmap on the left and any content on the right // with buttons underneath. struct MsgDialog : wxDialog @@ -87,6 +88,23 @@ public: }; #ifdef _WIN32 +// Generic static line, used intead of wxStaticLine +class StaticLine: public wxTextCtrl +{ +public: + StaticLine( wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxLI_HORIZONTAL, + const wxString& name = wxASCII_STR(wxTextCtrlNameStr)) + : wxTextCtrl(parent, id, wxEmptyString, pos, size!=wxDefaultSize ? size : (style == wxLI_HORIZONTAL ? wxSize(10, 1) : wxSize(1, 10)), wxSIMPLE_BORDER, wxDefaultValidator, name) + { + this->Enable(false); + } + ~StaticLine() {} +}; + // Generic message dialog, used intead of wxMessageDialog class MessageDialog : public MsgDialog { @@ -101,7 +119,158 @@ public: MessageDialog &operator=(const MessageDialog&) = delete; virtual ~MessageDialog() = default; }; + +// Generic rich message dialog, used intead of wxRichMessageDialog +class RichMessageDialog : public MsgDialog +{ + wxCheckBox* m_checkBox{ nullptr }; + wxString m_checkBoxText; + bool m_checkBoxValue{ false }; + +public: + RichMessageDialog( wxWindow *parent, + const wxString& message, + const wxString& caption = wxEmptyString, + long style = wxOK); + RichMessageDialog(RichMessageDialog&&) = delete; + RichMessageDialog(const RichMessageDialog&) = delete; + RichMessageDialog &operator=(RichMessageDialog&&) = delete; + RichMessageDialog &operator=(const RichMessageDialog&) = delete; + virtual ~RichMessageDialog() = default; + + int ShowModal() override; + + void ShowCheckBox(const wxString& checkBoxText, bool checked = false) + { + m_checkBoxText = checkBoxText; + m_checkBoxValue = checked; + } + + wxString GetCheckBoxText() const { return m_checkBoxText; } + bool IsCheckBoxChecked() const { return m_checkBoxValue; } + +// This part o fcode isported from the "wx\msgdlg.h" + using wxMD = wxMessageDialogBase; + // customization of the message box buttons + virtual bool SetYesNoLabels(const wxMD::ButtonLabel& yes, const wxMD::ButtonLabel& no) + { + DoSetCustomLabel(m_yes, yes); + DoSetCustomLabel(m_no, no); + return true; + } + + virtual bool SetYesNoCancelLabels(const wxMD::ButtonLabel& yes, + const wxMD::ButtonLabel& no, + const wxMD::ButtonLabel& cancel) + { + DoSetCustomLabel(m_yes, yes); + DoSetCustomLabel(m_no, no); + DoSetCustomLabel(m_cancel, cancel); + return true; + } + + virtual bool SetOKLabel(const wxMD::ButtonLabel& ok) + { + DoSetCustomLabel(m_ok, ok); + return true; +} + + virtual bool SetOKCancelLabels(const wxMD::ButtonLabel& ok, + const wxMD::ButtonLabel& cancel) + { + DoSetCustomLabel(m_ok, ok); + DoSetCustomLabel(m_cancel, cancel); + return true; + } + + virtual bool SetHelpLabel(const wxMD::ButtonLabel& help) + { + DoSetCustomLabel(m_help, help); + return true; + } + // test if any custom labels were set + bool HasCustomLabels() const + { + return !(m_ok.empty() && m_cancel.empty() && m_help.empty() && + m_yes.empty() && m_no.empty()); + } + + // these functions return the label to be used for the button which is + // either a custom label explicitly set by the user or the default label, + // i.e. they always return a valid string + wxString GetYesLabel() const + { + return m_yes.empty() ? GetDefaultYesLabel() : m_yes; + } + wxString GetNoLabel() const + { + return m_no.empty() ? GetDefaultNoLabel() : m_no; + } + wxString GetOKLabel() const + { + return m_ok.empty() ? GetDefaultOKLabel() : m_ok; + } + wxString GetCancelLabel() const + { + return m_cancel.empty() ? GetDefaultCancelLabel() : m_cancel; + } + wxString GetHelpLabel() const + { + return m_help.empty() ? GetDefaultHelpLabel() : m_help; + } + +protected: + // this function is called by our public SetXXXLabels() and should assign + // the value to var with possibly some transformation (e.g. Cocoa version + // currently uses this to remove any accelerators from the button strings + // while GTK+ one handles stock items specifically here) + void DoSetCustomLabel(wxString& var, const wxMD::ButtonLabel& label) + { + var = label.GetAsString(); + } + + // these functions return the custom label or empty string and should be + // used only in specific circumstances such as creating the buttons with + // these labels (in which case it makes sense to only use a custom label if + // it was really given and fall back on stock label otherwise), use the + // Get{Yes,No,OK,Cancel}Label() methods above otherwise + const wxString& GetCustomYesLabel() const { return m_yes; } + const wxString& GetCustomNoLabel() const { return m_no; } + const wxString& GetCustomOKLabel() const { return m_ok; } + const wxString& GetCustomHelpLabel() const { return m_help; } + const wxString& GetCustomCancelLabel() const { return m_cancel; } + +private: + // these functions may be overridden to provide different defaults for the + // default button labels (this is used by wxGTK) + virtual wxString GetDefaultYesLabel() const { return wxGetTranslation("Yes"); } + virtual wxString GetDefaultNoLabel() const { return wxGetTranslation("No"); } + virtual wxString GetDefaultOKLabel() const { return wxGetTranslation("OK"); } + virtual wxString GetDefaultCancelLabel() const { return wxGetTranslation("Cancel"); } + virtual wxString GetDefaultHelpLabel() const { return wxGetTranslation("Help"); } + + // labels for the buttons, initially empty meaning that the defaults should + // be used, use GetYes/No/OK/CancelLabel() to access them + wxString m_yes, + m_no, + m_ok, + m_cancel, + m_help; +}; #else +// just a wrapper for wxStaticLine to use the same code on all platforms +class StaticLine : public wxStaticLine +{ +public: + StaticLine(wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxLI_HORIZONTAL, + const wxString& name = wxASCII_STR(wxStaticLineNameStr)) + : wxStaticLine(parent, id, pos, size, style, name) {} + ~StaticLine() {} +}; // just a wrapper to wxMessageBox to use the same code on all platforms class MessageDialog : public wxMessageDialog { @@ -113,25 +282,19 @@ public: : wxMessageDialog(parent, message, caption, style) {} ~MessageDialog() {} }; -#endif -class MessageWithCheckDialog : public MsgDialog +// just a wrapper to wxRichMessageBox to use the same code on all platforms +class RichMessageDialog : public wxRichMessageDialog { - wxCheckBox* m_check{ nullptr }; public: - MessageWithCheckDialog(wxWindow* parent, + RichMessageDialog(wxWindow* parent, const wxString& message, - const wxString& checkbox_label, const wxString& caption = wxEmptyString, - long style = wxOK); - MessageWithCheckDialog(MessageWithCheckDialog&&) = delete; - MessageWithCheckDialog(const MessageWithCheckDialog&) = delete; - MessageWithCheckDialog& operator=(MessageWithCheckDialog&&) = delete; - MessageWithCheckDialog& operator=(const MessageWithCheckDialog&) = delete; - virtual ~MessageWithCheckDialog() = default; - - bool GetCheckVal(); + long style = wxOK) + : wxRichMessageDialog(parent, message, caption, style) {} + ~RichMessageDialog() {} }; +#endif // Generic info dialog, used for displaying exceptions class InfoDialog : public MsgDialog diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index eb64fb08ca..2567ca52f0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2482,15 +2482,15 @@ std::vector Plater::priv::load_files(const std::vector& input_ model.convert_from_meters(true); }; if (answer_convert_from_meters == wxOK_DEFAULT) { - MessageWithCheckDialog dlg(q, format_wxstr(_L_PLURAL( + RichMessageDialog dlg(q, format_wxstr(_L_PLURAL( "The dimensions of the object from file %s seem to be defined in meters.\n" "The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of the object?", "The dimensions of some objects from file %s seem to be defined in meters.\n" "The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of these objects?", model.objects.size()), from_path(filename)) + "\n", - _L("Apply to all the remaining small objects being loaded."), _L("The object is too small"), wxICON_WARNING | wxYES | wxNO); + dlg.ShowCheckBox(_L("Apply to all the remaining small objects being loaded.")); int answer = dlg.ShowModal(); - if (dlg.GetCheckVal()) + if (dlg.IsCheckBoxChecked()) answer_convert_from_meters = answer; else convert_model_if(model, answer == wxID_YES); @@ -2504,15 +2504,15 @@ std::vector Plater::priv::load_files(const std::vector& input_ convert_from_imperial_units(model, true); }; if (answer_convert_from_imperial_units == wxOK_DEFAULT) { - MessageWithCheckDialog dlg(q, format_wxstr(_L_PLURAL( + RichMessageDialog dlg(q, format_wxstr(_L_PLURAL( "The dimensions of the object from file %s seem to be defined in inches.\n" "The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of the object?", "The dimensions of some objects from file %s seem to be defined in inches.\n" "The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of these objects?", model.objects.size()), from_path(filename)) + "\n", - _L("Apply to all the remaining small objects being loaded."), _L("The object is too small"), wxICON_WARNING | wxYES | wxNO); + dlg.ShowCheckBox(_L("Apply to all the remaining small objects being loaded.")); int answer = dlg.ShowModal(); - if (dlg.GetCheckVal()) + if (dlg.IsCheckBoxChecked()) answer_convert_from_imperial_units = answer; else convert_model_if(model, answer == wxID_YES); From fb429c1e23cee632a13c926417960096d01d7bd8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 5 Nov 2021 15:28:42 +0100 Subject: [PATCH 02/16] Load layer height from config.ini if profile is missing from sl1 archive --- src/libslic3r/Format/SL1.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index c4b8f030f7..aef8038d69 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -135,7 +135,7 @@ ArchiveData extract_sla_archive(const std::string &zipfname, ExPolygons rings_to_expolygons(const std::vector &rings, double px_w, double px_h) { - ExPolygons polys; polys.reserve(rings.size()); + auto polys = reserve_vector(rings.size()); for (const marchsq::Ring &ring : rings) { Polygon poly; Points &pts = poly.points; @@ -147,7 +147,7 @@ ExPolygons rings_to_expolygons(const std::vector &rings, polys.emplace_back(poly); } - // reverse the raster transformations + // TODO: Is a union necessary? return union_ex(polys); } @@ -270,11 +270,11 @@ std::vector extract_slices_from_sla_archive( png::ReadBuf rb{arch.images[i].buf.data(), arch.images[i].buf.size()}; if (!png::decode_png(rb, img)) return; - auto rings = marchsq::execute(img, 128, rstp.win); + uint8_t isoval = 128; + auto rings = marchsq::execute(img, isoval, rstp.win); ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h); - // Invert the raster transformations indicated in - // the profile metadata + // Invert the raster transformations indicated in the profile metadata invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height); slices[i] = std::move(expolys); @@ -310,7 +310,18 @@ ConfigSubstitutions import_sla_archive( std::string exclude_entries{"thumbnail"}; ArchiveData arch = extract_sla_archive(zipfname, exclude_entries); DynamicPrintConfig profile_in, profile_use; - ConfigSubstitutions config_substitutions = profile_in.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable); + ConfigSubstitutions config_substitutions = + profile_in.load(arch.profile, + ForwardCompatibilitySubstitutionRule::Enable); + + if (profile_in.empty()) { // missing profile... do guess work + // try to recover the layer height from the config.ini which was + // present in all versions of sl1 files. + auto lh_str = arch.config.find("layerHeight")->second.data(); + double lh = std::stod(lh_str); // TODO replace with std::from_chars + profile_out.set("layer_height", lh); + profile_out.set("initial_layer_height", lh); + } // If the archive contains an empty profile, use the one that was passed as output argument // then replace it with the readed profile to report that it was empty. From 1c940ef145ae844aa4cdf8943b5356a9c2accbfe Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 5 Nov 2021 15:29:58 +0100 Subject: [PATCH 03/16] Do not reset sl1 import dialog settings between imports --- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index c4465edbaf..287b2e88be 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -122,7 +122,9 @@ public: std::string err; ConfigSubstitutions config_substitutions; - priv(Plater *plt) : plater{plt} {} + ImportDlg import_dlg; + + priv(Plater *plt) : plater{plt}, import_dlg{plt} {} }; SLAImportJob::SLAImportJob(std::shared_ptr pri, Plater *plater) @@ -176,14 +178,12 @@ void SLAImportJob::prepare() { reset(); - ImportDlg dlg{p->plater}; - - if (dlg.ShowModal() == wxID_OK) { - auto path = dlg.get_path(); + if (p->import_dlg.ShowModal() == wxID_OK) { + auto path = p->import_dlg.get_path(); auto nm = wxFileName(path); p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : nm.GetFullPath(); - p->sel = dlg.get_selection(); - p->win = dlg.get_marchsq_windowsize(); + p->sel = p->import_dlg.get_selection(); + p->win = p->import_dlg.get_marchsq_windowsize(); p->config_substitutions.clear(); } else { p->path = ""; From c4c8b7608e795323e39e949ab08fa85e9416af6c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 5 Nov 2021 15:32:14 +0100 Subject: [PATCH 04/16] Fix some mesh errors in sl1 archive reconstruction --- src/libslic3r/SlicesToTriangleMesh.cpp | 59 +++++++++++++------------- src/libslic3r/SlicesToTriangleMesh.hpp | 8 ++-- src/libslic3r/TriangleMesh.cpp | 26 +++++------- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 2 +- 4 files changed, 45 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/SlicesToTriangleMesh.cpp b/src/libslic3r/SlicesToTriangleMesh.cpp index 7a2975d12d..3b55cf066b 100644 --- a/src/libslic3r/SlicesToTriangleMesh.cpp +++ b/src/libslic3r/SlicesToTriangleMesh.cpp @@ -1,3 +1,4 @@ +#include #include "SlicesToTriangleMesh.hpp" @@ -22,11 +23,16 @@ inline indexed_triangle_set wall_strip(const Polygon &poly, ret.vertices.reserve(ret.vertices.size() + 2 *offs); + // The expression unscaled(p).cast().eval() is important here + // as it ensures identical conversion of 2D scaled coordinates to float 3D + // to that used by the tesselation. This way, the duplicated vertices in the + // output mesh can be found with the == operator of the points. + // its_merge_vertices will then reliably remove the duplicates. for (const Point &p : poly.points) - ret.vertices.emplace_back(to_3d(unscaled(p), float(lower_z_mm))); + ret.vertices.emplace_back(to_3d(unscaled(p).cast().eval(), float(lower_z_mm))); for (const Point &p : poly.points) - ret.vertices.emplace_back(to_3d(unscaled(p), float(upper_z_mm))); + ret.vertices.emplace_back(to_3d(unscaled(p).cast().eval(), float(upper_z_mm))); for (size_t i = startidx + 1; i < startidx + offs; ++i) { ret.indices.emplace_back(i - 1, i, i + offs - 1); @@ -84,12 +90,14 @@ indexed_triangle_set slices_to_mesh( const ExPolygons &upper = slices[i + 1]; const ExPolygons &lower = slices[i]; - ExPolygons dff1 = diff_ex(lower, upper); - ExPolygons dff2 = diff_ex(upper, lower); - its_merge(layers[i], triangulate_expolygons_3d(dff1, grid[i], NORMALS_UP)); - its_merge(layers[i], triangulate_expolygons_3d(dff2, grid[i], NORMALS_DOWN)); + // Small 0 area artefacts can be created by diff_ex, and the + // tesselation also can create 0 area triangles. These will be removed + // by its_remove_degenerate_faces. + ExPolygons free_top = diff_ex(lower, upper); + ExPolygons overhang = diff_ex(upper, lower); + its_merge(layers[i], triangulate_expolygons_3d(free_top, grid[i], NORMALS_UP)); + its_merge(layers[i], triangulate_expolygons_3d(overhang, grid[i], NORMALS_DOWN)); its_merge(layers[i], straight_walls(upper, grid[i], grid[i + 1])); - }); auto merge_fn = []( const indexed_triangle_set &a, const indexed_triangle_set &b ) { @@ -99,37 +107,30 @@ indexed_triangle_set slices_to_mesh( auto ret = execution::reduce(ex_tbb, layers.begin(), layers.end(), indexed_triangle_set{}, merge_fn); - // sla::Contour3D ret = tbb::parallel_reduce( - // tbb::blocked_range(layers.begin(), layers.end()), - // sla::Contour3D{}, - // [](const tbb::blocked_range& r, sla::Contour3D - // init) { - // for(auto it = r.begin(); it != r.end(); ++it ) - // init.merge(*it); return init; - // }, - // []( const sla::Contour3D &a, const sla::Contour3D &b ) { - // sla::Contour3D res{a}; res.merge(b); return res; - // }); - its_merge(ret, triangulate_expolygons_3d(slices.front(), zmin, NORMALS_DOWN)); its_merge(ret, straight_walls(slices.front(), zmin, grid.front())); its_merge(ret, triangulate_expolygons_3d(slices.back(), grid.back(), NORMALS_UP)); - + + // FIXME: these repairs do not fix the mesh entirely. There will be cracks + // in the output. It is very hard to do the meshing in a way that does not + // leave errors. + its_merge_vertices(ret); + its_remove_degenerate_faces(ret); + its_compactify_vertices(ret); + return ret; } void slices_to_mesh(indexed_triangle_set & mesh, - const std::vector &slices, - double zmin, - double lh, - double ilh) + const std::vector &slices, + double zmin, + double lh, + double ilh) { - std::vector wall_meshes(slices.size()); std::vector grid(slices.size(), zmin + ilh); - - for (size_t i = 1; i < grid.size(); ++i) - grid[i] = grid[i - 1] + lh; - + + for (size_t i = 1; i < grid.size(); ++i) grid[i] = grid[i - 1] + lh; + indexed_triangle_set cntr = slices_to_mesh(slices, zmin, grid); its_merge(mesh, cntr); } diff --git a/src/libslic3r/SlicesToTriangleMesh.hpp b/src/libslic3r/SlicesToTriangleMesh.hpp index 2fd1778851..57b540d9fb 100644 --- a/src/libslic3r/SlicesToTriangleMesh.hpp +++ b/src/libslic3r/SlicesToTriangleMesh.hpp @@ -7,10 +7,10 @@ namespace Slic3r { void slices_to_mesh(indexed_triangle_set & mesh, - const std::vector &slices, - double zmin, - double lh, - double ilh); + const std::vector &slices, + double zmin, + double lh, + double ilh); inline indexed_triangle_set slices_to_mesh( const std::vector &slices, double zmin, double lh, double ilh) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index fb2621225a..efd8e97f71 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -705,22 +705,16 @@ void its_flip_triangles(indexed_triangle_set &its) int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit) { - int last = 0; - for (int i = 0; i < int(its.indices.size()); ++ i) { - const stl_triangle_vertex_indices &face = its.indices[i]; - if (face(0) != face(1) && face(0) != face(2) && face(1) != face(2)) { - if (last < i) - its.indices[last] = its.indices[i]; - ++ last; - } - } - int removed = int(its.indices.size()) - last; - if (removed) { - its.indices.erase(its.indices.begin() + last, its.indices.end()); - // Optionally shrink the vertices. - if (shrink_to_fit) - its.indices.shrink_to_fit(); - } + auto it = std::remove_if(its.indices.begin(), its.indices.end(), [](auto &face) { + return face(0) == face(1) || face(0) == face(2) || face(1) == face(2); + }); + + int removed = std::distance(it, its.indices.end()); + its.indices.erase(it, its.indices.end()); + + if (removed && shrink_to_fit) + its.indices.shrink_to_fit(); + return removed; } diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index 287b2e88be..0d42cec2d3 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -236,7 +236,7 @@ void SLAImportJob::finalize() if (!p->mesh.empty()) { bool is_centered = false; - p->plater->sidebar().obj_list()->load_mesh_object(TriangleMesh{p->mesh}, + p->plater->sidebar().obj_list()->load_mesh_object(TriangleMesh{std::move(p->mesh)}, name, is_centered); } From 535191c1e93015386929cc8f0b20405523057d61 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 5 Nov 2021 15:58:40 +0100 Subject: [PATCH 05/16] Verify layerHeight conf value before use --- src/libslic3r/Format/SL1.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index aef8038d69..a8f920ae24 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -317,10 +317,16 @@ ConfigSubstitutions import_sla_archive( if (profile_in.empty()) { // missing profile... do guess work // try to recover the layer height from the config.ini which was // present in all versions of sl1 files. - auto lh_str = arch.config.find("layerHeight")->second.data(); - double lh = std::stod(lh_str); // TODO replace with std::from_chars - profile_out.set("layer_height", lh); - profile_out.set("initial_layer_height", lh); + if (auto lh_opt = arch.config.find("layerHeight"); + lh_opt != arch.config.not_found()) + { + auto lh_str = lh_opt->second.data(); + try { + double lh = std::stod(lh_str); // TODO replace with std::from_chars + profile_out.set("layer_height", lh); + profile_out.set("initial_layer_height", lh); + } catch(...) {} + } } // If the archive contains an empty profile, use the one that was passed as output argument From 2fcab52f86a40e37d7a9546d31f09a3e92c4a4b6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 5 Nov 2021 17:19:57 +0100 Subject: [PATCH 06/16] Added "Color" parameter for SLA material --- src/libslic3r/Preset.cpp | 1 + src/libslic3r/PrintConfig.cpp | 7 +++++ src/slic3r/GUI/3DScene.cpp | 54 +++++++++++++++++++++++------------ src/slic3r/GUI/Plater.cpp | 11 ++++++- src/slic3r/GUI/Tab.cpp | 7 +++++ 5 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 09cd384687..0487ddaec7 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -534,6 +534,7 @@ static std::vector s_Preset_sla_print_options { }; static std::vector s_Preset_sla_material_options { + "material_colour", "material_type", "initial_layer_height", "bottle_cost", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 232e724e76..e165c731ff 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3163,6 +3163,13 @@ void PrintConfigDef::init_sla_params() // SLA Material settings. + + def = this->add("material_colour", coStrings); + def->label = L("Color"); + def->tooltip = L("This is only used in the Slic3r interface as a visual help."); + def->gui_type = ConfigOptionDef::GUIType::color; + def->set_default_value(new ConfigOptionStrings{ "#29B2B2" }); + def = this->add("material_type", coString); def->label = L("SLA material type"); def->tooltip = L("SLA material type"); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index ee28fd718f..6e7174baff 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1131,29 +1131,45 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con if (config == nullptr) return; - const ConfigOptionStrings* extruders_opt = dynamic_cast(config->option("extruder_colour")); - if (extruders_opt == nullptr) - return; - - const ConfigOptionStrings* filamemts_opt = dynamic_cast(config->option("filament_colour")); - if (filamemts_opt == nullptr) - return; - - unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size()); - if (colors_count == 0) - return; - - std::vector colors(colors_count); - unsigned char rgb[3]; - for (unsigned int i = 0; i < colors_count; ++i) { - const std::string& txt_color = config->opt_string("extruder_colour", i); + std::vector colors; + + if (static_cast(config->opt_int("printer_technology")) == ptSLA) + { + const ConfigOptionStrings* resin_clr = dynamic_cast(config->option("material_colour")); + if (resin_clr == nullptr) + return; + assert(resin_clr->values.size() == 1); + colors.resize(1); + + const std::string& txt_color = config->opt_string("material_colour", 0); if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) - colors[i].set(txt_color, rgb); - else { - const std::string& txt_color = config->opt_string("filament_colour", i); + colors[0].set(txt_color, rgb); + } + else + { + const ConfigOptionStrings* extruders_opt = dynamic_cast(config->option("extruder_colour")); + if (extruders_opt == nullptr) + return; + + const ConfigOptionStrings* filamemts_opt = dynamic_cast(config->option("filament_colour")); + if (filamemts_opt == nullptr) + return; + + unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size()); + if (colors_count == 0) + return; + colors.resize(colors_count); + + for (unsigned int i = 0; i < colors_count; ++i) { + const std::string& txt_color = config->opt_string("extruder_colour", i); if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) colors[i].set(txt_color, rgb); + else { + const std::string& txt_color = config->opt_string("filament_colour", i); + if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) + colors[i].set(txt_color, rgb); + } } } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2567ca52f0..fe8c72fea6 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1920,7 +1920,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance", "brim_width", "brim_separation", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", - "extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology", + "extruder_colour", "filament_colour", "material_colour", "max_print_height", "printer_model", "printer_technology", // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. "layer_height", "first_layer_height", "min_layer_height", "max_layer_height", "brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", @@ -6222,6 +6222,15 @@ void Plater::on_config_change(const DynamicPrintConfig &config) } } + if (opt_key == "material_colour") { + update_scheduled = true; // update should be scheduled (for update 3DScene) + + // update material color in full config + std::vector material_colors = { config.opt_string("material_colour", (unsigned)0) }; + p->config->option("material_colour")->values = material_colors; + continue; + } + p->config->set_key_value(opt_key, config.option(opt_key)->clone()); if (opt_key == "printer_technology") { this->set_printer_technology(config.opt_enum(opt_key)); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 1a96f4bafe..4880a3b7af 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4157,6 +4157,7 @@ void TabSLAMaterial::build() auto page = add_options_page(L("Material"), "resin"); auto optgroup = page->new_optgroup(L("Material")); + optgroup->append_single_option_line("material_colour"); optgroup->append_single_option_line("bottle_cost"); optgroup->append_single_option_line("bottle_volume"); optgroup->append_single_option_line("bottle_weight"); @@ -4164,6 +4165,12 @@ void TabSLAMaterial::build() optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) { + if (opt_key == "material_colour") { + update_dirty(); + on_value_change(opt_key, value); + return; + } + DynamicPrintConfig new_conf = *m_config; if (opt_key == "bottle_volume") { From 54b72fdaa9302e78030fba70fb9914bd71f1120e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 5 Nov 2021 22:52:13 +0100 Subject: [PATCH 07/16] Added a missing include (GCC 11.1). --- src/slic3r/GUI/MsgDialog.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp index 22a81579a6..909ff396d6 100644 --- a/src/slic3r/GUI/MsgDialog.hpp +++ b/src/slic3r/GUI/MsgDialog.hpp @@ -10,6 +10,7 @@ #include #include #include +#include class wxBoxSizer; class wxCheckBox; From b572588fc5b646b7f1cb80f6fde41856f1070f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 5 Nov 2021 10:07:21 +0100 Subject: [PATCH 08/16] Small refactoring of storing colored polygons in multi-material segmentation. Previously, colored polygons were stored so that each polygon had a color assigned to it, which made it difficult to perform operations like union or so on all polygons of the same color. Now polygons are stored grouped by their assigned color/extruder. --- src/libslic3r/MultiMaterialSegmentation.cpp | 133 ++++++++++---------- src/libslic3r/MultiMaterialSegmentation.hpp | 2 +- src/libslic3r/PrintObjectSlice.cpp | 6 +- 3 files changed, 70 insertions(+), 71 deletions(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 11e2632994..ddc17713b9 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1113,7 +1113,7 @@ static inline Polygon to_polygon(const std::vector &lines) // It iterates through all nodes on the border between two different colors, and from this point, // start selection always left most edges for every node to construct CCW polygons. // Assumes that graph is planar (without self-intersection edges) -static std::vector> extract_colored_segments(const MMU_Graph &graph) +static std::vector extract_colored_segments(const MMU_Graph &graph, const size_t num_extruders) { std::vector used_arcs(graph.arcs.size(), false); // When there is no next arc, then is returned original_arc or edge with is marked as used @@ -1153,7 +1153,7 @@ static std::vector> extract_colored_segments(const MM return std::all_of(node.arc_idxs.cbegin(), node.arc_idxs.cend(), [&used_arcs](const size_t &arc_idx) -> bool { return used_arcs[arc_idx]; }); }; - std::vector> polygons_segments; + std::vector expolygons_segments(num_extruders + 1); for (size_t node_idx = 0; node_idx < graph.all_border_points; ++node_idx) { const MMU_Graph::Node &node = graph.nodes[node_idx]; @@ -1183,12 +1183,11 @@ static std::vector> extract_colored_segments(const MM p_arc = &next; } while (graph.nodes[p_arc->to_idx].point != start_p || !all_arc_used(graph.nodes[p_arc->to_idx])); - Polygon poly = to_polygon(face_lines); - if (poly.is_counter_clockwise() && poly.is_valid()) - polygons_segments.emplace_back(poly, arc.color); + if (Polygon poly = to_polygon(face_lines); poly.is_counter_clockwise() && poly.is_valid()) + expolygons_segments[arc.color].emplace_back(std::move(poly)); } } - return polygons_segments; + return expolygons_segments; } // Used in remove_multiple_edges_in_vertices() @@ -1269,21 +1268,20 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto } } -static void cut_segmented_layers(const std::vector &input_expolygons, - std::vector>> &segmented_regions, - const float cut_width, - const std::function &throw_on_cancel_callback) +static void cut_segmented_layers(const std::vector &input_expolygons, + std::vector> &segmented_regions, + const float cut_width, + const std::function &throw_on_cancel_callback) { BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - cutting segmented layers in parallel - begin"; tbb::parallel_for(tbb::blocked_range(0, segmented_regions.size()),[&segmented_regions, &input_expolygons, &cut_width, &throw_on_cancel_callback](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { throw_on_cancel_callback(); - std::vector> segmented_regions_cuts; - for (const std::pair &colored_expoly : segmented_regions[layer_idx]) { - ExPolygons cut_colored_expoly = diff_ex(colored_expoly.first, offset_ex(input_expolygons[layer_idx], cut_width)); - for (ExPolygon &expoly : cut_colored_expoly) - segmented_regions_cuts.emplace_back(std::move(expoly), colored_expoly.second); - } + const size_t num_extruders_plus_one = segmented_regions[layer_idx].size(); + std::vector segmented_regions_cuts(num_extruders_plus_one); // Indexed by extruder_id + for (size_t extruder_idx = 0; extruder_idx < num_extruders_plus_one; ++extruder_idx) + if (const ExPolygons &ex_polygons = segmented_regions[layer_idx][extruder_idx]; !ex_polygons.empty()) + segmented_regions_cuts[extruder_idx] = diff_ex(ex_polygons, offset_ex(input_expolygons[layer_idx], cut_width)); segmented_regions[layer_idx] = std::move(segmented_regions_cuts); } }); // end of parallel_for @@ -1323,7 +1321,7 @@ static inline std::vector> mmu_segmentation_top_and_bott // Project upwards pointing painted triangles over top surfaces, // project downards pointing painted triangles over bottom surfaces. std::vector> top_raw(num_extruders), bottom_raw(num_extruders); - std::vector zs = zs_from_layers(print_object.layers()); + std::vector zs = zs_from_layers(layers); Transform3d object_trafo = print_object.trafo_centered(); #ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM @@ -1532,31 +1530,35 @@ static inline std::vector> mmu_segmentation_top_and_bott return triangles_by_color_merged; } -static std::vector>> merge_segmented_layers( - const std::vector>> &segmented_regions, - std::vector> &&top_and_bottom_layers, - const std::function &throw_on_cancel_callback) +static std::vector> merge_segmented_layers( + const std::vector> &segmented_regions, + std::vector> &&top_and_bottom_layers, + const size_t num_extruders, + const std::function &throw_on_cancel_callback) { - std::vector>> segmented_regions_merged(segmented_regions.size()); + const size_t num_layers = segmented_regions.size(); + std::vector> segmented_regions_merged(num_layers); + segmented_regions_merged.assign(num_layers, std::vector(num_extruders)); + assert(num_extruders + 1 == top_and_bottom_layers.size()); BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging segmented layers in parallel - begin"; - tbb::parallel_for(tbb::blocked_range(0, segmented_regions.size()), [&segmented_regions, &top_and_bottom_layers, &segmented_regions_merged, &throw_on_cancel_callback](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(0, num_layers), [&segmented_regions, &top_and_bottom_layers, &segmented_regions_merged, &num_extruders, &throw_on_cancel_callback](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { - for (const std::pair &colored_expoly : segmented_regions[layer_idx]) { + assert(segmented_regions[layer_idx].size() == num_extruders + 1); + // Zero is skipped because it is the default color of the volume + for (size_t extruder_id = 1; extruder_id < num_extruders + 1; ++extruder_id) { throw_on_cancel_callback(); - // Zero is the default color of the volume. - if(colored_expoly.second == 0) - continue; - ExPolygons cut_colored_expoly = {colored_expoly.first}; - for (const std::vector &top_and_bottom_layer : top_and_bottom_layers) - cut_colored_expoly = diff_ex(cut_colored_expoly, top_and_bottom_layer[layer_idx]); - for (ExPolygon &ex_poly : cut_colored_expoly) - segmented_regions_merged[layer_idx].emplace_back(std::move(ex_poly), colored_expoly.second - 1); - } + if (!segmented_regions[layer_idx][extruder_id].empty()) { + ExPolygons segmented_regions_trimmed = segmented_regions[layer_idx][extruder_id]; + for (const std::vector &top_and_bottom_by_extruder : top_and_bottom_layers) + if (!top_and_bottom_by_extruder[layer_idx].empty() && !segmented_regions_trimmed.empty()) + segmented_regions_trimmed = diff_ex(segmented_regions_trimmed, top_and_bottom_by_extruder[layer_idx]); - for (size_t color_idx = 1; color_idx < top_and_bottom_layers.size(); ++color_idx) - for (ExPolygon &expoly : top_and_bottom_layers[color_idx][layer_idx]) - segmented_regions_merged[layer_idx].emplace_back(std::move(expoly), color_idx - 1); + segmented_regions_merged[layer_idx][extruder_id - 1] = std::move(segmented_regions_trimmed); + } + + append(segmented_regions_merged[layer_idx][extruder_id - 1], top_and_bottom_layers[extruder_id][layer_idx]); + } } }); // end of parallel_for BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging segmented layers in parallel - end"; @@ -1565,7 +1567,7 @@ static std::vector>> merge_segmented_la } #ifdef MMU_SEGMENTATION_DEBUG_REGIONS -static void export_regions_to_svg(const std::string &path, const std::vector> ®ions, const ExPolygons &lslices) +static void export_regions_to_svg(const std::string &path, const std::vector ®ions, const ExPolygons &lslices) { const std::vector colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "yellow"}; coordf_t stroke_width = scale_(0.05); @@ -1574,12 +1576,12 @@ static void export_regions_to_svg(const std::string &path, const std::vector ®ion : regions) { - int region_color = int(region.second); - if (region_color >= 0 && region_color < int(colors.size())) - svg.draw(region.first, colors[region_color]); + for (const ExPolygons &by_extruder : regions) { + size_t extrude_idx = &by_extruder - ®ions.front(); + if (extrude_idx >= 0 && extrude_idx < int(colors.size())) + svg.draw(by_extruder, colors[extrude_idx], stroke_width); else - svg.draw(region.first, "black"); + svg.draw(by_extruder, "black", stroke_width); } } #endif // MMU_SEGMENTATION_DEBUG_REGIONS @@ -1667,20 +1669,23 @@ static bool has_layer_only_one_color(const std::vector> return true; } -std::vector>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function &throw_on_cancel_callback) +std::vector> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function &throw_on_cancel_callback) { - std::vector>> segmented_regions(print_object.layers().size()); - std::vector> painted_lines(print_object.layers().size()); - std::array painted_lines_mutex; - std::vector edge_grids(print_object.layers().size()); - const ConstLayerPtrsAdaptor layers = print_object.layers(); - std::vector input_expolygons(layers.size()); + const size_t num_extruders = print_object.print()->config().nozzle_diameter.size(); + const size_t num_layers = print_object.layers().size(); + std::vector> segmented_regions(num_layers); + segmented_regions.assign(num_layers, std::vector(num_extruders + 1)); + std::vector> painted_lines(num_layers); + std::array painted_lines_mutex; + std::vector edge_grids(num_layers); + const ConstLayerPtrsAdaptor layers = print_object.layers(); + std::vector input_expolygons(num_layers); throw_on_cancel_callback(); // Merge all regions and remove small holes BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - slices preparation in parallel - begin"; - tbb::parallel_for(tbb::blocked_range(0, layers.size()), [&layers, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(0, num_layers), [&layers, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { throw_on_cancel_callback(); ExPolygons ex_polygons; @@ -1711,7 +1716,7 @@ std::vector>> multi_material_segmentati }); // end of parallel_for BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - slices preparation in parallel - end"; - for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { + for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) { throw_on_cancel_callback(); BoundingBox bbox(get_extents(layers[layer_idx]->regions())); bbox.merge(get_extents(input_expolygons[layer_idx])); @@ -1723,8 +1728,7 @@ std::vector>> multi_material_segmentati BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - projection of painted triangles - begin"; for (const ModelVolume *mv : print_object.model_object()->volumes) { - const size_t num_extruders = print_object.print()->config().nozzle_diameter.size() + 1; - tbb::parallel_for(tbb::blocked_range(1, num_extruders), [&mv, &print_object, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(1, num_extruders + 1), [&mv, &print_object, &layers, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range &range) { for (size_t extruder_idx = range.begin(); extruder_idx < range.end(); ++extruder_idx) { throw_on_cancel_callback(); const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx)); @@ -1732,7 +1736,7 @@ std::vector>> multi_material_segmentati continue; const Transform3f tr = print_object.trafo().cast() * mv->get_matrix().cast(); - tbb::parallel_for(tbb::blocked_range(0, custom_facets.indices.size()), [&tr, &custom_facets, &print_object, &edge_grids, &input_expolygons, &painted_lines, &painted_lines_mutex, &extruder_idx](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(0, custom_facets.indices.size()), [&tr, &custom_facets, &print_object, &layers, &edge_grids, &input_expolygons, &painted_lines, &painted_lines_mutex, &extruder_idx](const tbb::blocked_range &range) { for (size_t facet_idx = range.begin(); facet_idx < range.end(); ++facet_idx) { float min_z = std::numeric_limits::max(); float max_z = std::numeric_limits::lowest(); @@ -1748,15 +1752,15 @@ std::vector>> multi_material_segmentati std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); }); // Find lowest slice not below the triangle. - auto first_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(min_z - EPSILON), + auto first_layer = std::upper_bound(layers.begin(), layers.end(), float(min_z - EPSILON), [](float z, const Layer *l1) { return z < l1->slice_z; }); - auto last_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(max_z + EPSILON), + auto last_layer = std::upper_bound(layers.begin(), layers.end(), float(max_z + EPSILON), [](float z, const Layer *l1) { return z < l1->slice_z; }); --last_layer; for (auto layer_it = first_layer; layer_it != (last_layer + 1); ++layer_it) { const Layer *layer = *layer_it; - size_t layer_idx = layer_it - print_object.layers().begin(); + size_t layer_idx = layer_it - layers.begin(); if (input_expolygons[layer_idx].empty() || facet[0].z() > layer->slice_z || layer->slice_z > facet[2].z()) continue; @@ -1799,7 +1803,7 @@ std::vector>> multi_material_segmentati << std::count_if(painted_lines.begin(), painted_lines.end(), [](const std::vector &pl) { return !pl.empty(); }); BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - layers segmentation in parallel - begin"; - tbb::parallel_for(tbb::blocked_range(0, print_object.layers().size()), [&edge_grids, &input_expolygons, &painted_lines, &segmented_regions, &throw_on_cancel_callback](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(0, num_layers), [&edge_grids, &input_expolygons, &painted_lines, &segmented_regions, &num_extruders, &throw_on_cancel_callback](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { throw_on_cancel_callback(); if (!painted_lines[layer_idx].empty()) { @@ -1832,8 +1836,7 @@ std::vector>> multi_material_segmentati assert(!color_poly.front().empty()); if (has_layer_only_one_color(color_poly)) { // If the whole layer is painted using the same color, it is not needed to construct a Voronoi diagram for the segmentation of this layer. - for (const ExPolygon &ex_polygon : input_expolygons[layer_idx]) - segmented_regions[layer_idx].emplace_back(ex_polygon, size_t(color_poly.front().front().color)); + segmented_regions[layer_idx][size_t(color_poly.front().front().color)] = input_expolygons[layer_idx]; } else { MMU_Graph graph = build_graph(layer_idx, color_poly); remove_multiple_edges_in_vertices(graph, color_poly); @@ -1846,9 +1849,7 @@ std::vector>> multi_material_segmentati } #endif // MMU_SEGMENTATION_DEBUG_GRAPH - std::vector> segmentation = extract_colored_segments(graph); - for (std::pair ®ion : segmentation) - segmented_regions[layer_idx].emplace_back(std::move(region)); + segmented_regions[layer_idx] = extract_colored_segments(graph, num_extruders); } #ifdef MMU_SEGMENTATION_DEBUG_REGIONS @@ -1868,11 +1869,11 @@ std::vector>> multi_material_segmentati throw_on_cancel_callback(); } -// return segmented_regions; - std::vector> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object, input_expolygons, throw_on_cancel_callback); + // The first index is extruder number (includes default extruder), and the second one is layer number + std::vector> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object, input_expolygons, throw_on_cancel_callback); throw_on_cancel_callback(); - std::vector>> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers), throw_on_cancel_callback); + std::vector> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers), num_extruders, throw_on_cancel_callback); throw_on_cancel_callback(); #ifdef MMU_SEGMENTATION_DEBUG_REGIONS diff --git a/src/libslic3r/MultiMaterialSegmentation.hpp b/src/libslic3r/MultiMaterialSegmentation.hpp index 07767111a8..4efdc69514 100644 --- a/src/libslic3r/MultiMaterialSegmentation.hpp +++ b/src/libslic3r/MultiMaterialSegmentation.hpp @@ -11,7 +11,7 @@ class PrintObject; class ExPolygon; // Returns MMU segmentation based on painting in MMU segmentation gizmo -std::vector>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function &throw_on_cancel_callback); +std::vector> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function &throw_on_cancel_callback); } // namespace Slic3r diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index e2844a624c..a8936c32e3 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -538,7 +538,7 @@ template static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_cancel) { // Returns MMU segmentation based on painting in MMU segmentation gizmo - std::vector>> segmentation = multi_material_segmentation_by_painting(print_object, throw_on_cancel); + std::vector> segmentation = multi_material_segmentation_by_painting(print_object, throw_on_cancel); assert(segmentation.size() == print_object.layer_count()); tbb::parallel_for( tbb::blocked_range(0, segmentation.size(), std::max(segmentation.size() / 128, size_t(1))), @@ -568,9 +568,7 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance bool layer_split = false; for (size_t extruder_id = 0; extruder_id < num_extruders; ++ extruder_id) { ByExtruder ®ion = by_extruder[extruder_id]; - for (const std::pair &colored_polygon : segmentation[layer_id]) - if (colored_polygon.second == extruder_id) - region.expolygons.emplace_back(std::move(colored_polygon.first)); + append(region.expolygons, std::move(segmentation[layer_id][extruder_id])); if (! region.expolygons.empty()) { region.bbox = get_extents(region.expolygons); layer_split = true; From f5a6e532982b42c36f3a39b02353c5a44325326f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 5 Nov 2021 10:12:40 +0100 Subject: [PATCH 09/16] Fix of #7235 (Dimples in external perimeter after multi-material segmentation) --- src/libslic3r/MultiMaterialSegmentation.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index ddc17713b9..ba758d8a29 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1557,7 +1557,14 @@ static std::vector> merge_segmented_layers( segmented_regions_merged[layer_idx][extruder_id - 1] = std::move(segmented_regions_trimmed); } - append(segmented_regions_merged[layer_idx][extruder_id - 1], top_and_bottom_layers[extruder_id][layer_idx]); + if (!top_and_bottom_layers[extruder_id][layer_idx].empty()) { + bool was_top_and_bottom_empty = segmented_regions_merged[layer_idx][extruder_id - 1].empty(); + append(segmented_regions_merged[layer_idx][extruder_id - 1], top_and_bottom_layers[extruder_id][layer_idx]); + + // Remove dimples (#7235) appearing after merging side segmentation of the model with tops and bottoms painted layers. + if (!was_top_and_bottom_empty) + segmented_regions_merged[layer_idx][extruder_id - 1] = offset2_ex(union_ex(segmented_regions_merged[layer_idx][extruder_id - 1]), SCALED_EPSILON, -SCALED_EPSILON); + } } } }); // end of parallel_for From e13be902836e52b0f859be00dbe39fd96d5b035a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 5 Nov 2021 10:18:11 +0100 Subject: [PATCH 10/16] Fix of #7109 (Bulges with model's base color on multi-material painted models) --- src/libslic3r/PrintObjectSlice.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index a8936c32e3..84b2129389 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -630,6 +630,13 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance if (mine.empty()) break; } + // Filter out unprintable polygons produced by subtraction multi-material painted regions from layerm.region(). + // ExPolygon returned from multi-material segmentation does not precisely match ExPolygons in layerm.region() + // (because of preprocessing of the input regions in multi-material segmentation). Therefore, subtraction from + // layerm.region() could produce a huge number of small unprintable regions for the model's base extruder. + // This could, on some models, produce bulges with the model's base color (#7109). + if (! mine.empty()) + mine = opening(union_ex(mine), float(scale_(5 * EPSILON)), float(scale_(5 * EPSILON))); if (! mine.empty()) { ByRegion &dst = by_region[layerm.region().print_object_region_id()]; if (dst.expolygons.empty()) { From 69b7fac01f838bad38141b5f7ea8b6bdd5693dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 5 Nov 2021 23:19:01 +0100 Subject: [PATCH 11/16] Fixed a compiler warning. --- src/libslic3r/MultiMaterialSegmentation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index ba758d8a29..7041f7b825 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1563,7 +1563,7 @@ static std::vector> merge_segmented_layers( // Remove dimples (#7235) appearing after merging side segmentation of the model with tops and bottoms painted layers. if (!was_top_and_bottom_empty) - segmented_regions_merged[layer_idx][extruder_id - 1] = offset2_ex(union_ex(segmented_regions_merged[layer_idx][extruder_id - 1]), SCALED_EPSILON, -SCALED_EPSILON); + segmented_regions_merged[layer_idx][extruder_id - 1] = offset2_ex(union_ex(segmented_regions_merged[layer_idx][extruder_id - 1]), float(SCALED_EPSILON), -float(SCALED_EPSILON)); } } } From c782c2d389ccbc67d0d63ebad37896d6fa20c0d9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 8 Nov 2021 08:38:37 +0100 Subject: [PATCH 12/16] #7245 - Added validation of values typed by user in scale and size fields of object/part manipulator in sidebar --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 30 ++++++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index c279fad903..f5a274d69d 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -866,6 +866,9 @@ void ObjectManipulation::change_rotation_value(int axis, double value) void ObjectManipulation::change_scale_value(int axis, double value) { + if (value <= 0.0) + return; + if (std::abs(m_cache.scale_rounded(axis) - value) < EPSILON) return; @@ -882,6 +885,9 @@ void ObjectManipulation::change_scale_value(int axis, double value) void ObjectManipulation::change_size_value(int axis, double value) { + if (value <= 0.0) + return; + if (std::abs(m_cache.size_rounded(axis) - value) < EPSILON) return; @@ -947,10 +953,26 @@ void ObjectManipulation::on_change(const std::string& opt_key, int axis, double change_position_value(axis, new_value); else if (opt_key == "rotation") change_rotation_value(axis, new_value); - else if (opt_key == "scale") - change_scale_value(axis, new_value); - else if (opt_key == "size") - change_size_value(axis, new_value); + else if (opt_key == "scale") { + if (new_value > 0.0) + change_scale_value(axis, new_value); + else { + new_value = m_cache.scale(axis); + m_cache.scale(axis) = 0.0; + m_cache.scale_rounded(axis) = DBL_MAX; + change_scale_value(axis, new_value); + } + } + else if (opt_key == "size") { + if (new_value > 0.0) + change_size_value(axis, new_value); + else { + new_value = m_cache.size(axis); + m_cache.size(axis) = 0.0; + m_cache.size_rounded(axis) = DBL_MAX; + change_size_value(axis, new_value); + } + } } void ObjectManipulation::set_uniform_scaling(const bool new_value) From 24d13031268aaa0f617657521af8c22690057191 Mon Sep 17 00:00:00 2001 From: combolek <4743344+combolek@users.noreply.github.com> Date: Sat, 6 Nov 2021 12:39:52 -0700 Subject: [PATCH 13/16] =?UTF-8?q?Fixes=20issue=20#7247:=20Wxwidgets=203.0?= =?UTF-8?q?=20yet=20again.=20MsgDialog.hpp:270:40:=20error:=20=E2=80=98wxA?= =?UTF-8?q?SCII=5FSTR=E2=80=99=20was=20not=20declared=20in=20this=20scope?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace wxASCII_STR with wxString::FromAscii for compatibility with wxWidgets 3.0 --- src/slic3r/GUI/MsgDialog.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp index 909ff396d6..a4807fd06d 100644 --- a/src/slic3r/GUI/MsgDialog.hpp +++ b/src/slic3r/GUI/MsgDialog.hpp @@ -98,7 +98,7 @@ public: const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxLI_HORIZONTAL, - const wxString& name = wxASCII_STR(wxTextCtrlNameStr)) + const wxString& name = wxString::FromAscii(wxTextCtrlNameStr)) : wxTextCtrl(parent, id, wxEmptyString, pos, size!=wxDefaultSize ? size : (style == wxLI_HORIZONTAL ? wxSize(10, 1) : wxSize(1, 10)), wxSIMPLE_BORDER, wxDefaultValidator, name) { this->Enable(false); @@ -268,7 +268,7 @@ public: const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxLI_HORIZONTAL, - const wxString& name = wxASCII_STR(wxStaticLineNameStr)) + const wxString& name = wxString::FromAscii(wxStaticLineNameStr)) : wxStaticLine(parent, id, pos, size, style, name) {} ~StaticLine() {} }; From 8bc074f5f2c9653293af68b2b78073056ff6267a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 8 Nov 2021 11:06:37 +0100 Subject: [PATCH 14/16] MSW: Dark Mode: Update of PrintHostDialogs --- src/slic3r/GUI/GUI_App.cpp | 1 + src/slic3r/GUI/PrintHostDialogs.cpp | 10 ++++++++++ src/slic3r/GUI/PrintHostDialogs.hpp | 1 + 3 files changed, 12 insertions(+) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 025614d4d2..9c16a782e3 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1620,6 +1620,7 @@ void GUI_App::update_ui_from_settings() m_force_colors_update = false; mainframe->force_color_changed(); mainframe->diff_dialog.force_color_changed(); + mainframe->printhost_queue_dlg()->force_color_changed(); #ifdef _MSW_DARK_MODE update_scrolls(mainframe); #endif //_MSW_DARK_MODE diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 3f2dc5a447..4fd45f380a 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -101,6 +101,8 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr EndDialog(wxID_OK); }); + wxGetApp().UpdateDlgDarkUI(this); + Fit(); CenterOnParent(); @@ -331,6 +333,14 @@ void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect) save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS); } +void PrintHostQueueDialog::on_sys_color_changed() +{ +#ifdef _WIN32 + wxGetApp().UpdateDlgDarkUI(this); + wxGetApp().UpdateDVCDarkUI(job_list); +#endif +} + PrintHostQueueDialog::JobState PrintHostQueueDialog::get_state(int idx) { wxCHECK_MSG(idx >= 0 && idx < job_list->GetItemCount(), ST_ERROR, "Out of bounds access to job list"); diff --git a/src/slic3r/GUI/PrintHostDialogs.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp index 294593bd1c..eb56611590 100644 --- a/src/slic3r/GUI/PrintHostDialogs.hpp +++ b/src/slic3r/GUI/PrintHostDialogs.hpp @@ -72,6 +72,7 @@ public: } protected: void on_dpi_changed(const wxRect &suggested_rect) override; + void on_sys_color_changed() override; private: enum Column { From ce1fbbfa7c1867f651eb26aba9ea403e0b43c365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 8 Nov 2021 11:13:07 +0100 Subject: [PATCH 15/16] OSX specific: Fix of #7259 - (Darker colors of objects inside the multi-material gizmo on macOS Monterey on Arm64 CPU) Since macOS 12 (Monterey), the issue with the opposite direction on macOS running on Arm64 CPU seems to be fixed, and computed triangle normals inside fragment shader using dFdx and dFdy have the right direction. So the previous patch bad51cdb520919362100b4381b9bb03cd1830643 is constrained to just macOS versions before macOS 12 (Monterey). --- src/slic3r/GUI/GLShadersManager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 0214652b20..01407b09a6 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -79,7 +79,9 @@ std::pair GLShadersManager::init() // For Apple's on Arm CPU computed triangle normals inside fragment shader using dFdx and dFdy has the opposite direction. // Because of this, objects had darker colors inside the multi-material gizmo. // Based on https://stackoverflow.com/a/66206648, the similar behavior was also spotted on some other devices with Arm CPU. - if (platform_flavor() == PlatformFlavor::OSXOnArm) + // Since macOS 12 (Monterey), this issue with the opposite direction on Apple's Arm CPU seems to be fixed, and computed + // triangle normals inside fragment shader have the right direction. + if (platform_flavor() == PlatformFlavor::OSXOnArm && wxPlatformInfo::Get().GetOSMajorVersion() < 12) valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"}, {"FLIP_TRIANGLE_NORMALS"sv}); else valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"}); From 5190a1f425e9ab8bb2e53ed03cff69c7c9bf7227 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 8 Nov 2021 12:56:38 +0100 Subject: [PATCH 16/16] #7258 - Fixed cut of merged object --- src/libslic3r/Model.cpp | 45 +++++++++++++++---------------- src/slic3r/GUI/GUI_ObjectList.cpp | 39 +++++++++++++-------------- 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 8531ab07e0..81627347b0 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1263,10 +1263,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr instances[instance]->get_mirror() ); - z -= instances[instance]->get_offset()(2); + z -= instances[instance]->get_offset().z(); - // Lower part per-instance bounding boxes - std::vector lower_bboxes { instances.size() }; + // Displacement (in instance coordinates) to be applied to place the upper parts + Vec3d local_displace = Vec3d::Zero(); for (ModelVolume *volume : volumes) { const auto volume_matrix = volume->get_matrix(); @@ -1286,8 +1286,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr if (attributes.has(ModelObjectCutAttribute::KeepLower)) lower->add_volume(*volume); } - else if (! volume->mesh().empty()) { - + else if (! volume->mesh().empty()) { // Transform the mesh by the combined transformation matrix. // Flip the triangles in case the composite transformation is left handed. TriangleMesh mesh(volume->mesh()); @@ -1327,13 +1326,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr assert(vol->config.id() != volume->config.id()); vol->set_material(volume->material_id(), *volume->material()); - // Compute the lower part instances' bounding boxes to figure out where to place - // the upper part - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - for (size_t i = 0; i < instances.size(); i++) { - lower_bboxes[i].merge(instances[i]->transform_mesh_bounding_box(lower_mesh, true)); - } - } + // Compute the displacement (in instance coordinates) to be applied to place the upper parts + // The upper part displacement is set to half of the lower part bounding box + // this is done in hope at least a part of the upper part will always be visible and draggable + local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0)); } } } @@ -1341,17 +1337,18 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr ModelObjectPtrs res; if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) { - upper->invalidate_bounding_box(); - upper->center_around_origin(); + if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { + upper->center_around_origin(); + upper->translate_instances(-upper->origin_translation); + upper->origin_translation = Vec3d::Zero(); + } // Reset instance transformation except offset and Z-rotation - for (size_t i = 0; i < instances.size(); i++) { + for (size_t i = 0; i < instances.size(); ++i) { auto &instance = upper->instances[i]; const Vec3d offset = instance->get_offset(); - const double rot_z = instance->get_rotation()(2); - // The upper part displacement is set to half of the lower part bounding box - // this is done in hope at least a part of the upper part will always be visible and draggable - const Vec3d displace = lower_bboxes[i].size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0)); + const double rot_z = instance->get_rotation().z(); + const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), instance->get_rotation()) * local_displace; instance->set_transformation(Geometry::Transformation()); instance->set_offset(offset + displace); @@ -1361,14 +1358,16 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr res.push_back(upper); } if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) { - lower->invalidate_bounding_box(); - lower->center_around_origin(); + if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { + lower->center_around_origin(); + lower->translate_instances(-lower->origin_translation); + lower->origin_translation = Vec3d::Zero(); + } // Reset instance transformation except offset and Z-rotation for (auto *instance : lower->instances) { const Vec3d offset = instance->get_offset(); - const double rot_z = instance->get_rotation()(2); - + const double rot_z = instance->get_rotation().z(); instance->set_transformation(Geometry::Transformation()); instance->set_offset(offset); instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, rot_z)); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 8a24bee2eb..7674fe9085 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2044,8 +2044,7 @@ void ObjectList::split() void ObjectList::merge(bool to_multipart_object) { // merge selected objects to the multipart object - if (to_multipart_object) - { + if (to_multipart_object) { auto get_object_idxs = [this](std::vector& obj_idxs, wxDataViewItemArray& sels) { // check selections and split instances to the separated objects... @@ -2056,8 +2055,7 @@ void ObjectList::merge(bool to_multipart_object) break; } - if (!instance_selection) - { + if (!instance_selection) { for (wxDataViewItem item : sels) { assert(m_objects_model->GetItemType(item) & itObject); obj_idxs.emplace_back(m_objects_model->GetIdByItem(item)); @@ -2069,8 +2067,7 @@ void ObjectList::merge(bool to_multipart_object) std::map> sel_map; std::set empty_set; for (wxDataViewItem item : sels) { - if (m_objects_model->GetItemType(item) & itObject) - { + if (m_objects_model->GetItemType(item) & itObject) { int obj_idx = m_objects_model->GetIdByItem(item); int inst_cnt = (*m_objects)[obj_idx]->instances.size(); if (inst_cnt == 1) @@ -2087,8 +2084,7 @@ void ObjectList::merge(bool to_multipart_object) // all objects, created from the instances will be added to the end of list int new_objects_cnt = 0; // count of this new objects - for (auto map_item : sel_map) - { + for (auto map_item : sel_map) { int obj_idx = map_item.first; // object with just 1 instance if (map_item.second.empty()) { @@ -2148,37 +2144,36 @@ void ObjectList::merge(bool to_multipart_object) new_object->name = _u8L("Merged"); ModelConfig &config = new_object->config; - for (int obj_idx : obj_idxs) - { + for (int obj_idx : obj_idxs) { ModelObject* object = (*m_objects)[obj_idx]; const Geometry::Transformation& transformation = object->instances[0]->get_transformation(); - Vec3d scale = transformation.get_scaling_factor(); - Vec3d mirror = transformation.get_mirror(); - Vec3d rotation = transformation.get_rotation(); + const Vec3d scale = transformation.get_scaling_factor(); + const Vec3d mirror = transformation.get_mirror(); + const Vec3d rotation = transformation.get_rotation(); if (object->id() == (*m_objects)[obj_idxs.front()]->id()) new_object->add_instance(); - Transform3d volume_offset_correction = new_object->instances[0]->get_transformation().get_matrix().inverse() * transformation.get_matrix(); + const Transform3d& volume_offset_correction = transformation.get_matrix(); // merge volumes for (const ModelVolume* volume : object->volumes) { ModelVolume* new_volume = new_object->add_volume(*volume); //set rotation - Vec3d vol_rot = new_volume->get_rotation() + rotation; + const Vec3d vol_rot = new_volume->get_rotation() + rotation; new_volume->set_rotation(vol_rot); // set scale - Vec3d vol_sc_fact = new_volume->get_scaling_factor().cwiseProduct(scale); + const Vec3d vol_sc_fact = new_volume->get_scaling_factor().cwiseProduct(scale); new_volume->set_scaling_factor(vol_sc_fact); // set mirror - Vec3d vol_mirror = new_volume->get_mirror().cwiseProduct(mirror); + const Vec3d vol_mirror = new_volume->get_mirror().cwiseProduct(mirror); new_volume->set_mirror(vol_mirror); // set offset - Vec3d vol_offset = volume_offset_correction* new_volume->get_offset(); + const Vec3d vol_offset = volume_offset_correction* new_volume->get_offset(); new_volume->set_offset(vol_offset); } new_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1"); @@ -2211,6 +2206,11 @@ void ObjectList::merge(bool to_multipart_object) for (const auto& range : object->layer_config_ranges) new_object->layer_config_ranges.emplace(range); } + + new_object->center_around_origin(); + new_object->translate_instances(-new_object->origin_translation); + new_object->origin_translation = Vec3d::Zero(); + // remove selected objects remove(); @@ -2221,8 +2221,7 @@ void ObjectList::merge(bool to_multipart_object) } // merge all parts to the one single object // all part's settings will be lost - else - { + else { wxDataViewItem item = GetSelection(); if (!item) return;