Cancel only text update job

This commit is contained in:
Filip Sykala 2022-03-29 17:05:24 +02:00
parent feb9eda0c8
commit 394a59d44f
4 changed files with 143 additions and 76 deletions

View file

@ -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<bool>(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<bool>(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<std::atomic<bool> >(false);
EmbossDataUpdate data{font, create_configuration(), create_volume_name(),
m_volume->id(), m_update_job_cancel};
queue_job(worker, std::make_unique<EmbossUpdateJob>(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
/// <summary>
/// Keep list of loadable OS fonts
/// Filtrate which can be loaded.
/// Sort alphanumerical.
/// </summary>
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<std::string> 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<std::string> m_efacenames;
#endif // DEBUG_NOT_LOADABLE_FONTS
protected:
/// <summary>
/// Called by wxFontEnumerator::EnumerateFacenames() for each match.
/// </summary>
/// <param name="facename">name identificator to load face by wxFont</param>
/// <returns> True to continue enumeration or false to stop it.</returns>
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<wxFont> &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();

View file

@ -14,6 +14,7 @@
#include <memory>
#include <mutex>
#include <thread>
#include <atomic>
#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<bool> m_update_job_cancel;
std::shared_ptr<std::atomic<bool>> m_update_job_cancel;
// actual volume
ModelVolume *m_volume;

View file

@ -28,12 +28,13 @@ namespace priv{
/// <param name="font">Define shape of characters.
/// NOTE: Can't be const cache glyphs</param>
/// <param name="font_prop">Property of font</param>
/// <param name="ctl">Control for job, check of cancelation</param>
/// <param name="was_canceled">Lambda returning bool to check if process was canceled</param>
/// <returns>Triangle mesh model</returns>
template<typename Fnc>
static TriangleMesh create_mesh(const char *text,
Emboss::FontFileWithCache &font,
const FontProp &font_prop,
GUI::Job::Ctl &ctl);
Fnc was_canceled);
/// <summary>
/// Create default mesh for embossed text
/// </summary>
@ -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<typename Fnc>
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<Emboss::ProjectZ>(depth);
Emboss::ProjectScale project(std::move(projectZ), scale);
if (ctl.was_canceled()) return {};
if (was_canceled()) return {};
return TriangleMesh(Emboss::polygons2model(shapes, project));
}

View file

@ -1,6 +1,9 @@
#ifndef slic3r_EmbossJob_hpp_
#define slic3r_EmbossJob_hpp_
#include <atomic>
#include <memory>
#include <string>
#include <libslic3r/Emboss.hpp>
#include <libslic3r/ModelVolumeType.hpp>
#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<bool> cancel;
std::shared_ptr<std::atomic<bool> > cancel;
};
/// <summary>
@ -83,14 +86,20 @@ struct EmbossDataCreateObject : public EmbossDataBase
/// <summary>
/// Update text shape in existing text volume
/// Predict that there is only one runnig(not canceled) instance of it
/// </summary>
class EmbossUpdateJob : public Job
{
EmbossDataUpdate m_input;
TriangleMesh m_result;
public:
// only move params to private variable
EmbossUpdateJob(EmbossDataUpdate&& input);
/// <summary>
/// Create volume movel by input data
/// </summary>
/// <param name="ctl">Control containing cancel flag</param>
void process(Ctl &ctl) override;
/// <summary>
@ -106,6 +115,7 @@ public:
/// <summary>
/// Create new TextVolume on the surface of ModelObject
/// Should not be stopped
/// </summary>
class EmbossCreateVolumeJob : public Job
{
@ -121,6 +131,7 @@ public:
/// <summary>
/// Create new TextObject on the platter
/// Should not be stopped
/// </summary>
class EmbossCreateObjectJob : public Job
{