diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 1ba6590a8f..7526ca04cd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -69,7 +69,7 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) , m_is_initialized(false) // initialize on first opening gizmo , m_rotate_gizmo(parent, GLGizmoRotate::Axis::Z) // grab id = 2 (Z axis) , m_font_manager(m_imgui->get_glyph_ranges()) - , m_update_job_cancel(std::make_shared(false)) + , m_update_job_cancel(nullptr) { m_rotate_gizmo.set_group_id(0); // TODO: add suggestion to use https://fontawesome.com/ @@ -749,20 +749,26 @@ bool GLGizmoEmboss::process() // no volume is selected -> selection from right panel if (m_volume == nullptr) return false; - // exist loaded font? - Emboss::FontFileWithCache font = m_font_manager.get_font().font_file_with_cache; - if (!font.has_value()) return false; - auto &worker = wxGetApp().plater()->get_ui_job_worker(); - // cancel must be befor create new data - - *m_update_job_cancel = true; // set old job to cancel - m_update_job_cancel = std::make_shared(false); // create new shared ptr + // without text there is no to emboss + if (m_text.empty()) return false; - // IMPROVE: Cancel only EmbossUpdateJob no others - worker.cancel(); + // exist loaded font + if (!m_font_manager.is_activ_font()) return false; + Emboss::FontFileWithCache font = m_font_manager.get_font().font_file_with_cache; + assert(font.has_value()); + if (!font.has_value()) return false; + + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + // Can't use cancel, because I want cancel only previous update job + // worker.cancel(); + + // Cancel only EmbossUpdateJob no others + if (m_update_job_cancel != nullptr) + m_update_job_cancel->store(true); + // create new shared ptr to cancel new job + m_update_job_cancel = std::make_shared >(false); EmbossDataUpdate data{font, create_configuration(), create_volume_name(), m_volume->id(), m_update_job_cancel}; - queue_job(worker, std::make_unique(std::move(data))); // notification is removed befor object is changed by job @@ -944,58 +950,90 @@ void GLGizmoEmboss::draw_text_input() if (exist_change) m_font_manager.clear_imgui_font(); } -void GLGizmoEmboss::draw_font_list() +//#define DEBUG_NOT_LOADABLE_FONTS + +/// +/// Keep list of loadable OS fonts +/// Filtrate which can be loaded. +/// Sort alphanumerical. +/// +class MyFontEnumerator : public wxFontEnumerator { - class MyFontEnumerator : public wxFontEnumerator - { - wxArrayString m_facenames; - wxFontEncoding m_encoding; - bool m_fixed_width_only; - bool m_is_init; - public: - MyFontEnumerator(wxFontEncoding encoding, bool fixed_width_only) - : m_encoding(encoding) - , m_fixed_width_only(fixed_width_only) - , m_is_init(false) - {} - const wxArrayString& get_facenames() const{ return m_facenames; } - bool is_init() const { return m_is_init; } - bool init() { - if (m_is_init) return false; - m_is_init = true; - if (!wxFontEnumerator::EnumerateFacenames(m_encoding, m_fixed_width_only)) return false; - if (m_facenames.empty()) return false; - std::sort(m_facenames.begin(), m_facenames.end()); - return true; - } + wxArrayString m_facenames; + wxFontEncoding m_encoding; + bool m_fixed_width_only; + bool m_is_init; +public: + MyFontEnumerator() + : m_encoding(wxFontEncoding::wxFONTENCODING_SYSTEM) + , m_fixed_width_only(false) + , m_is_init(false) + {} + const wxArrayString& get_facenames() const { return m_facenames; } + const wxFontEncoding& get_encoding() const { return m_encoding; } + bool is_init() const { return m_is_init; } + bool init() { + if (m_is_init) return false; + m_is_init = true; + if (!wxFontEnumerator::EnumerateFacenames(m_encoding, m_fixed_width_only)) return false; + if (m_facenames.empty()) return false; + std::sort(m_facenames.begin(), m_facenames.end()); + return true; + } - std::vector m_efacenames; - protected: - virtual bool OnFacename(const wxString& facename) wxOVERRIDE { - // vertical font start with @, we will filter it out - if (facename.empty() || facename[0] == '@') return true; - wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(m_encoding)); - //* - if (!WxFontUtils::can_load(wx_font)) return true; // can't load - /*/ - auto ff = WxFontUtils::create_font_file(wx_font); - if (ff == nullptr) { - m_efacenames.emplace_back(facename.c_str()); - return true; // can't create font file - } // */ - m_facenames.Add(facename); - return true; - } - }; - wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM; - bool fixed_width_only = false; - static MyFontEnumerator fontEnumerator(encoding, fixed_width_only); + void del_facename(const wxString &facename) { + + } +#ifdef DEBUG_NOT_LOADABLE_FONTS + std::vector m_efacenames; +#endif // DEBUG_NOT_LOADABLE_FONTS + +protected: + /// + /// Called by wxFontEnumerator::EnumerateFacenames() for each match. + /// + /// name identificator to load face by wxFont + /// True to continue enumeration or false to stop it. + virtual bool OnFacename(const wxString& facename) wxOVERRIDE { + // vertical font start with @, we will filter it out + if (facename.empty() || facename[0] == '@') return true; + wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(m_encoding)); + + //* + // Faster chech if wx_font is loadable but not 100% + // names could contain not loadable font + if (!WxFontUtils::can_load(wx_font)) { +#ifdef DEBUG_NOT_LOADABLE_FONTS + m_efacenames.emplace_back(facename.c_str()); +#endif // DEBUG_NOT_LOADABLE_FONTS + return true; // can't load + } + /*/ + // Slow copy of font files to try load font + // After this all files are loadable + auto ff = WxFontUtils::create_font_file(wx_font); + if (ff == nullptr) { +#ifdef DEBUG_NOT_LOADABLE_FONTS + m_efacenames.emplace_back(facename.c_str()); +#endif // DEBUG_NOT_LOADABLE_FONTS + return true; // can't create font file + } // */ + m_facenames.Add(facename); + return true; + } +}; + +void GLGizmoEmboss::draw_font_list() +{ + static MyFontEnumerator fontEnumerator; std::optional &wx_font_opt = m_font_manager.get_wx_font(); wxString actual_face_name = wx_font_opt.has_value() ? wx_font_opt->GetFaceName() : ""; const char * selected = (!actual_face_name.empty()) ? actual_face_name.ToUTF8().data() : " --- "; + + wxString del_facename; if (ImGui::BeginCombo("##font_selector", selected)) { if(!fontEnumerator.is_init()) fontEnumerator.init(); const wxArrayString &face_names = fontEnumerator.get_facenames(); @@ -1006,7 +1044,7 @@ void GLGizmoEmboss::draw_font_list() if (ImGui::Selectable(face_name.ToUTF8().data(), is_selected) && wxFontEnumerator::IsValidFacename(face_name)) { // Select font - wxFont wx_font(wxFontInfo().FaceName(face_name).Encoding(encoding)); + wxFont wx_font(wxFontInfo().FaceName(face_name).Encoding(fontEnumerator.get_encoding())); if(m_font_manager.set_wx_font(wx_font)) process(); } @@ -1019,6 +1057,10 @@ void GLGizmoEmboss::draw_font_list() ImGui::EndCombo(); } + // delete unloadable face name when appear + if (!del_facename.empty()) + fontEnumerator.del_facename(del_facename); + #ifdef ALLOW_ADD_FONT_BY_FILE ImGui::SameLine(); // select font file by file browser @@ -1318,7 +1360,8 @@ void GLGizmoEmboss::draw_style_list() { m_font_manager = FontManager(m_imgui->get_glyph_ranges()); FontList font_list = create_default_font_list(); m_font_manager.add_fonts(font_list); - // TODO: What to do when NO one default font is loadable? + // What to do when NO one default font is loadable? + [[maybe_unused]] bool success = m_font_manager.load_first_valid_font(); assert(success); select_stored_font_item(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index e2799f476f..648d9df8bc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "libslic3r/Emboss.hpp" #include "libslic3r/Point.hpp" @@ -207,7 +208,7 @@ private: std::string m_text; // cancel for previous update of volume to cancel finalize part - std::shared_ptr m_update_job_cancel; + std::shared_ptr> m_update_job_cancel; // actual volume ModelVolume *m_volume; diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index 34cae5c96c..85b5b12eef 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -28,12 +28,13 @@ namespace priv{ /// Define shape of characters. /// NOTE: Can't be const cache glyphs /// Property of font -/// Control for job, check of cancelation +/// Lambda returning bool to check if process was canceled /// Triangle mesh model +template static TriangleMesh create_mesh(const char *text, Emboss::FontFileWithCache &font, const FontProp &font_prop, - GUI::Job::Ctl &ctl); + Fnc was_canceled); /// /// Create default mesh for embossed text /// @@ -46,18 +47,27 @@ static TriangleMesh create_default_mesh(); /// Update Volume EmbossUpdateJob::EmbossUpdateJob(EmbossDataUpdate&& input) : m_input(std::move(input)) -{} +{ + assert(m_input.cancel != nullptr); + assert(m_input.font_file.has_value()); + assert(!m_input.text_configuration.text.empty()); +} void EmbossUpdateJob::process(Ctl &ctl) { + auto was_canceled = [&ctl, &cancel = m_input.cancel]()->bool { + if (cancel->load()) return true; + return ctl.was_canceled(); + }; + // check if exist valid font if (!m_input.font_file.has_value()) return; const TextConfiguration &cfg = m_input.text_configuration; m_result = priv::create_mesh(cfg.text.c_str(), m_input.font_file, - cfg.font_item.prop, ctl); + cfg.font_item.prop, was_canceled); if (m_result.its.empty()) return; - if (ctl.was_canceled()) return; + if (was_canceled()) return; // center triangle mesh Vec3d shift = m_result.bounding_box().center(); @@ -66,7 +76,7 @@ void EmbossUpdateJob::process(Ctl &ctl) void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &) { - if (canceled || *m_input.cancel) return; + if (canceled || m_input.cancel->load()) return; // for sure that some object is created from shape if (m_result.its.indices.empty()) return; @@ -133,11 +143,11 @@ void EmbossCreateVolumeJob::process(Ctl &ctl) { // Emboss text window is opened by creation new emboss text object const char *text = m_input.text_configuration.text.c_str(); FontProp &prop = m_input.text_configuration.font_item.prop; - - m_result = priv::create_mesh(text, m_input.font_file, prop, ctl); + auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); }; + m_result = priv::create_mesh(text, m_input.font_file, prop, was_canceled); if (m_result.its.empty()) m_result = priv::create_default_mesh(); - if (ctl.was_canceled()) return; + if (was_canceled()) return; // Create new volume inside of object const FontProp &font_prop = m_input.text_configuration.font_item.prop; @@ -217,15 +227,16 @@ void EmbossCreateObjectJob::process(Ctl &ctl) // It is neccessary to create some shape // Emboss text window is opened by creation new emboss text object const char *text = m_input.text_configuration.text.c_str(); - FontProp &prop = m_input.text_configuration.font_item.prop; + FontProp &prop = m_input.text_configuration.font_item.prop; + auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); }; if (!m_input.font_file.has_value()) m_result = priv::create_default_mesh(); else - m_result = priv::create_mesh(text, m_input.font_file, prop, ctl); + m_result = priv::create_mesh(text, m_input.font_file, prop, was_canceled); if (m_result.its.empty()) m_result = priv::create_default_mesh(); - if (ctl.was_canceled()) return; + if (was_canceled()) return; // Create new object // calculate X,Y offset position for lay on platter in place of @@ -281,24 +292,25 @@ void EmbossCreateObjectJob::finalize(bool canceled, std::exception_ptr &) //////////////////////////// /// private namespace implementation +template TriangleMesh priv::create_mesh(const char *text, Emboss::FontFileWithCache &font, const FontProp &font_prop, - GUI::Job::Ctl &ctl) + Fnc was_canceled) { assert(font.has_value()); if (!font.has_value()) return {}; ExPolygons shapes = Emboss::text2shapes(font, text, font_prop); if (shapes.empty()) return {}; - if (ctl.was_canceled()) return {}; + if (was_canceled()) return {}; int unit_per_em = font.font_file->unit_per_em; float scale = font_prop.size_in_mm / unit_per_em; float depth = font_prop.emboss / scale; auto projectZ = std::make_unique(depth); Emboss::ProjectScale project(std::move(projectZ), scale); - if (ctl.was_canceled()) return {}; + if (was_canceled()) return {}; return TriangleMesh(Emboss::polygons2model(shapes, project)); } diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index 1e7d1ad2f8..cc7cae31b5 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -1,6 +1,9 @@ #ifndef slic3r_EmbossJob_hpp_ #define slic3r_EmbossJob_hpp_ +#include +#include +#include #include #include #include "slic3r/Utils/RaycastManager.hpp" @@ -37,7 +40,7 @@ struct EmbossDataUpdate : public EmbossDataBase // flag that job is canceled // for time after process. - std::shared_ptr cancel; + std::shared_ptr > cancel; }; /// @@ -83,14 +86,20 @@ struct EmbossDataCreateObject : public EmbossDataBase /// /// Update text shape in existing text volume +/// Predict that there is only one runnig(not canceled) instance of it /// class EmbossUpdateJob : public Job { EmbossDataUpdate m_input; TriangleMesh m_result; - public: + // only move params to private variable EmbossUpdateJob(EmbossDataUpdate&& input); + + /// + /// Create volume movel by input data + /// + /// Control containing cancel flag void process(Ctl &ctl) override; /// @@ -106,6 +115,7 @@ public: /// /// Create new TextVolume on the surface of ModelObject +/// Should not be stopped /// class EmbossCreateVolumeJob : public Job { @@ -121,6 +131,7 @@ public: /// /// Create new TextObject on the platter +/// Should not be stopped /// class EmbossCreateObjectJob : public Job {