diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 1f1284a9e..2ac8a1ff0 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -150,6 +150,8 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/ArrangeJob.cpp GUI/Jobs/RotoptimizeJob.hpp GUI/Jobs/RotoptimizeJob.cpp + GUI/Jobs/SLAImportJob.hpp + GUI/Jobs/SLAImportJob.cpp GUI/Jobs/ProgressIndicator.hpp GUI/ProgressStatusBar.hpp GUI/ProgressStatusBar.cpp diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp new file mode 100644 index 000000000..e791bf94e --- /dev/null +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -0,0 +1,226 @@ +#include "SLAImportJob.hpp" + +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/AppConfig.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/PresetBundle.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/Utils/SLAImport.hpp" + +#include +#include +#include +#include +#include + +namespace Slic3r { namespace GUI { + +enum class Sel { modelAndProfile, profileOnly, modelOnly}; + +class ImportDlg: public wxDialog { + wxFilePickerCtrl *m_filepicker; + wxComboBox *m_import_dropdown, *m_quality_dropdown; + +public: + ImportDlg(Plater *plater) + : wxDialog{plater, wxID_ANY, "Import SLA archive"} + { + auto szvert = new wxBoxSizer{wxVERTICAL}; + auto szfilepck = new wxBoxSizer{wxHORIZONTAL}; + + m_filepicker = new wxFilePickerCtrl(this, wxID_ANY, + from_u8(wxGetApp().app_config->get_last_dir()), _(L("Choose SLA archive:")), + "SL1 archive files (*.sl1, *.zip)|*.sl1;*.SL1;*.zip;*.ZIP", + wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE | wxFD_OPEN | wxFD_FILE_MUST_EXIST); + + szfilepck->Add(new wxStaticText(this, wxID_ANY, _(L("Import file: "))), 0, wxALIGN_CENTER); + szfilepck->Add(m_filepicker, 1); + szvert->Add(szfilepck, 0, wxALL | wxEXPAND, 5); + + auto szchoices = new wxBoxSizer{wxHORIZONTAL}; + + static const std::vector inp_choices = { + _(L("Import model and profile")), + _(L("Import profile only")), + _(L("Import model only")) + }; + + m_import_dropdown = new wxComboBox( + this, wxID_ANY, inp_choices[0], wxDefaultPosition, wxDefaultSize, + inp_choices.size(), inp_choices.data(), wxCB_READONLY | wxCB_DROPDOWN); + + szchoices->Add(m_import_dropdown); + szchoices->Add(new wxStaticText(this, wxID_ANY, _(L("Quality: "))), 0, wxALIGN_CENTER | wxALL, 5); + + static const std::vector qual_choices = { + _(L("Accurate")), + _(L("Balanced")), + _(L("Quick")) + }; + + m_quality_dropdown = new wxComboBox( + this, wxID_ANY, qual_choices[0], wxDefaultPosition, wxDefaultSize, + qual_choices.size(), qual_choices.data(), wxCB_READONLY | wxCB_DROPDOWN); + szchoices->Add(m_quality_dropdown); + + m_import_dropdown->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) { + if (get_selection() == Sel::profileOnly) + m_quality_dropdown->Disable(); + else m_quality_dropdown->Enable(); + }); + + szvert->Add(szchoices, 0, wxALL, 5); + szvert->AddStretchSpacer(1); + auto szbtn = new wxBoxSizer(wxHORIZONTAL); + szbtn->Add(new wxButton{this, wxID_CANCEL}); + szbtn->Add(new wxButton{this, wxID_OK}); + szvert->Add(szbtn, 0, wxALIGN_RIGHT | wxALL, 5); + + SetSizerAndFit(szvert); + } + + Sel get_selection() const + { + int sel = m_import_dropdown->GetSelection(); + return Sel(std::min(int(Sel::modelOnly), std::max(0, sel))); + } + + Vec2i get_marchsq_windowsize() const + { + enum { Accurate, Balanced, Fast}; + + switch(m_quality_dropdown->GetSelection()) + { + case Fast: return {8, 8}; + case Balanced: return {4, 4}; + default: + case Accurate: + return {2, 2}; + } + } + + wxString get_path() const + { + return m_filepicker->GetPath(); + } +}; + +class SLAImportJob::priv { +public: + Plater *plater; + + Sel sel = Sel::modelAndProfile; + + TriangleMesh mesh; + DynamicPrintConfig profile; + wxString path; + Vec2i win = {2, 2}; + std::string err; + + priv(Plater *plt): plater{plt} {} +}; + +SLAImportJob::SLAImportJob(std::shared_ptr pri, Plater *plater) + : Job{std::move(pri)}, p{std::make_unique(plater)} +{} + +SLAImportJob::~SLAImportJob() = default; + +void SLAImportJob::process() +{ + auto progr = [this](int s) { + if (s < 100) update_status(int(s), _(L("Importing SLA archive"))); + return !was_canceled(); + }; + + if (p->path.empty()) return; + + std::string path = p->path.ToUTF8().data(); + try { + switch (p->sel) { + case Sel::modelAndProfile: + import_sla_archive(path, p->win, p->mesh, p->profile, progr); + break; + case Sel::modelOnly: + import_sla_archive(path, p->win, p->mesh, progr); + break; + case Sel::profileOnly: + import_sla_archive(path, p->profile); + break; + } + + } catch (std::exception &ex) { + p->err = ex.what(); + } + + update_status(100, was_canceled() ? _(L("Importing canceled.")) : + _(L("Importing done."))); +} + +void SLAImportJob::reset() +{ + p->sel = Sel::modelAndProfile; + p->mesh = {}; + p->profile = {}; + p->win = {2, 2}; + p->path.Clear(); +} + +void SLAImportJob::prepare() +{ + reset(); + + ImportDlg dlg{p->plater}; + + if (dlg.ShowModal() == wxID_OK) { + auto path = dlg.get_path(); + auto nm = wxFileName(path); + p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : path.ToUTF8(); + p->sel = dlg.get_selection(); + p->win = dlg.get_marchsq_windowsize(); + } else { + p->path = ""; + } +} + +void SLAImportJob::finalize() +{ + // Ignore the arrange result if aborted. + if (was_canceled()) return; + + if (!p->err.empty()) { + show_error(p->plater, p->err); + p->err = ""; + return; + } + + std::string name = wxFileName(p->path).GetName().ToUTF8().data(); + + if (!p->profile.empty()) { + const ModelObjectPtrs& objects = p->plater->model().objects; + for (auto object : objects) + if (object->volumes.size() > 1) + { + Slic3r::GUI::show_info(nullptr, + _(L("You cannot load SLA project with a multi-part object on the bed")) + "\n\n" + + _(L("Please check your object list before preset changing.")), + _(L("Attention!")) ); + return; + } + + DynamicPrintConfig config = {}; + config.apply(SLAFullPrintConfig::defaults()); + config += std::move(p->profile); + + wxGetApp().preset_bundle->load_config_model(name, std::move(config)); + wxGetApp().load_current_presets(); + } + + if (!p->mesh.empty()) + p->plater->sidebar().obj_list()->load_mesh_object(p->mesh, name); + + reset(); +} + +}} diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.hpp b/src/slic3r/GUI/Jobs/SLAImportJob.hpp new file mode 100644 index 000000000..cff6cc899 --- /dev/null +++ b/src/slic3r/GUI/Jobs/SLAImportJob.hpp @@ -0,0 +1,31 @@ +#ifndef SLAIMPORTJOB_HPP +#define SLAIMPORTJOB_HPP + +#include "Job.hpp" + +namespace Slic3r { namespace GUI { + +class Plater; + +class SLAImportJob : public Job { + class priv; + + std::unique_ptr p; + +public: + SLAImportJob(std::shared_ptr pri, Plater *plater); + ~SLAImportJob(); + + void process() override; + + void reset(); + +protected: + void prepare() override; + + void finalize() override; +}; + +}} // namespace Slic3r::GUI + +#endif // SLAIMPORTJOB_HPP diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f9fe06a85..db9e7e59a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include "libslic3r/libslic3r.h" #include "libslic3r/Format/STL.hpp" @@ -64,6 +63,7 @@ #include "Tab.hpp" #include "Jobs/ArrangeJob.hpp" #include "Jobs/RotoptimizeJob.hpp" +#include "Jobs/SLAImportJob.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" #include "ProgressStatusBar.hpp" @@ -73,7 +73,6 @@ #include "../Utils/PrintHost.hpp" #include "../Utils/FixModelByWin10.hpp" #include "../Utils/UndoRedo.hpp" -#include "../Utils/SLAImport.hpp" #include "RemovableDriveManager.hpp" #if ENABLE_NON_STATIC_CANVAS_MANAGER #ifdef __APPLE__ @@ -1487,7 +1486,7 @@ struct Plater::priv class Jobs: public ExclusiveJobGroup { priv *m; - size_t m_arrange_id, m_rotoptimize_id; + size_t m_arrange_id, m_rotoptimize_id, m_sla_import_id; void before_start() override { m->background_process.stop(); } @@ -1496,6 +1495,7 @@ struct Plater::priv { m_arrange_id = add_job(std::make_unique(m->statusbar(), m->q)); m_rotoptimize_id = add_job(std::make_unique(m->statusbar(), m->q)); + m_sla_import_id = add_job(std::make_unique(m->statusbar(), m->q)); } void arrange() @@ -1510,6 +1510,12 @@ struct Plater::priv start(m_rotoptimize_id); } + void import_sla_arch() + { + m->take_snapshot(_(L("Import SLA archive"))); + start(m_sla_import_id); + } + } m_ui_jobs; bool delayed_scene_refresh; @@ -4255,19 +4261,7 @@ void Plater::add_model() void Plater::import_sl1_archive() { - wxFileDialog dlg(this, _(L("Choose SL1 archive:")), - from_u8(wxGetApp().app_config->get_last_dir()), "", - "SL1 archive files (*.sl1)|*.sl1;*.SL1;*.zip;*.ZIP", - wxFD_OPEN | wxFD_FILE_MUST_EXIST); - - if (dlg.ShowModal() == wxID_OK) { - try { - TriangleMesh mesh = import_model_from_sla_zip(dlg.GetPath()); - p->sidebar->obj_list()->load_mesh_object(mesh, wxFileName(dlg.GetPath()).GetName()); - } catch (std::exception &ex) { - show_error(this, ex.what()); - } - } + p->m_ui_jobs.import_sla_arch(); } void Plater::extract_config_from_project()