diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index e68de0443c..dd95bad6cb 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -178,6 +178,8 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/PlaterJob.cpp GUI/Jobs/ArrangeJob.hpp GUI/Jobs/ArrangeJob.cpp + GUI/Jobs/EmbossJob.cpp + GUI/Jobs/EmbossJob.hpp GUI/Jobs/RotoptimizeJob.hpp GUI/Jobs/RotoptimizeJob.cpp GUI/Jobs/FillBedJob.hpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 71437b2a14..5e68577c47 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -9,6 +9,8 @@ #include "slic3r/GUI/MsgDialog.hpp" #include "slic3r/GUI/format.hpp" #include "slic3r/GUI/CameraUtils.hpp" +#include "slic3r/GUI/Jobs/EmbossJob.hpp" +#include "slic3r/GUI/Jobs/NotificationProgressIndicator.hpp" // TODO: remove include #include "libslic3r/SVG.hpp" // debug store @@ -184,7 +186,9 @@ public: return std::string(fullFileName.c_str()); } }; + #endif // __linux__ + } // namespace Slic3r using namespace Slic3r; @@ -193,12 +197,15 @@ using namespace Slic3r::GUI; GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) : GLGizmoBase(parent, M_ICON_FILENAME, -2) , m_font_selected(0) + , m_font(nullptr) , m_volume(nullptr) , m_exist_notification(false) , m_is_initialized(false) // initialize on first opening gizmo + , m_job(std::make_unique()) { // TODO: add suggestion to use https://fontawesome.com/ - // (copy & paste) unicode symbols from web + // (copy & paste) unicode symbols from web + } GLGizmoEmboss::~GLGizmoEmboss() {} @@ -261,7 +268,6 @@ void GLGizmoEmboss::set_volume_type(ModelVolumeType volume_type) { if (m_volume == nullptr) return; m_volume->set_type(volume_type); - refresh_object_list(); m_parent.reload_scene(true); } @@ -460,7 +466,24 @@ ModelVolume *GLGizmoEmboss::get_selected_volume(const Selection &selection, bool GLGizmoEmboss::process() { // exist loaded font? - if (!m_font.has_value()) return false; + 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; + + const Selection &selection = m_parent.get_selection(); + if (!selection.is_empty() && selection.get_object_idx() >= 0) { + int object_idx = selection.get_object_idx(); + Model &model = wxGetApp().plater()->model(); + data.object_ptr = model.objects[object_idx]; + } + m_job->restart(data); + return true; + ExPolygons shapes = Emboss::text2shapes(*m_font, m_text.c_str(), m_font_prop); // exist 2d shape made by text ? // (no shape means that font doesnt have any of text symbols) @@ -488,7 +511,7 @@ bool GLGizmoEmboss::add_volume(const std::string & name, GUI_App &app = wxGetApp(); Plater * plater = app.plater(); - plater->take_snapshot(_L("Add") + " " + name); + plater->take_snapshot(_L("Emboss text") + ": " + name); if (m_volume == nullptr) { // decide to add as volume or new object const Selection &selection = m_parent.get_selection(); @@ -498,25 +521,28 @@ bool GLGizmoEmboss::add_volume(const std::string & name, app.obj_list()->load_mesh_object(tm, name, true, &text_configuration); app.mainframe->update_title(); - // load mesh cause close gizmo, soo I open it again - m_parent.get_gizmos_manager().open_gizmo(GLGizmosManager::EType::Emboss); + // load mesh cause close gizmo, on windows but not on linux + // Open gizmo again when it is closed + GLGizmosManager& mng = m_parent.get_gizmos_manager(); + if (mng.get_current_type() != GLGizmosManager::Emboss) + mng.open_gizmo(GLGizmosManager::Emboss); return true; } else { // create new volume inside of object int object_idx = selection.get_object_idx(); ModelObject *obj = plater->model().objects[object_idx]; m_volume = obj->add_volume(std::move(tm)); + // set a default extruder value, since user can't add it manually + m_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); } } else { + // update volume m_volume->set_mesh(std::move(tm)); m_volume->set_new_unique_id(); m_volume->calculate_convex_hull(); m_volume->get_object()->invalidate_bounding_box(); } m_volume->name = name; - - // set a default extruder value, since user can't add it manually - m_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); m_volume->text_configuration = create_configuration(); // update volume name in object list @@ -543,7 +569,7 @@ void GLGizmoEmboss::draw_window() bool loaded = load_font(font_index); } #endif // ALLOW_DEBUG_MODE - if (!m_font.has_value()) { + if (m_font == nullptr) { ImGui::Text(_u8L("Warning: No font is selected. Select correct one.").c_str()); } draw_font_list(); @@ -562,7 +588,7 @@ void GLGizmoEmboss::draw_window() if (ImGui::Button(_u8L("Close").c_str())) close(); // Option to create text volume when reselecting volumes - m_imgui->disabled_begin(!m_font.has_value()); + m_imgui->disabled_begin(m_font == nullptr); if (m_volume == nullptr) { ImGui::SameLine(); if (ImGui::Button(_u8L("Generate preview").c_str())) process(); @@ -694,7 +720,7 @@ void GLGizmoEmboss::draw_advanced() } } load_imgui_font(); - if (m_font.has_value()) m_font->cache.clear(); + if (m_font != nullptr) m_font->cache.clear(); process(); } ImGui::SetNextItemWidth(m_gui_cfg->advanced_input_width); @@ -710,7 +736,7 @@ void GLGizmoEmboss::draw_advanced() process(); // when more collection add selector - if (m_font.has_value() && m_font->count > 1) { + if (m_font != nullptr && m_font->count > 1) { ImGui::SetNextItemWidth(m_gui_cfg->advanced_input_width); if (ImGui::BeginCombo(_u8L("Font collection").c_str(), std::to_string(m_font->index).c_str())) { @@ -789,19 +815,23 @@ bool GLGizmoEmboss::create_default_model_object() void GLGizmoEmboss::refresh_object_list() { const Selection &selection = m_parent.get_selection(); - if (selection.is_empty() || selection.get_object_idx() < 0 || + if (selection.is_empty() || + selection.get_object_idx() < 0 || m_volume == nullptr) return; - ObjectList * obj_list = wxGetApp().obj_list(); - ModelVolume *volume = m_volume; // copy for lambda - auto add_to_selection = [volume](const ModelVolume *vol) { - return vol == volume; - }; - wxDataViewItemArray sel = - obj_list->reorder_volumes_and_get_selection(selection.get_object_idx(), - add_to_selection); - if (!sel.IsEmpty()) obj_list->select_item(sel.front()); + ObjectList * obj_list = wxGetApp().obj_list(); + ModelVolume *volume = m_volume; // copy for lambda + + // 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;}; + wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection( + selection.get_object_idx(), add_to_selection); + + if (!sel.IsEmpty()) + obj_list->select_item(sel.front()); + obj_list->selection_changed(); } @@ -824,7 +854,8 @@ bool GLGizmoEmboss::load_font() std::optional font_opt = Emboss::load_font( fi.path.c_str()); if (!font_opt.has_value()) return false; - m_font = font_opt; + // TODO: fix copy of font data + m_font = std::make_shared(std::move(*font_opt)); load_imgui_font(); return true; } @@ -842,7 +873,8 @@ bool GLGizmoEmboss::load_font(const wxFont &font) { auto font_opt = WxFontUtils::load_font(font); if (!font_opt.has_value()) return false; - m_font = font_opt; + // TODO: fix copy of font data + m_font = std::make_shared(std::move(*font_opt)); WxFontUtils::update_property(m_font_prop, font); m_font_prop.emboss = m_font_prop.size_in_mm / 2.f; load_imgui_font(); @@ -888,7 +920,7 @@ void GLGizmoEmboss::check_imgui_font_range() void GLGizmoEmboss::load_imgui_font() { - if (!m_font.has_value()) return; + if (m_font == nullptr) return; ImFontGlyphRangesBuilder builder; builder.AddRanges(m_imgui->get_glyph_ranges()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 43e20c768a..ea3c42dcae 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "libslic3r/Emboss.hpp" #include "libslic3r/Point.hpp" @@ -20,6 +21,8 @@ class wxFont; namespace Slic3r{class AppConfig;} namespace Slic3r::GUI { +class EmbossJob; + class GLGizmoEmboss : public GLGizmoBase { public: @@ -115,21 +118,13 @@ private: FontList m_font_list; size_t m_font_selected;// index to m_font_list - std::optional m_font; - + // to share data with job thread + std::shared_ptr m_font; std::string m_text; - FontProp m_font_prop; - // text position - struct Orientation - { - Vec3f origin = Vec3f(0.f, 0.f, 0.f); - Vec3f normal = Vec3f(0.f, 0.f, 1.f); - Vec3f up = Vec3f(0.f, 1.f, 0.f); - Orientation() = default; - }; - Orientation m_orientation; + // thread to process object on change text + std::unique_ptr m_job; // actual volume ModelVolume *m_volume; diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp new file mode 100644 index 0000000000..fe731710ae --- /dev/null +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -0,0 +1,160 @@ +#include "EmbossJob.hpp" + +#include "libslic3r/Model.hpp" + +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/MainFrame.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/GUI_App.hpp" + +using namespace Slic3r; +using namespace GUI; + +//EmbossJob::EmbossJob(): Job(std::make_shared(wxGetApp().plater()->get_notification_manager())){} +EmbossJob::EmbossJob() : Job(std::make_shared()) {} + +void EmbossJob::restart(const Data &data) +{ + if (Job::is_running()) { + std::lock_guard lk(m_mutex); + bool create_restart = !m_data_next.has_value(); + m_data_next = data; // copy + if (create_restart) { + if (m_restart_thread.joinable()) m_restart_thread.join(); + m_restart_thread = std::thread([this]() { + Job::cancel(); + Job::join(); + std::lock_guard lk(m_mutex); + 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; + + // Do NOT process empty string + const TextConfiguration &cfg = m_data->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); + + // 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); + + // for sure that some object is created from shape + if (its.indices.empty()) return; + m_result = its; +} + +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 = {}; + + // 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(); + std::string name = m_data->volume_name; + + + plater->take_snapshot(_L("Emboss text") + ": " + name); + ModelVolume *volume = m_data->volume_ptr; + if (volume == nullptr) { + // decide to add as volume or new object + if (m_data->object_ptr == nullptr) { + // create new object + app.obj_list()->load_mesh_object(tm, name, true, &m_data->text_configuration); + app.mainframe->update_title(); + + // TODO: find Why ??? + // load mesh cause close gizmo, on windows but not on linux + // Open gizmo again when it is closed + GLGizmosManager &mng = canvas->get_gizmos_manager(); + if (mng.get_current_type() != GLGizmosManager::Emboss) + mng.open_gizmo(GLGizmosManager::Emboss); + return; + } else { + // create new volume inside of object + ModelObject *obj = m_data->object_ptr; + 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)); + } + } else { + // update volume + volume->set_mesh(std::move(tm)); + volume->set_new_unique_id(); + volume->calculate_convex_hull(); + volume->get_object()->invalidate_bounding_box(); + } + + volume->name = name; + volume->text_configuration = m_data->text_configuration; + + // update volume name in object list + select_volume(volume); + + //ObjectList *obj_list = wxGetApp().obj_list(); + //obj_list->selection_changed(); + + // after applied another font notification is no more valid + //remove_notification_not_valid_font(); + + // Job promiss to refresh is not working + canvas->reload_scene(true); + + // set data to model + Job::finalize(); +} + +void EmbossJob::select_volume(ModelVolume *volume) +{ + if (volume == nullptr) return; + + 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(); + wxDataViewItemArray sel = + obj_list->reorder_volumes_and_get_selection(selection.get_object_idx(), + add_to_selection); + + if (!sel.IsEmpty()) obj_list->select_item(sel.front()); + obj_list->selection_changed(); +} \ No newline at end of file diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp new file mode 100644 index 0000000000..12e326751d --- /dev/null +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -0,0 +1,67 @@ +#ifndef slic3r_EmbossJob_hpp_ +#define slic3r_EmbossJob_hpp_ + +#include +#include "Job.hpp" + +#include "libslic3r/Emboss.hpp" + +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 +{ +public: + struct Data + { + std::shared_ptr font; + TextConfiguration text_configuration; + std::string volume_name; + // when nulltrp new volume will be created + ModelVolume *volume_ptr; + // when nullptr volume is created in new object + ModelObject *object_ptr; + }; + EmbossJob(); + void restart(const Data &data); +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; + std::thread m_restart_thread; + + // 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; } + }; +}; +} // namespace Slic3r::GUI + +#endif // slic3r_EmbossJob_hpp_