diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 6370a81b12..8c56ddc75b 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -195,8 +195,6 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/NotificationProgressIndicator.cpp GUI/Jobs/ThreadSafeQueue.hpp GUI/Jobs/SLAImportDialog.hpp - GUI/Jobs/ReRunJob.hpp - GUI/Jobs/StopableJob.hpp GUI/ProgressStatusBar.hpp GUI/ProgressStatusBar.cpp GUI/Mouse3DController.cpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index a2928a4717..4349eb8947 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -201,7 +201,6 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) , m_volume(nullptr) , m_exist_notification(false) , m_is_initialized(false) // initialize on first opening gizmo - , m_job(std::make_unique()) , m_rotate_gizmo(parent, GLGizmoRotate::Axis::Z) { m_rotate_gizmo.set_group_id(0); @@ -582,8 +581,10 @@ bool GLGizmoEmboss::process() if (m_font == nullptr) return false; auto data = std::make_unique( m_font, create_configuration(), create_volume_name(), m_volume); - m_job->run(std::move(data)); - + + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + replace_job(worker, std::make_unique(std::move(data))); + // notification is removed befor object is changed by job remove_notification_not_valid_font(); return true; @@ -593,8 +594,6 @@ void GLGizmoEmboss::close() { // close gizmo == open it again m_parent.get_gizmos_manager().open_gizmo(GLGizmosManager::Emboss); - m_job->stop(); - m_job->join(); // free thread resource } void GLGizmoEmboss::draw_window() @@ -1610,7 +1609,7 @@ std::string GLGizmoEmboss::imgui_trunc(const std::string &text, float width) return text.substr(0, count_letter) + tail; } -namespace Slic3r { +namespace Slic3r::GUI { class Priv { @@ -1645,23 +1644,8 @@ public: , object_idx(object_idx) {} }; - struct UpdateVolume - { - TriangleMesh mesh; - std::string name; - TextConfiguration cfg; - ModelVolume * volume; - UpdateVolume(TriangleMesh && mesh, - std::string name, - TextConfiguration cfg, - ModelVolume * volume) - : mesh(std::move(mesh)), name(name), cfg(cfg), volume(volume) - {} - }; - static void create_emboss_object(EmbossObject &data); - static void create_emboss_volume(EmbossVolume &data); - static void update_emboss_volume(UpdateVolume &data); + static void create_emboss_volume(EmbossVolume &data); }; } // namespace Slic3r @@ -1696,21 +1680,6 @@ void GLGizmoEmboss::create_emboss_volume(TriangleMesh && mesh, }); } -void GLGizmoEmboss::update_emboss_volume(TriangleMesh && mesh, - std::string name, - TextConfiguration cfg, - ModelVolume * volume) -{ - assert(volume != nullptr); - // Move data to call after is not working - // data are owen by lambda - auto data = new Priv::UpdateVolume(std::move(mesh), name, cfg, volume); - wxGetApp().plater()->CallAfter([data]() { - ScopeGuard sg([data]() { delete data; }); - Priv::update_emboss_volume(*data); - }); -} - void Priv::create_emboss_object(EmbossObject &data) { GUI_App & app = wxGetApp(); @@ -1791,7 +1760,14 @@ void Priv::create_emboss_volume(EmbossVolume &data) canvas->reload_scene(true); } -void Priv::update_emboss_volume(UpdateVolume &data) { +void GLGizmoEmboss::update_emboss_volume(TriangleMesh && mesh, + const std::string& name, + const TextConfiguration& cfg, + ModelVolume * volume) +{ + // for sure that some object is created from shape + if (mesh.its.indices.empty()) return; + GUI_App & app = wxGetApp(); // may be move to input Plater * plater = app.plater(); ObjectList * obj_list = app.obj_list(); @@ -1801,38 +1777,35 @@ void Priv::update_emboss_volume(UpdateVolume &data) { // Check emboss gizmo is still open if (manager.get_current_type() != GLGizmosManager::Emboss) return; - const std::string &name = data.name; - plater->take_snapshot(_L("Emboss text") + ": " + name); // find volume by object id - NOT WORK // -> edit text change volume id so could apper not found volume - //ModelVolume *volume = nullptr; - //Model &model = plater->model(); - //for (auto obj : model.objects) + // ModelVolume *volume = nullptr; + // Model &model = plater->model(); + // for (auto obj : model.objects) // for (auto vol : obj->volumes) - // if (vol->id() == volume_id) { + // if (vol->id() == volume_id) { // volume = vol; // break; // } - //if (volume == nullptr) return; - ModelVolume *volume = data.volume; + // if (volume == nullptr) return; assert(volume != nullptr); // update volume - volume->set_mesh(std::move(data.mesh)); + volume->set_mesh(std::move(mesh)); volume->set_new_unique_id(); volume->calculate_convex_hull(); volume->get_object()->invalidate_bounding_box(); volume->name = name; - volume->text_configuration = data.cfg; + volume->text_configuration = cfg; // update volume in right panel( volume / object name) const Selection &selection = canvas->get_selection(); - const GLVolume * v = selection.get_volume( + const GLVolume * gl_volume = selection.get_volume( *selection.get_volume_idxs().begin()); - int object_idx = v->object_idx(); - int volume_idx = v->volume_idx(); + int object_idx = gl_volume->object_idx(); + int volume_idx = gl_volume->volume_idx(); obj_list->update_name_in_list(object_idx, volume_idx); // update printable state on canvas @@ -1847,7 +1820,6 @@ void Priv::update_emboss_volume(UpdateVolume &data) { /// /// WxFontUtils - Start definition /// - std::optional WxFontUtils::load_font(const wxFont &font) { if (!font.IsOk()) return {}; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index ad69bf77a9..b79a99a1a3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -132,9 +132,6 @@ private: FontProp m_font_prop; TriangleMesh m_default_mesh; // when add new text this shape is used - // thread to process object on change text - std::unique_ptr m_job; - // actual volume ModelVolume *m_volume; @@ -180,10 +177,10 @@ private: TextConfiguration cfg, ModelVolumeType type, size_t object_idx); - static void update_emboss_volume(TriangleMesh && mesh, - std::string name, - TextConfiguration cfg, - ModelVolume * volume); + static void update_emboss_volume(TriangleMesh && mesh, + const std::string & name, + const TextConfiguration &cfg, + ModelVolume * volume); // only temporary solution static const std::string M_ICON_FILENAME; diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index d0759e6cad..1c26f19e17 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -14,46 +14,51 @@ using namespace Slic3r; using namespace GUI; -void EmbossJob::process(std::unique_ptr input) -{ +void EmbossJob::process(Ctl &ctl) { // Changing cursor to busy wxBeginBusyCursor(); ScopeGuard sg([]() { wxEndBusyCursor(); }); // only for sure - assert(input != nullptr); + assert(m_input != nullptr); // check if exist valid font - if (input->font == nullptr) return; + if (m_input->font == nullptr) return; - const TextConfiguration &cfg = input->text_configuration; + const TextConfiguration &cfg = m_input->text_configuration; const std::string & text = cfg.text; // Do NOT process empty string if (text.empty()) return; const FontProp &prop = cfg.font_prop; - ExPolygons shapes = Emboss::text2shapes(*input->font, text.c_str(), prop); + ExPolygons shapes = Emboss::text2shapes(*m_input->font, text.c_str(), prop); - if (is_stoping()) return; + if (ctl.was_canceled()) return; // exist 2d shape made by text ? // (no shape means that font hasn't any of text symbols) if (shapes.empty()) return; - float scale = prop.size_in_mm / input->font->ascent; + float scale = prop.size_in_mm / m_input->font->ascent; auto projectZ = std::make_unique(prop.emboss / scale); Emboss::ProjectScale project(std::move(projectZ), scale); - TriangleMesh result(Emboss::polygons2model(shapes, project)); + m_result = TriangleMesh(Emboss::polygons2model(shapes, project)); - // for sure that some object is created from shape - if (result.its.indices.empty()) return; + if (ctl.was_canceled()) return; // center triangle mesh - Vec3d shift = result.bounding_box().center(); - result.translate(-shift.cast()); - - if (is_stoping()) return; - GLGizmoEmboss::update_emboss_volume(std::move(result), input->volume_name, - input->text_configuration, - input->volume); + Vec3d shift = m_result.bounding_box().center(); + m_result.translate(-shift.cast()); +} + +void EmbossJob::finalize(bool canceled, std::exception_ptr &) { + if (canceled) return; + + // for sure that some object is created from shape + if (m_result.its.indices.empty()) return; + + GLGizmoEmboss::update_emboss_volume(std::move(m_result), + m_input->volume_name, + m_input->text_configuration, + m_input->volume); } diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index 67de35c718..282be02222 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -1,12 +1,12 @@ #ifndef slic3r_EmbossJob_hpp_ #define slic3r_EmbossJob_hpp_ -#include "StopableJob.hpp" #include "libslic3r/Emboss.hpp" -//#include "libslic3r/ObjectID.hpp" +#include "Job.hpp" namespace Slic3r { class ModelVolume; +class TriangleMesh; } namespace Slic3r::GUI { @@ -39,10 +39,14 @@ struct EmbossData {} }; -class EmbossJob : public StopableJob +class EmbossJob : public Job { -protected: - void process(std::unique_ptr input) override; + std::unique_ptr m_input; + TriangleMesh m_result; +public: + EmbossJob(std::unique_ptr input) : m_input(std::move(input)) {} + void process(Ctl &ctl) override; + void finalize(bool canceled, std::exception_ptr &) override; }; } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/ReRunJob.hpp b/src/slic3r/GUI/Jobs/ReRunJob.hpp deleted file mode 100644 index ca1af56110..0000000000 --- a/src/slic3r/GUI/Jobs/ReRunJob.hpp +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef slic3r_ReRunJob_hpp_ -#define slic3r_ReRunJob_hpp_ - -#include -#include -#include -#include - -namespace Slic3r::GUI { - -// inspired by Job.hpp -// All public function can be call only from UI thread -// Mechanism to stack processing unique input and do only the last one -template -class ReRunJob -{ - std::mutex m_next_mutex; - std::unique_ptr m_input_next = nullptr; - - std::thread m_thread; - // indicate checking of new input. - std::atomic m_running{false}; - - using Func = std::function)>; - Func m_func; - -public: - ReRunJob(Func func) : m_func(func) {} - virtual ~ReRunJob() { - try { - // thread join could throw exception - // https://en.cppreference.com/w/cpp/thread/thread/join - join(); - } catch (std::system_error err) {} - } - - void re_run(std::unique_ptr input) - { - if (input == nullptr) return; - { - // keep access to next input - std::lock_guard lg(m_next_mutex); - if (is_running()) { - // when runnig - m_input_next = std::move(input); - return; // on end of run will be used new input - } - m_running.store(true); - } - if (m_thread.joinable()) m_thread.join(); - // at this moment is not running --> stoped - assert(m_input_next == nullptr); - try { // Execute the job - m_thread = std::thread( - [this](std::unique_ptr input) { - do { - m_func(std::move(input)); - - std::lock_guard lg(m_next_mutex); - // it is not in while condition because of lock guard - if (m_input_next == nullptr) { - m_running.store(false); - return; - } - input = std::move(m_input_next); - m_input_next = nullptr; - } while (true); - }, - std::move(input)); - } catch (std::exception &) {} - } - - bool is_running() const { return m_running.load(); } - void join() - { - if (m_thread.joinable()) m_thread.join(); - assert(!is_running()); - } -}; - -} // namespace Slic3r::GUI - -#endif // slic3r_ReRunJob_hpp_ diff --git a/src/slic3r/GUI/Jobs/StopableJob.hpp b/src/slic3r/GUI/Jobs/StopableJob.hpp deleted file mode 100644 index 3dcb393b5e..0000000000 --- a/src/slic3r/GUI/Jobs/StopableJob.hpp +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef slic3r_StopableJob_hpp_ -#define slic3r_StopableJob_hpp_ - -#include -#include -#include -#include - -namespace Slic3r::GUI { - -// inspired by Job.hpp -// Mechanism to stack processing and do only last one -// Ability to stop processing developer must add check is_stopping() into process() -template class StopableJob -{ - std::mutex m_mutex; - std::unique_ptr m_input_next = nullptr; - - std::thread m_thread; - // when running == true, than check of new input in thread. - bool m_running = false; - // faster interupt inside func, developer must add StopCondifion call - bool m_stop = false; -public: - /// - /// Stop and join thread - /// - virtual ~StopableJob(); - - /// - /// Restart processing of input - /// - /// Data needed to process - void run(std::unique_ptr input); - - /// - /// Check if job processing input now - /// - /// True when run now otherwise False - bool is_running(); // const; -- mutex - - /// - /// Check if actual job is stopping now - /// - /// True when at stop process otherwise False - bool is_stoping(); // const; -- mutex - - /// - /// set flag to stop processing - /// - void stop(); - - /// - /// Free thread resources by join thread - /// Be Carefull, it is blocking until join - /// Suggest to call stop() before join - /// Thread join could throw exception - /// https://en.cppreference.com/w/cpp/thread/thread/join - /// - void join(); -protected: - - /// - /// Thread job of processing input data - /// Note: Use check function is_stoping(), when true than interupt processing - /// - /// input data to process - virtual void process(std::unique_ptr input) = 0; -}; - -////// -// Implementation -////// -template -StopableJob::~StopableJob() -{ - stop(); - try { - // thread join could throw exception - // https://en.cppreference.com/w/cpp/thread/thread/join - join(); - } catch (const std::system_error & /* err*/) {} -} - -template -void StopableJob::run(std::unique_ptr input) -{ - if (input == nullptr) return; - { - // keep access to next input - std::lock_guard lg(m_mutex); - if (m_running) { - // when runnig - m_stop = true; - m_input_next = std::move(input); - return; // on end of run will be used new input - } - m_running = true; - m_stop = false; - } - if (m_thread.joinable()) m_thread.join(); - // at this moment is not running --> stoped - assert(m_input_next == nullptr); - try { // Execute the job - m_thread = std::thread( - [this](std::unique_ptr input) { - do { - process(std::move(input)); - - std::lock_guard lg(m_mutex); - m_stop = false; - // this is not while (end)condition because of lock guard - if (m_input_next == nullptr) { - m_running = false; - return; - } - input = std::move(m_input_next); - m_input_next = nullptr; - } while (true); - }, - std::move(input)); - } catch (std::exception &) {} -} - -template -bool StopableJob::is_running() -{ - std::lock_guard lg(m_mutex); - return m_running; -} - - -template -bool StopableJob::is_stoping() -{ - std::lock_guard lg(m_mutex); - return m_stop; -} - -template -void StopableJob::stop() -{ - std::lock_guard lg(m_mutex); - if (!m_running) return; - m_input_next = nullptr; - m_stop = true; -} - -template -void StopableJob::join() -{ - if (m_thread.joinable()) m_thread.join(); - assert(!m_running); -} - -} // namespace Slic3r::GUI - -#endif // slic3r_StopableJob_hpp_