From 43d200a7fb100b59fa41c3eac9143e14101a6123 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 23 Nov 2021 22:21:14 +0100 Subject: [PATCH] Using of Re run Job --- src/slic3r/GUI/GUI_ObjectList.cpp | 2 +- src/slic3r/GUI/GUI_ObjectList.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 15 ++- src/slic3r/GUI/Jobs/EmbossJob.cpp | 157 +++++++++++------------- src/slic3r/GUI/Jobs/EmbossJob.hpp | 137 ++++++++++++--------- 5 files changed, 160 insertions(+), 153 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 543d95a4d4..c5aed55b7c 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1743,7 +1743,7 @@ void ObjectList::load_shape_object_from_gallery(const wxArrayString& input_files wxGetApp().mainframe->update_title(); } -void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center, TextConfiguration* text_config/* = nullptr*/) +void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center, const TextConfiguration* text_config/* = nullptr*/) { // Add mesh to model as a new object Model& model = wxGetApp().plater()->model(); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 39a73abbdd..86e4139ed3 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -257,7 +257,7 @@ public: void load_shape_object(const std::string &type_name); void load_shape_object_from_gallery(); void load_shape_object_from_gallery(const wxArrayString& input_files); - void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true, TextConfiguration* text_config = nullptr); + void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true, const TextConfiguration* text_config = nullptr); void del_object(const int obj_idx); void del_subobject_item(wxDataViewItem& item); void del_settings_from_config(const wxDataViewItem& parent_item); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 97ce8de7ee..c3dd90ff89 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -472,14 +472,13 @@ bool GLGizmoEmboss::process() // exist loaded font? if (m_font == nullptr) return false; - - EmbossJob::Data data; - data.font = m_font; - data.text_configuration = create_configuration(); - data.volume_name = create_volume_name(); - data.volume_ptr = m_volume; - data.object_idx = m_parent.get_selection().get_object_idx(); - m_job->restart(data); + auto data = std::make_unique(); + data->font = m_font; + data->text_configuration = create_configuration(); + data->volume_name = create_volume_name(); + data->volume_ptr = m_volume; + data->object_idx = m_parent.get_selection().get_object_idx(); + m_job->re_run(std::move(data)); // notification is removed befor object is changed by job remove_notification_not_valid_font(); diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index 2795ace4c5..357ea41309 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -13,108 +13,92 @@ using namespace Slic3r; using namespace GUI; -//EmbossJob::EmbossJob(): Job(std::make_shared(wxGetApp().plater()->get_notification_manager())){} -EmbossJob::EmbossJob() : Job(std::make_shared()) {} +namespace Priv { -EmbossJob::~EmbossJob() { - Job::cancel(); - Job::join(); -} +static void process(std::unique_ptr input); +static void finalize(const EmbossData &input, const indexed_triangle_set &result); -void EmbossJob::restart(const Data &data) +// TODO: move to objec list utils +static void select_volume(ModelVolume *volume); + +} // namespace Priv + +EmbossJob::EmbossJob() : ReRunJob(Priv::process) {} + + +void Priv::process(std::unique_ptr input) { - if (Job::is_running()) { - Job::cancel(); - std::lock_guard lk(m_mutex); - bool create_restart = !m_data_next.has_value(); - m_data_next = data; // copy + // only for sure + assert(input != nullptr); - if (create_restart) { - wxGetApp().plater()->CallAfter([this]() { - const Plater *plater = wxGetApp().plater(); - if (plater == nullptr) return; - const GLCanvas3D *canvas = plater->canvas3D(); - if (canvas == nullptr) return; - const GLGizmosManager &mng = canvas->get_gizmos_manager(); - // check if simplify is still activ gizmo - if (mng.get_current_type() != GLGizmosManager::Emboss) return; - - // when emboss is current, this object exist - Job::join(); - { - // Not neccessary to lock mutex because job is stoped, - // but for sure that no other thread try to restart job. - // Is not critical and could be there. - std::lock_guard lk(m_mutex); - assert(m_data_next.has_value()); - m_data = std::move(m_data_next); - // after move optional, data is UNDEFINED - m_data_next = {}; - } - Job::start(); - }); - } - } else { - m_data = data; // copy - Job::start(); - } -} - -void EmbossJob::prepare() {} - -void EmbossJob::process() { - // check if data was set - if (!m_data.has_value()) return; // check if exist valid font - if (m_data->font == nullptr) return; + if (input->font == nullptr) return; // Do NOT process empty string - const TextConfiguration &cfg = m_data->text_configuration; - const std::string &text = cfg.text; + const TextConfiguration &cfg = input->text_configuration; + const std::string & text = cfg.text; if (text.empty()) return; const FontProp &prop = cfg.font_prop; - ExPolygons shapes = Emboss::text2shapes(*m_data->font, text.c_str(), prop); - + ExPolygons shapes = Emboss::text2shapes(*input->font, text.c_str(), prop); + // 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 / m_data->font->ascent; - auto project = std::make_unique( - std::make_unique(prop.emboss / scale), scale); - - indexed_triangle_set its = Emboss::polygons2model(shapes, *project); + float scale = prop.size_in_mm / input->font->ascent; + auto projectZ = std::make_unique(prop.emboss / scale); + Emboss::ProjectScale project(std::move(projectZ), scale); + auto its = std::make_unique(Emboss::polygons2model(shapes, project)); // for sure that some object is created from shape - if (its.indices.empty()) return; - m_result = its; + if (its->indices.empty()) return; + + // Call setting model in UI thread + + // Not work !!! CallAfter use only reference to lambda not lambda itself + // wxGetApp().plater()->CallAfter( + // [its2 = std::move(its), input2 = std::move(input)]() { + // Priv::finalize(*input2, *its2); + // }); + + struct Data + { + std::unique_ptr input; + std::unique_ptr result; + Data(std::unique_ptr input, + std::unique_ptr result) + : input(std::move(input)), result(std::move(result)) + {} + }; + Data *data = new Data(std::move(input), std::move(its)); + wxGetApp().plater()->CallAfter([data]() { + Priv::finalize(*data->input, *data->result); + delete data; + }); } -void EmbossJob::finalize() { - // check result was created - if (!m_result.has_value()) return; - if (m_result->indices.empty()) return; - // move object data - TriangleMesh tm(std::move(*m_result)); - m_result = {}; - +void Priv::finalize(const EmbossData &input, const indexed_triangle_set &result) +{ + // it is sad, but there is no move constructor --> copy + TriangleMesh tm(std::move(result)); + // center triangle mesh Vec3d shift = tm.bounding_box().center(); tm.translate(-shift.cast()); - GUI_App & app = wxGetApp(); - Plater * plater = app.plater(); - GLCanvas3D *canvas = plater->canvas3D(); - const std::string &name = m_data->volume_name; + GUI_App & app = wxGetApp(); + Plater * plater = app.plater(); + GLCanvas3D * canvas = plater->canvas3D(); + const std::string &name = input.volume_name; plater->take_snapshot(_L("Emboss text") + ": " + name); - ModelVolume *volume = m_data->volume_ptr; + ModelVolume *volume = input.volume_ptr; if (volume == nullptr) { - // decide to add as volume or new object - if (m_data->object_idx < 0) { + // decide to add as volume or new object + if (input.object_idx < 0) { // create new object - app.obj_list()->load_mesh_object(tm, name, true, &m_data->text_configuration); + app.obj_list()->load_mesh_object(tm, name, true, &input.text_configuration); app.mainframe->update_title(); // TODO: find Why ??? @@ -127,9 +111,9 @@ void EmbossJob::finalize() { } else { // create new volume inside of object Model &model = plater->model(); - if (model.objects.size() <= m_data->object_idx) return; - ModelObject *obj = model.objects[m_data->object_idx]; - volume = obj->add_volume(std::move(tm)); + if (model.objects.size() <= input.object_idx) return; + ModelObject *obj = model.objects[input.object_idx]; + volume = obj->add_volume(std::move(tm)); // set a default extruder value, since user can't add it manually volume->config.set_key_value("extruder", new ConfigOptionInt(0)); } @@ -141,8 +125,8 @@ void EmbossJob::finalize() { volume->get_object()->invalidate_bounding_box(); } - volume->name = name; - volume->text_configuration = m_data->text_configuration; + volume->name = name; + volume->text_configuration = input.text_configuration; // update volume name in object list // updata selection after new volume added @@ -153,18 +137,19 @@ void EmbossJob::finalize() { canvas->reload_scene(true); } -void EmbossJob::select_volume(ModelVolume *volume) -{ +void Priv::select_volume(ModelVolume *volume) +{ if (volume == nullptr) return; - ObjectList * obj_list = wxGetApp().obj_list(); - + ObjectList *obj_list = wxGetApp().obj_list(); + // select only actual volume // when new volume is created change selection to this volume auto add_to_selection = [volume](const ModelVolume *vol) { return vol == volume; }; - const Selection &selection = wxGetApp().plater()->canvas3D()->get_selection(); + const Selection &selection = + wxGetApp().plater()->canvas3D()->get_selection(); wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection(selection.get_object_idx(), add_to_selection); diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index 490bd9d9e2..aa3ce20266 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -10,70 +10,93 @@ namespace Slic3r { class ModelVolume; -class ModelObject; } namespace Slic3r::GUI { -// thread process of conversion from text to model -// [optionaly] union with model -class EmbossJob : protected Job +// inspired by Job.hpp +// All public function can be call only from UI thread +// Mechanism to stack processing and do only 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() { join(); } + + 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()); + } +}; + +struct EmbossData +{ + // Pointer on Data of font (glyph shapes) + std::shared_ptr font; + // font item is not used for create object + TextConfiguration text_configuration; + // new volume name created from text + std::string volume_name; + // when volume_ptr == nullptr than new volume will be created + ModelVolume *volume_ptr; + // when volume_ptr == nullptr && object_idx < 0 than new object will be created + int object_idx; +}; + +class EmbossJob : public ReRunJob { public: - struct Data - { - // Pointer on Data of font (glyph shapes) - std::shared_ptr font; - // font item is not used for create object - TextConfiguration text_configuration; - // new volume name created from text - std::string volume_name; - // when volume_ptr == nullptr than new volume will be created - ModelVolume *volume_ptr; - // when volume_ptr == nullptr && object_idx < 0 than new object will be created - int object_idx; - }; - EmbossJob(); - ~EmbossJob();// cancel work and join - - void restart(const Data &data); - - // do not allow Job::start - bool join(int timeout_ms = 0) { return Job::join(timeout_ms); }; - bool is_running() const { return Job::is_running(); } - void cancel() { Job::cancel(); } - -protected: - // Launched just before start(), a job can use it to prepare internals - virtual void prepare() override; - - // The method where the actual work of the job should be defined. - virtual void process() override; - - // Launched when the job is finished. It refreshes the 3Dscene by def. - virtual void finalize() override; - -private: - std::optional m_data; - std::optional m_result; - - std::mutex m_mutex; // protect next data - std::optional m_data_next; - - // TODO: move to objec list utils - void select_volume(ModelVolume * volume); - - - class Progress : public ProgressIndicator - { - // Inherited via ProgressIndicator - virtual void set_range(int range) override {} - virtual void set_cancel_callback(CancelFn = CancelFn()) override {} - virtual void set_progress(int pr) override {} - virtual void set_status_text(const char *) override {} - virtual int get_range() const override { return 100; } - }; + EmbossJob(); }; + } // namespace Slic3r::GUI #endif // slic3r_EmbossJob_hpp_