diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 922d153e4..1ddb7fa8b 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -117,8 +117,6 @@ add_library(libslic3r STATIC "${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h" Line.cpp Line.hpp - MeshBoolean.cpp - MeshBoolean.hpp Model.cpp Model.hpp Arrange.hpp @@ -220,6 +218,24 @@ add_library(libslic3r STATIC SLA/Clustering.hpp ) +if (SLIC3R_STATIC) + set(CGAL_Boost_USE_STATIC_LIBS ON) + set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE TRUE) +endif () + +find_package(CGAL REQUIRED) + +add_library(libslic3r_cgal OBJECT MeshBoolean.cpp MeshBoolean.hpp) +target_include_directories(libslic3r_cgal PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + $ + $) +target_compile_definitions(libslic3r_cgal PRIVATE + $) +target_compile_options(libslic3r_cgal PRIVATE + $) +target_sources(libslic3r PRIVATE $) + encoding_check(libslic3r) target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0) @@ -240,6 +256,7 @@ target_link_libraries(libslic3r qhull semver TBB::tbb + $ ${CMAKE_DL_LIBS} ) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index eec2b6a01..4156481b0 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -140,6 +140,7 @@ set(SLIC3R_GUI_SOURCES GUI/ProgressStatusBar.cpp GUI/PrintHostDialogs.cpp GUI/PrintHostDialogs.hpp + GUI/Job.hpp GUI/Mouse3DController.cpp GUI/Mouse3DController.hpp Utils/Http.cpp @@ -176,7 +177,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi CGAL::CGAL) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () diff --git a/src/slic3r/GUI/Job.hpp b/src/slic3r/GUI/Job.hpp new file mode 100644 index 000000000..9accd0ef3 --- /dev/null +++ b/src/slic3r/GUI/Job.hpp @@ -0,0 +1,154 @@ +#ifndef JOB_HPP +#define JOB_HPP + +#include + +#include +#include +#include + +#include + +#include + +namespace Slic3r { namespace GUI { + +// A class to handle UI jobs like arranging and optimizing rotation. +// These are not instant jobs, the user has to be informed about their +// state in the status progress indicator. On the other hand they are +// separated from the background slicing process. Ideally, these jobs should +// run when the background process is not running. +// +// TODO: A mechanism would be useful for blocking the plater interactions: +// objects would be frozen for the user. In case of arrange, an animation +// could be shown, or with the optimize orientations, partial results +// could be displayed. +class Job : public wxEvtHandler +{ + int m_range = 100; + boost::thread m_thread; + std::atomic m_running{false}, m_canceled{false}; + bool m_finalized = false; + std::shared_ptr m_progress; + + void run() + { + m_running.store(true); + process(); + m_running.store(false); + + // ensure to call the last status to finalize the job + update_status(status_range(), ""); + } + +protected: + // status range for a particular job + virtual int status_range() const { return 100; } + + // status update, to be used from the work thread (process() method) + void update_status(int st, const wxString &msg = "") + { + auto evt = new wxThreadEvent(); + evt->SetInt(st); + evt->SetString(msg); + wxQueueEvent(this, evt); + } + + bool was_canceled() const { return m_canceled.load(); } + + // Launched just before start(), a job can use it to prepare internals + virtual void prepare() {} + + // Launched when the job is finished. It refreshes the 3Dscene by def. + virtual void finalize() { m_finalized = true; } + + bool is_finalized() const { return m_finalized; } + +public: + Job(std::shared_ptr pri) : m_progress(pri) + { + Bind(wxEVT_THREAD, [this](const wxThreadEvent &evt) { + auto msg = evt.GetString(); + if (!msg.empty()) + m_progress->set_status_text(msg.ToUTF8().data()); + + if (m_finalized) return; + + m_progress->set_progress(evt.GetInt()); + if (evt.GetInt() == status_range()) { + // set back the original range and cancel callback + m_progress->set_range(m_range); + m_progress->set_cancel_callback(); + wxEndBusyCursor(); + + finalize(); + + // dont do finalization again for the same process + m_finalized = true; + } + }); + } + + Job(const Job &) = delete; + Job(Job &&) = delete; + Job &operator=(const Job &) = delete; + Job &operator=(Job &&) = delete; + + virtual void process() = 0; + + void start() + { // Start the job. No effect if the job is already running + if (!m_running.load()) { + prepare(); + + // Save the current status indicatior range and push the new one + m_range = m_progress->get_range(); + m_progress->set_range(status_range()); + + // init cancellation flag and set the cancel callback + m_canceled.store(false); + m_progress->set_cancel_callback( + [this]() { m_canceled.store(true); }); + + m_finalized = false; + + // Changing cursor to busy + wxBeginBusyCursor(); + + try { // Execute the job + m_thread = create_thread([this] { this->run(); }); + } catch (std::exception &) { + update_status(status_range(), + _(L("ERROR: not enough resources to " + "execute a new job."))); + } + + // The state changes will be undone when the process hits the + // last status value, in the status update handler (see ctor) + } + } + + // To wait for the running job and join the threads. False is + // returned if the timeout has been reached and the job is still + // running. Call cancel() before this fn if you want to explicitly + // end the job. + bool join(int timeout_ms = 0) + { + if (!m_thread.joinable()) return true; + + if (timeout_ms <= 0) + m_thread.join(); + else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) + return false; + + return true; + } + + bool is_running() const { return m_running.load(); } + void cancel() { m_canceled.store(true); } +}; + +} +} + +#endif // JOB_HPP diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index e613e6915..9e70fbaa2 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -58,7 +58,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S #endif // _WIN32 // initialize status bar - m_statusbar.reset(new ProgressStatusBar(this)); + m_statusbar = std::make_shared(this); + m_statusbar->set_font(GUI::wxGetApp().normal_font()); m_statusbar->embed(this); m_statusbar->set_status_text(_(L("Version")) + " " + SLIC3R_VERSION + diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 28bd0242b..1a7568092 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -135,7 +135,7 @@ public: Plater* m_plater { nullptr }; wxNotebook* m_tabpanel { nullptr }; wxProgressDialog* m_progress_dialog { nullptr }; - std::unique_ptr m_statusbar; + std::shared_ptr m_statusbar; }; } // GUI diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index df705f25f..43d2f06a5 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -69,6 +69,7 @@ #include "Camera.hpp" #include "Mouse3DController.hpp" #include "Tab.hpp" +#include "Job.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" #include "ProgressStatusBar.hpp" @@ -1457,136 +1458,30 @@ struct Plater::priv // objects would be frozen for the user. In case of arrange, an animation // could be shown, or with the optimize orientations, partial results // could be displayed. - class Job : public wxEvtHandler + class PlaterJob: public Job { - int m_range = 100; - boost::thread m_thread; - priv * m_plater = nullptr; - std::atomic m_running{false}, m_canceled{false}; - bool m_finalized = false; - - void run() - { - m_running.store(true); - process(); - m_running.store(false); - - // ensure to call the last status to finalize the job - update_status(status_range(), ""); - } - + priv *m_plater; protected: - // status range for a particular job - virtual int status_range() const { return 100; } - - // status update, to be used from the work thread (process() method) - void update_status(int st, const wxString &msg = "") - { - auto evt = new wxThreadEvent(); - evt->SetInt(st); - evt->SetString(msg); - wxQueueEvent(this, evt); - } priv & plater() { return *m_plater; } const priv &plater() const { return *m_plater; } - bool was_canceled() const { return m_canceled.load(); } - - // Launched just before start(), a job can use it to prepare internals - virtual void prepare() {} // Launched when the job is finished. It refreshes the 3Dscene by def. - virtual void finalize() + void finalize() override { // Do a full refresh of scene tree, including regenerating // all the GLVolumes. FIXME The update function shall just // reload the modified matrices. - if (!was_canceled()) + if (!Job::was_canceled()) plater().update(unsigned(UpdateParams::FORCE_FULL_SCREEN_REFRESH)); + + Job::finalize(); } public: - Job(priv *_plater) : m_plater(_plater) - { - Bind(wxEVT_THREAD, [this](const wxThreadEvent &evt) { - auto msg = evt.GetString(); - if (!msg.empty()) - plater().statusbar()->set_status_text(msg); - - if (m_finalized) return; - - plater().statusbar()->set_progress(evt.GetInt()); - if (evt.GetInt() == status_range()) { - // set back the original range and cancel callback - plater().statusbar()->set_range(m_range); - plater().statusbar()->set_cancel_callback(); - wxEndBusyCursor(); - - finalize(); - - // dont do finalization again for the same process - m_finalized = true; - } - }); - } - - Job(const Job &) = delete; - Job(Job &&) = delete; - Job &operator=(const Job &) = delete; - Job &operator=(Job &&) = delete; - - virtual void process() = 0; - - void start() - { // Start the job. No effect if the job is already running - if (!m_running.load()) { - prepare(); - - // Save the current status indicatior range and push the new one - m_range = plater().statusbar()->get_range(); - plater().statusbar()->set_range(status_range()); - - // init cancellation flag and set the cancel callback - m_canceled.store(false); - plater().statusbar()->set_cancel_callback( - [this]() { m_canceled.store(true); }); - - m_finalized = false; - - // Changing cursor to busy - wxBeginBusyCursor(); - - try { // Execute the job - m_thread = create_thread([this] { this->run(); }); - } catch (std::exception &) { - update_status(status_range(), - _(L("ERROR: not enough resources to " - "execute a new job."))); - } - - // The state changes will be undone when the process hits the - // last status value, in the status update handler (see ctor) - } - } - - // To wait for the running job and join the threads. False is - // returned if the timeout has been reached and the job is still - // running. Call cancel() before this fn if you want to explicitly - // end the job. - bool join(int timeout_ms = 0) - { - if (!m_thread.joinable()) return true; - - if (timeout_ms <= 0) - m_thread.join(); - else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) - return false; - - return true; - } - - bool is_running() const { return m_running.load(); } - void cancel() { m_canceled.store(true); } + PlaterJob(priv *_plater) + : Job(_plater->statusbar()), m_plater(_plater) + {} }; enum class Jobs : size_t { @@ -1595,7 +1490,7 @@ struct Plater::priv Hollow }; - class ArrangeJob : public Job + class ArrangeJob : public PlaterJob { using ArrangePolygon = arrangement::ArrangePolygon; using ArrangePolygons = arrangement::ArrangePolygons; @@ -1712,7 +1607,7 @@ struct Plater::priv } public: - using Job::Job; + using PlaterJob::PlaterJob; int status_range() const override { return int(m_selected.size()); } @@ -1729,17 +1624,17 @@ struct Plater::priv } }; - class RotoptimizeJob : public Job + class RotoptimizeJob : public PlaterJob { public: - using Job::Job; + using PlaterJob::PlaterJob; void process() override; }; - class HollowJob : public Job + class HollowJob : public PlaterJob { public: - using Job::Job; + using PlaterJob::PlaterJob; void prepare() override; void process() override; void finalize() override; @@ -1847,7 +1742,7 @@ struct Plater::priv void reset_all_gizmos(); void update_ui_from_settings(); - ProgressStatusBar* statusbar(); + std::shared_ptr statusbar(); std::string get_config(const std::string &key) const; BoundingBoxf bed_shape_bb() const; BoundingBox scaled_bed_shape_bb() const; @@ -2266,9 +2161,9 @@ void Plater::priv::update_ui_from_settings() preview->get_canvas3d()->update_ui_from_settings(); } -ProgressStatusBar* Plater::priv::statusbar() +std::shared_ptr Plater::priv::statusbar() { - return main_frame->m_statusbar.get(); + return main_frame->m_statusbar; } std::string Plater::priv::get_config(const std::string &key) const diff --git a/src/slic3r/GUI/ProgressIndicator.hpp b/src/slic3r/GUI/ProgressIndicator.hpp index 280ba63ac..674a81ba2 100644 --- a/src/slic3r/GUI/ProgressIndicator.hpp +++ b/src/slic3r/GUI/ProgressIndicator.hpp @@ -11,58 +11,17 @@ namespace Slic3r { */ class ProgressIndicator { public: - using CancelFn = std::function; // Cancel function signature. - -private: - float m_state = .0f, m_max = 1.f, m_step; - CancelFn m_cancelfunc = [](){}; - -public: - - inline virtual ~ProgressIndicator() {} - - /// Get the maximum of the progress range. - float max() const { return m_max; } - - /// Get the current progress state - float state() const { return m_state; } - - /// Set the maximum of the progress range - virtual void max(float maxval) { m_max = maxval; } - - /// Set the current state of the progress. - virtual void state(float val) { m_state = val; } - - /** - * @brief Number of states int the progress. Can be used instead of giving a - * maximum value. - */ - virtual void states(unsigned statenum) { - m_step = m_max / statenum; - } - - /// Message shown on the next status update. - virtual void message(const std::string&) = 0; - - /// Title of the operation. - virtual void title(const std::string&) = 0; - - /// Formatted message for the next status update. Works just like sprintf. - virtual void message_fmt(const std::string& fmt, ...); - - /// Set up a cancel callback for the operation if feasible. - virtual void on_cancel(CancelFn func = CancelFn()) { m_cancelfunc = func; } - - /** - * Explicitly shut down the progress indicator and call the associated - * callback. - */ - virtual void cancel() { m_cancelfunc(); } - - /// Convenience function to call message and status update in one function. - void update(float st, const std::string& msg) { - message(msg); state(st); - } + + /// Cancel callback function type + using CancelFn = std::function; + + virtual ~ProgressIndicator() = default; + + virtual void set_range(int range) = 0; + virtual void set_cancel_callback(CancelFn = CancelFn()) = 0; + virtual void set_progress(int pr) = 0; + virtual void set_status_text(const char *) = 0; // utf8 char array + virtual int get_range() const = 0; }; } diff --git a/src/slic3r/GUI/ProgressStatusBar.cpp b/src/slic3r/GUI/ProgressStatusBar.cpp index 33e4855cd..1ec2b8193 100644 --- a/src/slic3r/GUI/ProgressStatusBar.cpp +++ b/src/slic3r/GUI/ProgressStatusBar.cpp @@ -15,8 +15,7 @@ namespace Slic3r { ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id) - : self{new wxStatusBar(parent ? parent : GUI::wxGetApp().mainframe, - id == -1 ? wxID_ANY : id)} + : self{new wxStatusBar(parent, id == -1 ? wxID_ANY : id)} , m_prog{new wxGauge(self, wxGA_HORIZONTAL, 100, @@ -32,7 +31,6 @@ ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id) m_prog->Hide(); m_cancelbutton->Hide(); - self->SetFont(GUI::wxGetApp().normal_font()); self->SetFieldsCount(3); int w[] = {-1, 150, 155}; self->SetStatusWidths(3, w); @@ -149,8 +147,7 @@ void ProgressStatusBar::run(int rate) void ProgressStatusBar::embed(wxFrame *frame) { - wxFrame* mf = frame ? frame : GUI::wxGetApp().mainframe; - if(mf) mf->SetStatusBar(self); + if(frame) frame->SetStatusBar(self); } void ProgressStatusBar::set_status_text(const wxString& txt) @@ -173,6 +170,11 @@ wxString ProgressStatusBar::get_status_text() const return self->GetStatusText(); } +void ProgressStatusBar::set_font(const wxFont &font) +{ + self->SetFont(font); +} + void ProgressStatusBar::show_cancel_button() { if(m_cancelbutton) m_cancelbutton->Show(); diff --git a/src/slic3r/GUI/ProgressStatusBar.hpp b/src/slic3r/GUI/ProgressStatusBar.hpp index 1aab67d5a..faeb7a34e 100644 --- a/src/slic3r/GUI/ProgressStatusBar.hpp +++ b/src/slic3r/GUI/ProgressStatusBar.hpp @@ -6,6 +6,8 @@ #include #include +#include "ProgressIndicator.hpp" + class wxTimer; class wxGauge; class wxButton; @@ -14,6 +16,7 @@ class wxStatusBar; class wxWindow; class wxFrame; class wxString; +class wxFont; namespace Slic3r { @@ -22,7 +25,7 @@ namespace Slic3r { * of the Slicer main window. It consists of a message area to the left and a * progress indication area to the right with an optional cancel button. */ -class ProgressStatusBar +class ProgressStatusBar : public ProgressIndicator { wxStatusBar *self; // we cheat! It should be the base class but: perl! wxGauge *m_prog; @@ -30,30 +33,29 @@ class ProgressStatusBar std::unique_ptr m_timer; public: - /// Cancel callback function type - using CancelFn = std::function; ProgressStatusBar(wxWindow *parent = nullptr, int id = -1); - ~ProgressStatusBar(); + ~ProgressStatusBar() override; int get_progress() const; // if the argument is less than 0 it shows the last state or // pulses if no state was set before. - void set_progress(int); - int get_range() const; - void set_range(int = 100); + void set_progress(int) override; + int get_range() const override; + void set_range(int = 100) override; void show_progress(bool); void start_busy(int = 100); void stop_busy(); inline bool is_busy() const { return m_busy; } - void set_cancel_callback(CancelFn = CancelFn()); + void set_cancel_callback(CancelFn = CancelFn()) override; inline void reset_cancel_callback() { set_cancel_callback(); } void run(int rate); void embed(wxFrame *frame = nullptr); void set_status_text(const wxString& txt); void set_status_text(const std::string& txt); - void set_status_text(const char *txt); + void set_status_text(const char *txt) override; wxString get_status_text() const; + void set_font(const wxFont &font); // Temporary methods to satisfy Perl side void show_cancel_button();