diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index d1e5eb0d8f..50b6071cf9 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -20,9 +20,6 @@ #include "libslic3r/Line.hpp" using namespace Slic3r; - -double Emboss::SHAPE_SCALE = 0.001;//SCALING_FACTOR; - // do not expose out of this file stbtt_ data types namespace priv{ diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index abe0c125ff..e188d3fe6e 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -16,23 +16,21 @@ namespace Slic3r { /// class with only static function add ability to engraved OR raised /// text OR polygons onto model surface /// -class Emboss -{ -public: - Emboss() = delete; - +namespace Emboss +{ // every glyph's shape point is divided by SHAPE_SCALE - increase precission of fixed point value - static double SHAPE_SCALE; + // stored in fonts (to be able represents curve by sequence of lines) + static constexpr double SHAPE_SCALE = 0.001; // SCALING_FACTOR promile is fine enough /// /// Collect fonts registred inside OS /// /// OS registred TTF font files(full path) with names - static EmbossStyles get_font_list(); + EmbossStyles get_font_list(); #ifdef _WIN32 - static EmbossStyles get_font_list_by_register(); - static EmbossStyles get_font_list_by_enumeration(); - static EmbossStyles get_font_list_by_folder(); + EmbossStyles get_font_list_by_register(); + EmbossStyles get_font_list_by_enumeration(); + EmbossStyles get_font_list_by_folder(); #endif /// @@ -40,7 +38,7 @@ public: /// /// Unique identificator for font /// File path to font when found - static std::optional get_font_path(const std::wstring &font_face_name); + std::optional get_font_path(const std::wstring &font_face_name); // description of one letter struct Glyph @@ -126,14 +124,14 @@ public: /// /// Location of .ttf or .ttc font file /// Font object when loaded. - static std::unique_ptr create_font_file(const char *file_path); + std::unique_ptr create_font_file(const char *file_path); // data = raw file data - static std::unique_ptr create_font_file(std::unique_ptr> data); + std::unique_ptr create_font_file(std::unique_ptr> data); #ifdef _WIN32 // fix for unknown pointer HFONT using HFONT = void*; - static void * can_load(HFONT hfont); - static std::unique_ptr create_font_file(HFONT hfont); + void * can_load(HFONT hfont); + std::unique_ptr create_font_file(HFONT hfont); #endif // _WIN32 /// @@ -144,7 +142,7 @@ public: /// One character defined by unicode codepoint /// Precision of lettter outline curve in conversion to lines /// inner polygon cw(outer ccw) - static std::optional letter2glyph(const FontFile &font, unsigned int font_index, int letter, float flatness); + std::optional letter2glyph(const FontFile &font, unsigned int font_index, int letter, float flatness); /// /// Convert text into polygons @@ -154,14 +152,14 @@ public: /// User defined property of the font /// Way to interupt processing /// Inner polygon cw(outer ccw) - static ExPolygons text2shapes(FontFileWithCache &font, const char *text, const FontProp &font_prop, std::function was_canceled = nullptr); + ExPolygons text2shapes(FontFileWithCache &font, const char *text, const FontProp &font_prop, std::function was_canceled = nullptr); /// /// Fix intersections and self intersections in polygons glyph shape /// /// Input shape to heal /// Healed shapes - static ExPolygons heal_shape(const Polygons &shape); + ExPolygons heal_shape(const Polygons &shape); /// /// NOTE: call Slic3r::union_ex before this call @@ -175,7 +173,7 @@ public: /// Heal could create another issue, /// After healing it is checked again until shape is good or maximal count of iteration /// True when shapes is good otherwise False - static bool heal_shape(ExPolygons &shape, unsigned max_iteration = 10); + bool heal_shape(ExPolygons &shape, unsigned max_iteration = 10); /// /// Divide line segments in place near to point @@ -186,7 +184,7 @@ public: /// Expolygon to edit /// (epsilon)Euclidean distance from point to line which divide line /// True when some division was made otherwise false - static bool divide_segments_for_close_point(ExPolygons &expolygons, double distance); + bool divide_segments_for_close_point(ExPolygons &expolygons, double distance); /// /// Use data from font property to modify transformation @@ -194,7 +192,7 @@ public: /// Z-move as surface distance(FontProp::distance) /// Z-rotation as angle to Y axis(FontProp::angle) /// In / Out transformation to modify by property - static void apply_transformation(const FontProp &font_prop, Transform3d &transformation); + void apply_transformation(const FontProp &font_prop, Transform3d &transformation); /// /// Read information from naming table of font file @@ -203,7 +201,7 @@ public: /// Selector of font /// Index of font in collection /// True when the font description contains italic/obligue otherwise False - static bool is_italic(const FontFile &font, unsigned int font_index); + bool is_italic(const FontFile &font, unsigned int font_index); /// /// Create unique character set from string with filtered from text with only character from font @@ -213,7 +211,7 @@ public: /// Define font in collection /// True when text contain glyph unknown in font /// Unique set of character from text contained in font - static std::string create_range_text(const std::string &text, const FontFile &font, unsigned int font_index, bool* exist_unknown = nullptr); + std::string create_range_text(const std::string &text, const FontFile &font, unsigned int font_index, bool* exist_unknown = nullptr); /// /// Calculate scale for glyph shape convert from shape points to mm @@ -221,7 +219,7 @@ public: /// Property of font /// Font data /// Conversion to mm - static double get_shape_scale(const FontProp &fp, const FontFile &ff); + double get_shape_scale(const FontProp &fp, const FontFile &ff); /// /// Project spatial point @@ -274,7 +272,7 @@ public: /// text or image /// Define transformation from 2d to 3d(orientation, position, scale, ...) /// Projected shape into space - static indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection); + indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection); /// /// Create transformation for emboss text object to lay on surface point @@ -283,7 +281,7 @@ public: /// Normal of surface point /// Is compared with normal.z to suggest up direction /// Transformation onto surface point - static Transform3d create_transformation_onto_surface( + Transform3d create_transformation_onto_surface( const Vec3f &position, const Vec3f &normal, float up_limit = 0.9f); class ProjectZ : public IProjection @@ -345,7 +343,7 @@ public: Vec3d project(const Vec3d &point) const override; std::optional unproject(const Vec3d &p, double * depth = nullptr) const override; }; -}; +} // namespace Emboss } // namespace Slic3r #endif // slic3r_Emboss_hpp_ diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 94b846a5db..d514171f17 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -936,23 +936,8 @@ private: // 1 -> is splittable mutable int m_is_splittable{ -1 }; - ModelVolume(ModelObject *object, const TriangleMesh &mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART) : m_mesh(new TriangleMesh(mesh)), m_type(type), object(object) - { - assert(this->id().valid()); - assert(this->config.id().valid()); - assert(this->supported_facets.id().valid()); - assert(this->seam_facets.id().valid()); - assert(this->mmu_segmentation_facets.id().valid()); - assert(this->id() != this->config.id()); - assert(this->id() != this->supported_facets.id()); - assert(this->id() != this->seam_facets.id()); - assert(this->id() != this->mmu_segmentation_facets.id()); - if (mesh.facets_count() > 1) - calculate_convex_hull(); - } - ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) : - m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(type), object(object) { - assert(this->id().valid()); + inline bool check() { + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->supported_facets.id().valid()); assert(this->seam_facets.id().valid()); @@ -961,6 +946,24 @@ private: assert(this->id() != this->supported_facets.id()); assert(this->id() != this->seam_facets.id()); assert(this->id() != this->mmu_segmentation_facets.id()); + return true; + } + + ModelVolume(ModelObject *object, const TriangleMesh &mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART) : + m_mesh(new TriangleMesh(mesh)), m_type(type), object(object) + { + assert(check()); + if (m_mesh->facets_count() > 1) calculate_convex_hull(); + } + ModelVolume(ModelObject *object, TriangleMesh &&mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART) + : m_mesh(new TriangleMesh(std::move(mesh))), m_type(type), object(object) + { + assert(check()); + if (m_mesh->facets_count() > 1) calculate_convex_hull(); + } + ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) : + m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(type), object(object) { + assert(check()); } // Copying an existing volume, therefore this volume will get a copy of the ID assigned. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 04dc15266a..5c3b5ca771 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -57,7 +57,9 @@ #endif // ALLOW_DEBUG_MODE using namespace Slic3r; +using namespace Slic3r::Emboss; using namespace Slic3r::GUI; +using namespace Slic3r::GUI::Emboss; // anonymous namespace for unique names namespace { @@ -165,6 +167,18 @@ void GLGizmoEmboss::set_fine_position() ImGuiWrapper::draw(rect); } +namespace priv { + +/// +/// Prepare data for emboss +/// +/// Text to emboss +/// Keep actual selected style +/// Base data for emboss text +static DataBase create_emboss_data_base(const std::string &text, StyleManager &style_manager); + +} // namespace priv + void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos) { assert(volume_type == ModelVolumeType::MODEL_PART || @@ -188,11 +202,17 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous Plater* plater = wxGetApp().plater(); const Camera &camera = plater->get_camera(); const Pointfs &bed_shape = plater->build_volume().bed_shape(); - EmbossDataCreateObject data{create_emboss_data_base(), - screen_coor, - camera, - bed_shape}; - auto job = std::make_unique(std::move(data)); + // TODO: Fix double creation of base data (first is inside function start_volume_creation) + DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager); + FontProp& prop = emboss_data.text_configuration.style.prop; + + // can't create new object with using surface + if (prop.use_surface) prop.use_surface = false; + // can't create new object with distance from surface + if (prop.distance.has_value()) prop.distance.reset(); + + DataCreateObject data{std::move(emboss_data), screen_coor, camera, bed_shape}; + auto job = std::make_unique(std::move(data)); Worker &worker = plater->get_ui_job_worker(); queue_job(worker, std::move(job)); } @@ -369,9 +389,9 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // Calculate temporary position Transform3d object_trmat = m_raycast_manager.get_transformation(hit->tr_key); - Transform3d trmat = Emboss::create_transformation_onto_surface(hit->position, hit->normal); + Transform3d trmat = create_transformation_onto_surface(hit->position, hit->normal); const FontProp& font_prop = tc.style.prop; - Emboss::apply_transformation(font_prop, trmat); + apply_transformation(font_prop, trmat); // fix baked transformation from .3mf store process if (tc.fix_3mf_tr.has_value()) @@ -807,8 +827,7 @@ EmbossStyles GLGizmoEmboss::create_default_styles() void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); } -bool GLGizmoEmboss::start_volume_creation(ModelVolumeType volume_type, - const Vec2d &screen_coor) +bool GLGizmoEmboss::start_volume_creation(ModelVolumeType volume_type, const Vec2d &screen_coor) { Plater* plater = wxGetApp().plater(); @@ -848,18 +867,33 @@ bool GLGizmoEmboss::start_volume_creation(ModelVolumeType volume_type, GLVolume *gl_volume = volumes[hovered_id]; Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix(); - // create volume - EmbossDataCreateVolume data{create_emboss_data_base(), - volume_type, - screen_coor, - object_idx_signed, - camera, - *hit, - hit_object_trmat, - hit_instance_trmat}; + DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager); + // Create result volume transformation + Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); + const FontProp& font_prop = emboss_data.text_configuration.style.prop; + apply_transformation(font_prop, surface_trmat); + Transform3d volume_trmat = hit_instance_trmat.inverse() * hit_object_trmat * surface_trmat; + + std::unique_ptr job; + if (!emboss_data.text_configuration.style.prop.use_surface) { + // create volume + DataCreateVolume data{std::move(emboss_data), volume_type, object_idx_signed, volume_trmat}; + job = std::make_unique(std::move(data)); + } else { + // Model to cut surface from. + SurfaceVolumeData::ModelSources sources = create_sources(obj->volumes); + if (sources.empty()) return false; + + bool is_outside = volume_type == ModelVolumeType::MODEL_PART; + // check that there is not unexpected volume type + assert(is_outside || + volume_type == ModelVolumeType::NEGATIVE_VOLUME || + volume_type == ModelVolumeType::PARAMETER_MODIFIER); + CreateSurfaceVolumeData surface_data{std::move(emboss_data), volume_trmat, is_outside, std::move(sources), volume_type, object_idx_signed}; + job = std::make_unique(std::move(surface_data)); + } Worker &worker = plater->get_ui_job_worker(); - auto job = std::make_unique(std::move(data)); queue_job(worker, std::move(job)); return true; } @@ -957,7 +991,7 @@ bool GLGizmoEmboss::process() m_update_job_cancel->store(true); // create new shared ptr to cancel new job m_update_job_cancel = std::make_shared >(false); - EmbossDataUpdate data{create_emboss_data_base(), m_volume->id(), m_update_job_cancel}; + DataUpdate data{priv::create_emboss_data_base(m_text, m_style_manager), m_volume->id(), m_update_job_cancel}; std::unique_ptr job = nullptr; @@ -965,7 +999,7 @@ bool GLGizmoEmboss::process() const TextConfiguration &tc = data.text_configuration; if (tc.style.prop.use_surface) { // Model to cut surface from. - UseSurfaceData::ModelSources sources = UseSurfaceData::create_sources(m_volume); + SurfaceVolumeData::ModelSources sources = create_volume_sources(m_volume); if (sources.empty()) return false; Transform3d text_tr = m_volume->get_matrix(); @@ -977,11 +1011,10 @@ bool GLGizmoEmboss::process() // check that there is not unexpected volume type assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier()); - UseSurfaceData surface_data{std::move(data), text_tr, is_outside, - std::move(sources)}; - job = std::make_unique(std::move(surface_data)); + UpdateSurfaceVolumeData surface_data{std::move(data), text_tr, is_outside, std::move(sources)}; + job = std::make_unique(std::move(surface_data)); } else { - job = std::make_unique(std::move(data)); + job = std::make_unique(std::move(data)); } //* @@ -1024,7 +1057,7 @@ void GLGizmoEmboss::close() void GLGizmoEmboss::discard_and_close() { if (!m_unmodified_volume.has_value()) return; m_volume->set_transformation(m_unmodified_volume->tr); - EmbossUpdateJob::update_volume(m_volume, std::move(m_unmodified_volume->tm), m_unmodified_volume->tc, m_unmodified_volume->name); + UpdateJob::update_volume(m_volume, std::move(m_unmodified_volume->tm), m_unmodified_volume->tc, m_unmodified_volume->name); close(); //auto plater = wxGetApp().plater(); @@ -1139,18 +1172,18 @@ void GLGizmoEmboss::draw_window() void GLGizmoEmboss::draw_text_input() { - auto create_range_text = [&mng = m_style_manager, &text = m_text, &exist_unknown = m_text_contain_unknown_glyph]() { + auto create_range_text_prep = [&mng = m_style_manager, &text = m_text, &exist_unknown = m_text_contain_unknown_glyph]() { auto& ff = mng.get_font_file_with_cache(); assert(ff.has_value()); const auto &cn = mng.get_font_prop().collection_number; unsigned int font_index = (cn.has_value()) ? *cn : 0; - return Emboss::create_range_text(text, *ff.font_file, font_index, &exist_unknown); + return create_range_text(text, *ff.font_file, font_index, &exist_unknown); }; ImFont *imgui_font = m_style_manager.get_imgui_font(); if (imgui_font == nullptr) { // try create new imgui font - m_style_manager.create_imgui_font(create_range_text()); + m_style_manager.create_imgui_font(create_range_text_prep()); imgui_font = m_style_manager.get_imgui_font(); } bool exist_font = @@ -1190,10 +1223,10 @@ void GLGizmoEmboss::draw_text_input() if (prop.line_gap.has_value()) append_warning(_u8L("Line gap"), _u8L("Unsupported visualization of gap between lines inside text input.")); auto &ff = m_style_manager.get_font_file_with_cache(); - float imgui_size = EmbossStyleManager::get_imgui_font_size(prop, *ff.font_file); - if (imgui_size > EmbossStyleManager::max_imgui_font_size) + float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file); + if (imgui_size > StyleManager::max_imgui_font_size) append_warning(_u8L("To tall"), _u8L("Diminished font height inside text input.")); - if (imgui_size < EmbossStyleManager::min_imgui_font_size) + if (imgui_size < StyleManager::min_imgui_font_size) append_warning(_u8L("To small"), _u8L("Enlarged font height inside text input.")); if (!who.empty()) warning = GUI::format(_L("%1% is NOT shown."), who); } @@ -1217,7 +1250,7 @@ void GLGizmoEmboss::draw_text_input() const ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_AutoSelectAll; if (ImGui::InputTextMultiline("##Text", &m_text, text_size, flags)) { process(); - range_text = create_range_text(); + range_text = create_range_text_prep(); } if (exist_font) ImGui::PopFont(); @@ -1571,7 +1604,7 @@ void GLGizmoEmboss::draw_font_list() // When is unknown font is inside .3mf only font selection is allowed // Stop Imgui disable + Guard again start disabling ScopeGuard unknown_font_sc; - if (m_is_unknown_font) { + if (m_is_unknown_font) { m_imgui->disabled_end(); unknown_font_sc = ScopeGuard([&]() { m_imgui->disabled_begin(true); @@ -1963,7 +1996,7 @@ void GLGizmoEmboss::draw_delete_style_button() { void GLGizmoEmboss::draw_revert_all_styles_button() { if (draw_button(IconType::revert_all)) { - m_style_manager = EmbossStyleManager(m_imgui->get_glyph_ranges()); + m_style_manager = StyleManager(m_imgui->get_glyph_ranges()); m_style_manager.init(nullptr, create_default_styles()); assert(m_style_manager.get_font_file_with_cache().has_value()); process(); @@ -2029,7 +2062,7 @@ void GLGizmoEmboss::draw_style_list() { m_style_manager.init_style_images(m_gui_cfg->max_style_image_size, m_text); m_style_manager.init_trunc_names(max_style_name_width); std::optional> swap_indexes; - const std::vector &styles = m_style_manager.get_styles(); + const std::vector &styles = m_style_manager.get_styles(); for (const auto &item : styles) { size_t index = &item - &styles.front(); const EmbossStyle &style = item.style; @@ -2038,7 +2071,7 @@ void GLGizmoEmboss::draw_style_list() { bool is_selected = (index == m_style_manager.get_style_index()); ImVec2 select_size(0,m_gui_cfg->max_style_image_size.y()); // 0,0 --> calculate in draw - const std::optional &img = item.image; + const std::optional &img = item.image; // allow click delete button ImGuiSelectableFlags_ flags = ImGuiSelectableFlags_AllowItemOverlap; if (ImGui::Selectable(item.truncated_name.c_str(), is_selected, flags, select_size)) { @@ -2918,9 +2951,9 @@ bool GLGizmoEmboss::choose_svg_file() for (const auto &p : polys) bb.merge(p.contour.points); const FontProp &fp = m_style_manager.get_style().prop; float scale = fp.size_in_mm / std::max(bb.max.x(), bb.max.y()); - auto project = std::make_unique( - std::make_unique(fp.emboss / scale), scale); - indexed_triangle_set its = Emboss::polygons2model(polys, *project); + auto project = std::make_unique( + std::make_unique(fp.emboss / scale), scale); + indexed_triangle_set its = polygons2model(polys, *project); return false; // test store: // for (auto &poly : polys) poly.scale(1e5); @@ -2929,41 +2962,38 @@ bool GLGizmoEmboss::choose_svg_file() //return add_volume(name, its); } -EmbossDataBase GLGizmoEmboss::create_emboss_data_base() { +DataBase priv::create_emboss_data_base(const std::string &text, StyleManager& style_manager) +{ auto create_volume_name = [&]() { - bool contain_enter = m_text.find('\n') != std::string::npos; + bool contain_enter = text.find('\n') != std::string::npos; std::string text_fixed; if (contain_enter) { // change enters to space - text_fixed = m_text; // copy - std::replace(text_fixed.begin(), text_fixed.end(), '\n', ' '); + text_fixed = text; // copy + std::replace(text_fixed.begin(), text_fixed.end(), '\n', ' '); } - return _u8L("Text") + " - " + ((contain_enter) ? text_fixed : m_text); + return _u8L("Text") + " - " + ((contain_enter) ? text_fixed : text); }; - + auto create_configuration = [&]() -> TextConfiguration { - if (!m_style_manager.is_activ_font()) { - std::string default_text_for_emboss = _u8L("Embossed text"); - EmbossStyle es = m_style_manager.get_style(); + if (!style_manager.is_activ_font()) { + std::string default_text_for_emboss = _u8L("Embossed text"); + EmbossStyle es = style_manager.get_style(); TextConfiguration tc{es, default_text_for_emboss}; // TODO: investigate how to initialize return tc; } - EmbossStyle &es = m_style_manager.get_style(); + EmbossStyle &es = style_manager.get_style(); // actualize font path - during changes in gui it could be corrupted // volume must store valid path - assert(m_style_manager.get_wx_font().has_value()); - assert(es.path.compare(WxFontUtils::store_wxFont(*m_style_manager.get_wx_font())) == 0); - //style.path = WxFontUtils::store_wxFont(*m_style_manager.get_wx_font()); - return TextConfiguration{es, m_text}; - }; - - return EmbossDataBase{ - m_style_manager.get_font_file_with_cache(), - create_configuration(), - create_volume_name() + assert(style_manager.get_wx_font().has_value()); + assert(es.path.compare(WxFontUtils::store_wxFont(*style_manager.get_wx_font())) == 0); + // style.path = WxFontUtils::store_wxFont(*m_style_manager.get_wx_font()); + return TextConfiguration{es, text}; }; + + return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), create_configuration(), create_volume_name()}; } bool GLGizmoEmboss::load_configuration(ModelVolume *volume) @@ -2974,7 +3004,7 @@ bool GLGizmoEmboss::load_configuration(ModelVolume *volume) const TextConfiguration &tc = *tc_opt; const EmbossStyle &style = tc.style; - auto has_same_name = [&style](const EmbossStyleManager::Item &style_item) -> bool { + auto has_same_name = [&style](const StyleManager::Item &style_item) -> bool { const EmbossStyle &es = style_item.style; return es.name == style.name; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index e1a5c5b050..9df7722e60 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -34,8 +34,6 @@ namespace Slic3r{ namespace Slic3r::GUI { class MeshRaycaster; -struct EmbossDataBase; - class GLGizmoEmboss : public GLGizmoBase { public: @@ -157,8 +155,6 @@ private: bool choose_true_type_file(); bool choose_svg_file(); - // prepare base data for emboss text - EmbossDataBase create_emboss_data_base(); bool load_configuration(ModelVolume *volume); // When open text loaded from .3mf it could be written with unknown font @@ -225,7 +221,7 @@ private: std::optional m_set_window_offset; bool m_is_advanced_edit_style = false; - EmbossStyleManager m_style_manager; + Emboss::StyleManager m_style_manager; struct FaceName{ wxString wx_name; diff --git a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp index 6da3695fed..d1a671330a 100644 --- a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp +++ b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp @@ -15,11 +15,13 @@ #include "slic3r/GUI/GLCanvas3D.hpp" using namespace Slic3r; +using namespace Slic3r::Emboss; using namespace Slic3r::GUI; +using namespace Slic3r::GUI::Emboss; CreateFontStyleImagesJob::CreateFontStyleImagesJob( - EmbossStyleManager::StyleImagesData &&input) + StyleManager::StyleImagesData &&input) : m_input(std::move(input)) { assert(m_input.result != nullptr); @@ -34,15 +36,15 @@ void CreateFontStyleImagesJob::process(Ctl &ctl) // create shapes and calc size (bounding boxes) std::vector name_shapes(m_input.styles.size()); std::vector scales(m_input.styles.size()); - images = std::vector(m_input.styles.size()); + images = std::vector(m_input.styles.size()); for (auto &item : m_input.styles) { size_t index = &item - &m_input.styles.front(); ExPolygons &shapes = name_shapes[index]; - shapes = Emboss::text2shapes(item.font, m_input.text.c_str(), item.prop); + shapes = text2shapes(item.font, m_input.text.c_str(), item.prop); // create image description - EmbossStyleManager::StyleImage &image = images[index]; + StyleManager::StyleImage &image = images[index]; BoundingBox &bounding_box = image.bounding_box; for (ExPolygon &shape : shapes) bounding_box.merge(BoundingBox(shape.contour.points)); @@ -56,7 +58,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl) const auto &cn = item.prop.collection_number; unsigned int font_index = (cn.has_value()) ? *cn : 0; double unit_per_em = item.font.font_file->infos[font_index].unit_per_em; - double scale = item.prop.size_in_mm / unit_per_em * Emboss::SHAPE_SCALE * ppm; + double scale = item.prop.size_in_mm / unit_per_em * SHAPE_SCALE * ppm; scales[index] = scale; //double scale = font_prop.size_in_mm * SCALING_FACTOR; @@ -77,14 +79,14 @@ void CreateFontStyleImagesJob::process(Ctl &ctl) // arrange bounding boxes int offset_y = 0; width = 0; - for (EmbossStyleManager::StyleImage &image : images) { + for (StyleManager::StyleImage &image : images) { image.offset.y() = offset_y; offset_y += image.tex_size.y+1; if (width < image.tex_size.x) width = image.tex_size.x; } height = offset_y; - for (EmbossStyleManager::StyleImage &image : images) { + for (StyleManager::StyleImage &image : images) { const Point &o = image.offset; const ImVec2 &s = image.tex_size; image.uv0 = ImVec2(o.x() / (double) width, @@ -97,7 +99,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl) pixels = std::vector(4*width * height, {255}); // upload sub textures - for (EmbossStyleManager::StyleImage &image : images) { + for (StyleManager::StyleImage &image : images) { sla::Resolution resolution(image.tex_size.x, image.tex_size.y); size_t index = &image - &images.front(); double pixel_dim = SCALING_FACTOR / scales[index]; @@ -146,7 +148,7 @@ void CreateFontStyleImagesJob::finalize(bool canceled, std::exception_ptr &) // set up texture id void *texture_id = (void *) (intptr_t) tex_id; - for (EmbossStyleManager::StyleImage &image : images) + for (StyleManager::StyleImage &image : images) image.texture_id = texture_id; // move to result diff --git a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp index 453f14586f..c220f2ee01 100644 --- a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp +++ b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp @@ -7,7 +7,7 @@ #include "slic3r/Utils/EmbossStyleManager.hpp" #include "Job.hpp" -namespace Slic3r::GUI { +namespace Slic3r::GUI::Emboss { /// /// Create texture with name of styles written by its style @@ -15,7 +15,7 @@ namespace Slic3r::GUI { /// class CreateFontStyleImagesJob : public Job { - EmbossStyleManager::StyleImagesData m_input; + StyleManager::StyleImagesData m_input; // Output data // texture size @@ -23,10 +23,10 @@ class CreateFontStyleImagesJob : public Job // texture data std::vector pixels; // descriptors of sub textures - std::vector images; + std::vector images; public: - CreateFontStyleImagesJob(EmbossStyleManager::StyleImagesData &&input); + CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input); void process(Ctl &ctl) override; void finalize(bool canceled, std::exception_ptr &) override; }; diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index 7112ceb474..f49e8499bd 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -19,7 +19,9 @@ #include "slic3r/Utils/UndoRedo.hpp" using namespace Slic3r; -using namespace GUI; +using namespace Slic3r::Emboss; +using namespace Slic3r::GUI; +using namespace Slic3r::GUI::Emboss; // private namespace namespace priv{ @@ -31,11 +33,12 @@ constexpr float safe_extension = 1.0f; /// /// /// -bool check(const EmbossDataBase &input, bool check_fontfile = true); -bool check(const EmbossDataCreateVolume &input, bool is_main_thread = false); -bool check(const EmbossDataCreateObject &input); -bool check(const EmbossDataUpdate &input, bool is_main_thread = false); -bool check(const UseSurfaceData &input, bool is_main_thread = false); +bool check(const DataBase &input, bool check_fontfile = true, bool use_surface = false); +bool check(const DataCreateVolume &input, bool is_main_thread = false); +bool check(const DataCreateObject &input); +bool check(const DataUpdate &input, bool is_main_thread = false, bool use_surface = false); +bool check(const CreateSurfaceVolumeData &input, bool is_main_thread = false); +bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread = false); // /// Try to create mesh from text @@ -46,14 +49,8 @@ bool check(const UseSurfaceData &input, bool is_main_thread = false); /// NOTE: Cache glyphs is changed /// To check if process was canceled /// Triangle mesh model -template -static TriangleMesh try_create_mesh(const EmbossDataBase &input, - Emboss::FontFileWithCache &font, - Fnc was_canceled); -template -static TriangleMesh create_mesh(EmbossDataBase &input, - Fnc was_canceled, - Job::Ctl &ctl); +template static TriangleMesh try_create_mesh(const DataBase &input, FontFileWithCache &font, Fnc was_canceled); +template static TriangleMesh create_mesh(DataBase &input, Fnc was_canceled, Job::Ctl &ctl); /// /// Create default mesh for embossed text @@ -66,7 +63,18 @@ static TriangleMesh create_default_mesh(); /// /// New mesh data /// Text configuration, ... -static void update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data); +static void update_volume(TriangleMesh &&mesh, const DataUpdate &data); + +/// +/// Add new volume to object +/// +/// triangles of new volume +/// Object where to add volume +/// Type of new volume +/// Transformation of volume inside of object +/// Text configuration and New VolumeName +static void create_volume(TriangleMesh &&mesh, const size_t object_idx, + const ModelVolumeType type, const Transform3d trmat, const DataBase &data); /// /// Select Volume from objects @@ -74,8 +82,7 @@ static void update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data); /// All objects in scene /// Identifier of volume in object /// Pointer to volume when exist otherwise nullptr -static ModelVolume *get_volume(ModelObjectPtrs &objects, - const ObjectID &volume_id); +static ModelVolume *get_volume(ModelObjectPtrs &objects, const ObjectID &volume_id); /// /// Create projection for cut surface from mesh @@ -84,10 +91,7 @@ static ModelVolume *get_volume(ModelObjectPtrs &objects, /// Convert shape to milimeters /// Bounding box 3d of model volume for projection ranges /// Orthogonal cut_projection -static Emboss::OrthoProject create_projection_for_cut( - Transform3d tr, - double shape_scale, - const std::pair &z_range); +static OrthoProject create_projection_for_cut(Transform3d tr, double shape_scale, const std::pair &z_range); /// /// Create tranformation for emboss Cutted surface @@ -97,41 +101,43 @@ static Emboss::OrthoProject create_projection_for_cut( /// Text voliume transformation inside object /// Cutted surface from model /// Projection -static Emboss::OrthoProject3d create_emboss_projection( - bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut); +static OrthoProject3d create_emboss_projection(bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut); + +/// +/// Cut surface into triangle mesh +/// +/// (can't be const - cache of font) +/// SurfaceVolume data +/// Check to interupt execution +/// Extruded object from cuted surace +static TriangleMesh cut_surface(/*const*/ DataBase &input1, const SurfaceVolumeData &input2, std::function was_canceled); static void create_message(const std::string &message); // only in finalize static bool process(std::exception_ptr &eptr); -class EmbossJobException : public std::runtime_error -{ public: EmbossJobException(const char* message):runtime_error(message){}}; +class JobException : public std::runtime_error { +public: JobException(const char* message):runtime_error(message){}}; }// namespace priv ///////////////// /// Create Volume -EmbossCreateVolumeJob::EmbossCreateVolumeJob(EmbossDataCreateVolume &&input) +CreateVolumeJob::CreateVolumeJob(DataCreateVolume &&input) : m_input(std::move(input)) { assert(priv::check(m_input, true)); } -void EmbossCreateVolumeJob::process(Ctl &ctl) { +void CreateVolumeJob::process(Ctl &ctl) { if (!priv::check(m_input)) throw std::runtime_error("Bad input data for EmbossCreateVolumeJob."); auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); }; m_result = priv::create_mesh(m_input, was_canceled, ctl); - if (was_canceled()) return; - - // Create new volume inside of object - const FontProp &font_prop = m_input.text_configuration.style.prop; - Transform3d surface_trmat = Emboss::create_transformation_onto_surface( - m_input.hit.position, m_input.hit.normal); - Emboss::apply_transformation(font_prop, surface_trmat); - m_transformation = m_input.hit_instance_tr.inverse() * - m_input.hit_object_tr * surface_trmat; + // center result + Vec3f c = m_result.bounding_box().center().cast(); + if (!c.isApprox(Vec3f::Zero())) m_result.translate(-c); } -void EmbossCreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) { +void CreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) { // doesn't care about exception when process was canceled by user if (canceled) { eptr = nullptr; @@ -141,74 +147,19 @@ void EmbossCreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) { if (m_result.its.empty()) return priv::create_message(_u8L("Can't create empty volume.")); - GUI_App &app = wxGetApp(); - Plater *plater = app.plater(); - ObjectList *obj_list = app.obj_list(); - GLCanvas3D *canvas = plater->canvas3D(); - ModelObjectPtrs &objects = plater->model().objects; - - // create volume in object - size_t object_idx = m_input.object_idx; - - // Parent object for text volume was propably removed. - // Assumption: User know what he does, so text volume is no more needed. - if (objects.size() <= object_idx) - return; - - plater->take_snapshot(_L("Add Emboss text Volume")); - - ModelObject *obj = objects[object_idx]; - ModelVolumeType type = m_input.volume_type; - - ModelVolume *volume = obj->add_volume(std::move(m_result), type); - - // set a default extruder value, since user can't add it manually - volume->config.set_key_value("extruder", new ConfigOptionInt(0)); - - // do not allow model reload from disk - volume->source.is_from_builtin_objects = true; - - volume->name = m_input.volume_name; - volume->text_configuration = std::move(m_input.text_configuration); - volume->set_transformation(m_transformation); - - // update volume name in object list - // updata selection after new volume added - // change name of volume in right panel - // 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( - m_input.object_idx, add_to_selection); - if (!sel.IsEmpty()) obj_list->select_item(sel.front()); - - // update printable state on canvas - if (type == ModelVolumeType::MODEL_PART) - canvas->update_instance_printable_state_for_object(object_idx); - - obj_list->selection_changed(); - - // Now is valid text volume selected open emboss gizmo - GLGizmosManager &manager = canvas->get_gizmos_manager(); - if (manager.get_current_type() != GLGizmosManager::Emboss) - manager.open_gizmo(GLGizmosManager::Emboss); - - // redraw scene - canvas->reload_scene(true); + priv::create_volume(std::move(m_result), m_input.object_idx, m_input.volume_type, m_input.trmat, m_input); } ///////////////// /// Create Object -EmbossCreateObjectJob::EmbossCreateObjectJob(EmbossDataCreateObject &&input) +CreateObjectJob::CreateObjectJob(DataCreateObject &&input) : m_input(std::move(input)) { assert(priv::check(m_input)); } -void EmbossCreateObjectJob::process(Ctl &ctl) +void CreateObjectJob::process(Ctl &ctl) { if (!priv::check(m_input)) throw std::runtime_error("Bad input data for EmbossCreateObjectJob."); @@ -240,7 +191,7 @@ void EmbossCreateObjectJob::process(Ctl &ctl) m_transformation = Transform3d(tt); } -void EmbossCreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr) +void CreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr) { // doesn't care about exception when process was canceled by user if (canceled) { @@ -279,13 +230,13 @@ void EmbossCreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr) ///////////////// /// Update Volume -EmbossUpdateJob::EmbossUpdateJob(EmbossDataUpdate&& input) +UpdateJob::UpdateJob(DataUpdate&& input) : m_input(std::move(input)) { assert(priv::check(m_input, true)); } -void EmbossUpdateJob::process(Ctl &ctl) +void UpdateJob::process(Ctl &ctl) { if (!priv::check(m_input)) throw std::runtime_error("Bad input data for EmbossUpdateJob."); @@ -297,16 +248,14 @@ void EmbossUpdateJob::process(Ctl &ctl) m_result = priv::try_create_mesh(m_input, m_input.font_file, was_canceled); if (was_canceled()) return; if (m_result.its.empty()) - throw priv::EmbossJobException( - _u8L("Created text volume is empty. Change text or " - "font.").c_str()); + throw priv::JobException(_u8L("Created text volume is empty. Change text or font.").c_str()); // center triangle mesh Vec3d shift = m_result.bounding_box().center(); m_result.translate(-shift.cast()); } -void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &eptr) +void UpdateJob::finalize(bool canceled, std::exception_ptr &eptr) { // doesn't care about exception when process was canceled by user if (canceled || m_input.cancel->load()) { @@ -317,19 +266,14 @@ void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &eptr) priv::update_volume(std::move(m_result), m_input); } -UseSurfaceData::ModelSources UseSurfaceData::create_sources( - const ModelVolume *text_volume) -{ - if (text_volume == nullptr) return {}; - if (!text_volume->text_configuration.has_value()) return {}; - const ModelVolumePtrs &volumes = text_volume->get_object()->volumes; - // no other volume in object - if (volumes.size() <= 1) return {}; +namespace Slic3r::GUI::Emboss { - ModelSources result; +SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional text_volume_id) +{ + SurfaceVolumeData::ModelSources result; result.reserve(volumes.size() - 1); for (const ModelVolume *v : volumes) { - if (v->id() == text_volume->id()) continue; + if (text_volume_id.has_value() && v->id().id == *text_volume_id) continue; // skip modifiers and negative volumes, ... if (!v->is_model_part()) continue; const TriangleMesh &tm = v->mesh(); @@ -340,15 +284,60 @@ UseSurfaceData::ModelSources UseSurfaceData::create_sources( return result; } +SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume *text_volume) +{ + if (text_volume == nullptr) return {}; + if (!text_volume->text_configuration.has_value()) return {}; + const ModelVolumePtrs &volumes = text_volume->get_object()->volumes; + // no other volume in object + if (volumes.size() <= 1) return {}; + return create_sources(volumes, text_volume->id().id); +} + +} // namespace Slic3r::GUI::Emboss + ///////////////// -/// Cut Surface -UseSurfaceJob::UseSurfaceJob(UseSurfaceData &&input) +/// Create Surface volume +CreateSurfaceVolumeJob::CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input) : m_input(std::move(input)) { assert(priv::check(m_input, true)); } -void UseSurfaceJob::process(Ctl &ctl) { +void CreateSurfaceVolumeJob::process(Ctl &ctl) { + if (!priv::check(m_input)) + throw std::runtime_error("Bad input data for CreateSurfaceVolumeJob."); + // check cancelation of process + auto was_canceled = [&ctl]() -> bool { return ctl.was_canceled(); }; + m_result = priv::cut_surface(m_input, m_input, was_canceled); +} + +void CreateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) { + // doesn't care about exception when process was canceled by user + if (canceled) return; + if (priv::process(eptr)) return; + + // TODO: Find better way to Not center volume data when add !!! + TriangleMesh mesh = m_result; // Part1: copy + + priv::create_volume(std::move(m_result), m_input.object_idx, + m_input.volume_type, m_input.text_tr, m_input); + + // Part2: update volume data + //auto vol = wxGetApp().plater()->model().objects[m_input.object_idx]->volumes.back(); + //UpdateJob::update_volume(vol, std::move(mesh), m_input.text_configuration, m_input.volume_name); +} + +///////////////// +/// Cut Surface +UpdateSurfaceVolumeJob::UpdateSurfaceVolumeJob(UpdateSurfaceVolumeData &&input) + : m_input(std::move(input)) +{ + assert(priv::check(m_input, true)); +} + +void UpdateSurfaceVolumeJob::process(Ctl &ctl) +{ if (!priv::check(m_input)) throw std::runtime_error("Bad input data for UseSurfaceJob."); @@ -357,107 +346,25 @@ void UseSurfaceJob::process(Ctl &ctl) { if (cancel->load()) return true; return ctl.was_canceled(); }; - - const TextConfiguration &tc = m_input.text_configuration; - const char *text = tc.text.c_str(); - const FontProp &fp = tc.style.prop; - ExPolygons shapes = Emboss::text2shapes(m_input.font_file, text, fp, was_canceled); - if (shapes.empty() || shapes.front().contour.empty()) - throw priv::EmbossJobException( - _u8L("Font doesn't have any shape for given text.").c_str()); - - if (was_canceled()) return; - - // Define alignment of text - left, right, center, top bottom, .... - BoundingBox bb = get_extents(shapes); - Point projection_center = bb.center(); - for (ExPolygon &shape : shapes) shape.translate(-projection_center); - bb.translate(-projection_center); - - const Emboss::FontFile &ff = *m_input.font_file.font_file; - double shape_scale = Emboss::get_shape_scale(fp, ff); - - size_t biggest_count = 0; - const UseSurfaceData::ModelSource *biggest = nullptr; - std::vector s_to_itss(m_input.sources.size(), std::numeric_limits::max()); - std::vector itss; - itss.reserve(m_input.sources.size()); - for (const UseSurfaceData::ModelSource &s : m_input.sources) { - Transform3d mesh_tr_inv = s.tr.inverse(); - Transform3d cut_projection_tr = mesh_tr_inv * m_input.text_tr; - std::pair z_range{0., 1.}; - Emboss::OrthoProject cut_projection = - priv::create_projection_for_cut(cut_projection_tr, shape_scale, z_range); - // copy only part of source model - indexed_triangle_set its = its_cut_AoI(s.mesh->its, bb, cut_projection); - if (its.indices.empty()) continue; - if (biggest_count < its.vertices.size()) { - biggest_count = its.vertices.size(); - biggest = &s; - } - s_to_itss[&s - &m_input.sources.front()] = itss.size(); - itss.emplace_back(std::move(its)); - } - if (itss.empty()) - throw priv::EmbossJobException(_u8L("There is no volume in projection direction.").c_str()); - - Transform3d tr_inv = biggest->tr.inverse(); - size_t itss_index = s_to_itss[biggest - &m_input.sources.front()]; - BoundingBoxf3 mesh_bb = bounding_box(itss[itss_index]); - for (const UseSurfaceData::ModelSource &s : m_input.sources) { - if (&s == biggest) continue; - size_t itss_index = s_to_itss[&s - &m_input.sources.front()]; - if (itss_index == std::numeric_limits::max()) continue; - Transform3d tr = s.tr * tr_inv; - indexed_triangle_set &its = itss[itss_index]; - its_transform(its, tr); - BoundingBoxf3 bb = bounding_box(its); - mesh_bb.merge(bb); - } - - Transform3d mesh_tr_inv = tr_inv; - Transform3d cut_projection_tr = mesh_tr_inv * m_input.text_tr; - Transform3d emboss_tr = cut_projection_tr.inverse(); - BoundingBoxf3 mesh_bb_tr = mesh_bb.transformed(emboss_tr); - std::pair z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()}; - Emboss::OrthoProject cut_projection = priv::create_projection_for_cut( - cut_projection_tr, shape_scale, z_range); - float projection_ratio = (-z_range.first + priv::safe_extension) / - (z_range.second - z_range.first + 2 * priv::safe_extension); - - // Use CGAL to cut surface from triangle mesh - SurfaceCut cut = cut_surface(shapes, itss, cut_projection, projection_ratio); - if (cut.empty()) - throw priv::EmbossJobException( - _u8L("There is no valid surface for text projection.").c_str()); - if (was_canceled()) return; - - // !! Projection needs to transform cut - Emboss::OrthoProject3d projection = priv::create_emboss_projection( - m_input.is_outside, fp.emboss, emboss_tr, cut); - - indexed_triangle_set new_its = cut2model(cut, projection); - assert(!new_its.empty()); - - if (was_canceled()) return; - //its_write_obj(new_its, "C:/data/temp/projected.obj"); // only debug - m_result = TriangleMesh(std::move(new_its)); + m_result = priv::cut_surface(m_input, m_input, was_canceled); } -void UseSurfaceJob::finalize(bool canceled, std::exception_ptr &eptr) +void UpdateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) { // doesn't care about exception when process was canceled by user - if (canceled || m_input.cancel->load()) { + if (m_input.cancel->load()) { eptr = nullptr; return; } + if (canceled) return; if (priv::process(eptr)) return; priv::update_volume(std::move(m_result), m_input); } //////////////////////////// /// private namespace implementation -bool priv::check(const EmbossDataBase &input, bool check_fontfile){ +bool priv::check(const DataBase &input, bool check_fontfile, bool use_surface) +{ bool res = true; if (check_fontfile) { assert(input.font_file.has_value()); @@ -469,26 +376,24 @@ bool priv::check(const EmbossDataBase &input, bool check_fontfile){ res &= !input.text_configuration.text.empty(); assert(!input.volume_name.empty()); res &= !input.volume_name.empty(); + assert(input.text_configuration.style.prop.use_surface == use_surface); + res &= input.text_configuration.style.prop.use_surface == use_surface; return res; } -bool priv::check(const EmbossDataCreateVolume &input, bool is_main_thread) { +bool priv::check(const DataCreateVolume &input, bool is_main_thread) { bool check_fontfile = false; - bool res = check((EmbossDataBase) input, check_fontfile); + bool res = check((DataBase) input, check_fontfile); assert(input.volume_type != ModelVolumeType::INVALID); res &= input.volume_type != ModelVolumeType::INVALID; assert(input.object_idx >= 0); res &= input.object_idx >= 0; if (is_main_thread) - assert((size_t)input.object_idx < wxGetApp().model().objects.size()); - assert(input.screen_coor.x() >= 0.); - res &= input.screen_coor.x() >= 0.; - assert(input.screen_coor.y() >= 0.); - res &= input.screen_coor.y() >= 0.; + assert((size_t)input.object_idx < wxGetApp().model().objects.size()); return res; } -bool priv::check(const EmbossDataCreateObject &input) { +bool priv::check(const DataCreateObject &input) { bool check_fontfile = false; - bool res = check((EmbossDataBase) input, check_fontfile); + bool res = check((DataBase) input, check_fontfile); assert(input.screen_coor.x() >= 0.); res &= input.screen_coor.x() >= 0.; assert(input.screen_coor.y() >= 0.); @@ -497,8 +402,9 @@ bool priv::check(const EmbossDataCreateObject &input) { res &= input.bed_shape.size() >= 3; return res; } -bool priv::check(const EmbossDataUpdate &input, bool is_main_thread){ - bool res = check((EmbossDataBase) input); +bool priv::check(const DataUpdate &input, bool is_main_thread, bool use_surface){ + bool check_fontfile = true; + bool res = check((DataBase) input, check_fontfile, use_surface); assert(input.volume_id.id >= 0); res &= input.volume_id.id >= 0; if (is_main_thread) @@ -509,15 +415,24 @@ bool priv::check(const EmbossDataUpdate &input, bool is_main_thread){ assert(!input.cancel->load()); return res; } -bool priv::check(const UseSurfaceData &input, bool is_main_thread){ - bool res = check((EmbossDataUpdate) input, is_main_thread); +bool priv::check(const CreateSurfaceVolumeData &input, bool is_main_thread) +{ + bool use_surface = true; + bool res = check((DataBase)input, is_main_thread, use_surface); + assert(!input.sources.empty()); + res &= !input.sources.empty(); + return res; +} +bool priv::check(const UpdateSurfaceVolumeData &input, bool is_main_thread){ + bool use_surface = true; + bool res = check((DataUpdate)input, is_main_thread, use_surface); assert(!input.sources.empty()); res &= !input.sources.empty(); return res; } template -TriangleMesh priv::try_create_mesh(const EmbossDataBase &input, Emboss::FontFileWithCache &font, Fnc was_canceled) +TriangleMesh priv::try_create_mesh(const DataBase &input, FontFileWithCache &font, Fnc was_canceled) { const TextConfiguration &tc = input.text_configuration; const char *text = tc.text.c_str(); @@ -526,7 +441,7 @@ TriangleMesh priv::try_create_mesh(const EmbossDataBase &input, Emboss::FontFile assert(font.has_value()); if (!font.has_value()) return {}; - ExPolygons shapes = Emboss::text2shapes(font, text, prop, was_canceled); + ExPolygons shapes = text2shapes(font, text, prop, was_canceled); if (shapes.empty()) return {}; if (was_canceled()) return {}; @@ -536,14 +451,14 @@ TriangleMesh priv::try_create_mesh(const EmbossDataBase &input, Emboss::FontFile int unit_per_em = font.font_file->infos[font_index].unit_per_em; float scale = prop.size_in_mm / unit_per_em; float depth = prop.emboss / scale; - auto projectZ = std::make_unique(depth); - Emboss::ProjectScale project(std::move(projectZ), scale); + auto projectZ = std::make_unique(depth); + ProjectScale project(std::move(projectZ), scale); if (was_canceled()) return {}; - return TriangleMesh(Emboss::polygons2model(shapes, project)); + return TriangleMesh(polygons2model(shapes, project)); } template -TriangleMesh priv::create_mesh(EmbossDataBase &input, Fnc was_canceled, Job::Ctl& ctl) +TriangleMesh priv::create_mesh(DataBase &input, Fnc was_canceled, Job::Ctl& ctl) { // It is neccessary to create some shape // Emboss text window is opened by creation new emboss text object @@ -579,10 +494,10 @@ TriangleMesh priv::create_default_mesh() return triangle_mesh; } -void EmbossUpdateJob::update_volume(ModelVolume *volume, - TriangleMesh &&mesh, - const TextConfiguration &text_configuration, - const std::string &volume_name) +void UpdateJob::update_volume(ModelVolume *volume, + TriangleMesh &&mesh, + const TextConfiguration &text_configuration, + const std::string &volume_name) { // check inputs bool is_valid_input = @@ -626,7 +541,7 @@ void EmbossUpdateJob::update_volume(ModelVolume *volume, canvas->reload_scene(refresh_immediately); } -void priv::update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data) +void priv::update_volume(TriangleMesh &&mesh, const DataUpdate &data) { // for sure that some object will be created if (mesh.its.empty()) @@ -652,7 +567,72 @@ void priv::update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data) if (tc.has_value() && tc->fix_3mf_tr.has_value()) volume->set_transformation(volume->get_matrix() * tc->fix_3mf_tr->inverse()); - EmbossUpdateJob::update_volume(volume, std::move(mesh), data.text_configuration, data.volume_name); + UpdateJob::update_volume(volume, std::move(mesh), data.text_configuration, data.volume_name); +} + +void priv::create_volume( + TriangleMesh &&mesh, const size_t object_idx, + const ModelVolumeType type, const Transform3d trmat, const DataBase &data) +{ + GUI_App &app = wxGetApp(); + Plater *plater = app.plater(); + ObjectList *obj_list = app.obj_list(); + GLCanvas3D *canvas = plater->canvas3D(); + ModelObjectPtrs &objects = plater->model().objects; + + // Parent object for text volume was propably removed. + // Assumption: User know what he does, so text volume is no more needed. + if (objects.size() <= object_idx) + return priv::create_message(_u8L("Bad object index to create volume.")); + + if (mesh.its.empty()) + return priv::create_message(_u8L("Can't create empty volume.")); + + plater->take_snapshot(_L("Add Emboss text Volume")); + + ModelObject *obj = objects[object_idx]; + + // NOTE: be carefull add volume also center mesh !!! + // So first add simple shape(convex hull is also calculated) + ModelVolume *volume = obj->add_volume(make_cube(1., 1., 1.), type); + + // TODO: Refactor to create better way to not set cube at begining + // Revert mesh centering by set mesh after add cube + volume->set_mesh(std::move(mesh)); + volume->calculate_convex_hull(); + + + // set a default extruder value, since user can't add it manually + volume->config.set_key_value("extruder", new ConfigOptionInt(0)); + + // do not allow model reload from disk + volume->source.is_from_builtin_objects = true; + + volume->name = data.volume_name; // copy + volume->text_configuration = data.text_configuration; // copy + volume->set_transformation(trmat); + + // update volume name in object list + // updata selection after new volume added + // change name of volume in right panel + // 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(object_idx, add_to_selection); + if (!sel.IsEmpty()) obj_list->select_item(sel.front()); + + // update printable state on canvas + if (type == ModelVolumeType::MODEL_PART) canvas->update_instance_printable_state_for_object(object_idx); + + obj_list->selection_changed(); + + // Now is valid text volume selected open emboss gizmo + GLGizmosManager &manager = canvas->get_gizmos_manager(); + if (manager.get_current_type() != GLGizmosManager::Emboss) + manager.open_gizmo(GLGizmosManager::Emboss); + + // redraw scene + canvas->reload_scene(true); } ModelVolume *priv::get_volume(ModelObjectPtrs &objects, @@ -664,7 +644,7 @@ ModelVolume *priv::get_volume(ModelObjectPtrs &objects, return nullptr; }; -Emboss::OrthoProject priv::create_projection_for_cut( +OrthoProject priv::create_projection_for_cut( Transform3d tr, double shape_scale, const std::pair &z_range) @@ -686,10 +666,10 @@ Emboss::OrthoProject priv::create_projection_for_cut( // Projection is in direction from far plane tr.translate(Vec3d(0., 0., min_z)); tr.scale(shape_scale); - return Emboss::OrthoProject(tr, project_direction); + return OrthoProject(tr, project_direction); } -Emboss::OrthoProject3d priv::create_emboss_projection( +OrthoProject3d priv::create_emboss_projection( bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut) { // Offset of clossed side to model @@ -699,14 +679,98 @@ Emboss::OrthoProject3d priv::create_emboss_projection( back_move = -((is_outside) ? surface_offset : emboss); its_transform(cut, tr.pretranslate(Vec3d(0., 0., front_move))); Vec3d from_front_to_back(0., 0., back_move - front_move); - return Emboss::OrthoProject3d(from_front_to_back); + return OrthoProject3d(from_front_to_back); +} + +// input can't be const - cache of font +TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2, std::function was_canceled) +{ + const TextConfiguration &tc = input1.text_configuration; + const char *text = tc.text.c_str(); + const FontProp &fp = tc.style.prop; + + ExPolygons shapes = text2shapes(input1.font_file, text, fp, was_canceled); + if (shapes.empty() || shapes.front().contour.empty()) + throw JobException(_u8L("Font doesn't have any shape for given text.").c_str()); + + if (was_canceled()) return {}; + + // Define alignment of text - left, right, center, top bottom, .... + BoundingBox bb = get_extents(shapes); + Point projection_center = bb.center(); + for (ExPolygon &shape : shapes) shape.translate(-projection_center); + bb.translate(-projection_center); + + const FontFile &ff = *input1.font_file.font_file; + double shape_scale = get_shape_scale(fp, ff); + + const SurfaceVolumeData::ModelSources &sources = input2.sources; + const SurfaceVolumeData::ModelSource *biggest = nullptr; + + size_t biggest_count = 0; + // convert index from (s)ources to (i)ndexed (t)riangle (s)ets + std::vector s_to_itss(sources.size(), std::numeric_limits::max()); + std::vector itss; + itss.reserve(sources.size()); + for (const SurfaceVolumeData::ModelSource &s : sources) { + Transform3d mesh_tr_inv = s.tr.inverse(); + Transform3d cut_projection_tr = mesh_tr_inv * input2.text_tr; + std::pair z_range{0., 1.}; + OrthoProject cut_projection = create_projection_for_cut(cut_projection_tr, shape_scale, z_range); + // copy only part of source model + indexed_triangle_set its = its_cut_AoI(s.mesh->its, bb, cut_projection); + if (its.indices.empty()) continue; + if (biggest_count < its.vertices.size()) { + biggest_count = its.vertices.size(); + biggest = &s; + } + s_to_itss[&s - &sources.front()] = itss.size(); + itss.emplace_back(std::move(its)); + } + if (itss.empty()) throw JobException(_u8L("There is no volume in projection direction.").c_str()); + + Transform3d tr_inv = biggest->tr.inverse(); + size_t itss_index = s_to_itss[biggest - &sources.front()]; + BoundingBoxf3 mesh_bb = bounding_box(itss[itss_index]); + for (const SurfaceVolumeData::ModelSource &s : sources) { + if (&s == biggest) continue; + size_t itss_index = s_to_itss[&s - &sources.front()]; + if (itss_index == std::numeric_limits::max()) continue; + Transform3d tr = s.tr * tr_inv; + indexed_triangle_set &its = itss[itss_index]; + its_transform(its, tr); + BoundingBoxf3 bb = bounding_box(its); + mesh_bb.merge(bb); + } + + // tr_inv = transformation of mesh inverted + Transform3d cut_projection_tr = tr_inv * input2.text_tr; + Transform3d emboss_tr = cut_projection_tr.inverse(); + BoundingBoxf3 mesh_bb_tr = mesh_bb.transformed(emboss_tr); + std::pair z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()}; + OrthoProject cut_projection = create_projection_for_cut(cut_projection_tr, shape_scale, z_range); + float projection_ratio = (-z_range.first + safe_extension) / (z_range.second - z_range.first + 2 * safe_extension); + + // Use CGAL to cut surface from triangle mesh + SurfaceCut cut = cut_surface(shapes, itss, cut_projection, projection_ratio); + if (cut.empty()) throw JobException(_u8L("There is no valid surface for text projection.").c_str()); + if (was_canceled()) return {}; + + // !! Projection needs to transform cut + OrthoProject3d projection = create_emboss_projection(input2.is_outside, fp.emboss, emboss_tr, cut); + + indexed_triangle_set new_its = cut2model(cut, projection); + assert(!new_its.empty()); + + if (was_canceled()) return {}; + return TriangleMesh(std::move(new_its)); } bool priv::process(std::exception_ptr &eptr) { if (!eptr) return false; try { std::rethrow_exception(eptr); - } catch (priv::EmbossJobException &e) { + } catch (priv::JobException &e) { create_message(e.what()); eptr = nullptr; } diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index bd347812fa..cf64044bf0 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -15,15 +15,15 @@ class ModelVolume; class TriangleMesh; } -namespace Slic3r::GUI { +namespace Slic3r::GUI::Emboss { /// /// Base data holder for embossing /// -struct EmbossDataBase +struct DataBase { // Keep pointer on Data of font (glyph shapes) - Emboss::FontFileWithCache font_file; + Slic3r::Emboss::FontFileWithCache font_file; // font item is not used for create object TextConfiguration text_configuration; // new volume name created from text @@ -35,24 +35,16 @@ struct EmbossDataBase /// Volume is created on the surface of existing volume in object. /// NOTE: EmbossDataBase::font_file doesn't have to be valid !!! /// -struct EmbossDataCreateVolume : public EmbossDataBase +struct DataCreateVolume : public DataBase { // define embossed volume type ModelVolumeType volume_type; - // define position on screen where to create object - Vec2d screen_coor; - // parent ModelObject index where to create volume int object_idx; - // projection property - Camera camera; - - // used to find point on surface where to create new object - RaycastManager::SurfacePoint hit; - Transform3d hit_object_tr; - Transform3d hit_instance_tr; + // new created volume transformation + Transform3d trmat; }; /// @@ -60,14 +52,13 @@ struct EmbossDataCreateVolume : public EmbossDataBase /// Should not be stopped /// NOTE: EmbossDataBase::font_file doesn't have to be valid !!! /// -class EmbossCreateVolumeJob : public Job +class CreateVolumeJob : public Job { - EmbossDataCreateVolume m_input; - TriangleMesh m_result; - Transform3d m_transformation; + DataCreateVolume m_input; + TriangleMesh m_result; public: - EmbossCreateVolumeJob(EmbossDataCreateVolume&& input); + CreateVolumeJob(DataCreateVolume&& input); void process(Ctl &ctl) override; void finalize(bool canceled, std::exception_ptr &eptr) override; }; @@ -77,7 +68,7 @@ public: /// Object is placed on bed under screen coor /// OR to center of scene when it is out of bed shape /// -struct EmbossDataCreateObject : public EmbossDataBase +struct DataCreateObject : public DataBase { // define position on screen where to create object Vec2d screen_coor; @@ -93,13 +84,13 @@ struct EmbossDataCreateObject : public EmbossDataBase /// Create new TextObject on the platter /// Should not be stopped /// -class EmbossCreateObjectJob : public Job +class CreateObjectJob : public Job { - EmbossDataCreateObject m_input; - TriangleMesh m_result; - Transform3d m_transformation; + DataCreateObject m_input; + TriangleMesh m_result; + Transform3d m_transformation; public: - EmbossCreateObjectJob(EmbossDataCreateObject&& input); + CreateObjectJob(DataCreateObject&& input); void process(Ctl &ctl) override; void finalize(bool canceled, std::exception_ptr &eptr) override; }; @@ -107,7 +98,7 @@ public: /// /// Hold neccessary data to update embossed text object in job /// -struct EmbossDataUpdate : public EmbossDataBase +struct DataUpdate : public DataBase { // unique identifier of volume to change ObjectID volume_id; @@ -121,14 +112,14 @@ struct EmbossDataUpdate : 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 +class UpdateJob : public Job { - EmbossDataUpdate m_input; - TriangleMesh m_result; + DataUpdate m_input; + TriangleMesh m_result; public: // move params to private variable - EmbossUpdateJob(EmbossDataUpdate &&input); + UpdateJob(DataUpdate &&input); /// /// Create new embossed volume by m_input data and store to m_result @@ -158,10 +149,7 @@ public: const std::string &volume_name); }; -/// -/// Hold neccessary data to update embossed text object in job -/// -struct UseSurfaceData : public EmbossDataUpdate +struct SurfaceVolumeData { // Transformation of text volume inside of object Transform3d text_tr; @@ -180,26 +168,66 @@ struct UseSurfaceData : public EmbossDataUpdate }; using ModelSources = std::vector; ModelSources sources; - - /// - /// Copied triangles from object to be able create mesh for cut surface from - /// - /// Define text in object - /// Source data for cut surface from - static ModelSources create_sources(const ModelVolume *text_volume); }; +/// +/// Hold neccessary data to create(cut) volume from surface object in job +/// +struct CreateSurfaceVolumeData : public DataBase, public SurfaceVolumeData{ + // define embossed volume type + ModelVolumeType volume_type; + + // parent ModelObject index where to create volume + int object_idx; +}; + +/// +/// Cut surface from object and create cutted volume +/// Should not be stopped +/// +class CreateSurfaceVolumeJob : public Job +{ + CreateSurfaceVolumeData m_input; + TriangleMesh m_result; + +public: + CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input); + void process(Ctl &ctl) override; + void finalize(bool canceled, std::exception_ptr &eptr) override; +}; + +/// +/// Hold neccessary data to update embossed text object in job +/// +struct UpdateSurfaceVolumeData : public DataUpdate, public SurfaceVolumeData{}; + +/// +/// Copied triangles from object to be able create mesh for cut surface from +/// +/// Source object volumes for cut surface from +/// Source volume id +/// Source data for cut surface from +SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional text_volume_id = {}); + +/// +/// Copied triangles from object to be able create mesh for cut surface from +/// +/// Define text in object +/// Source data for cut surface from +SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume *text_volume); + + /// /// Update text volume to use surface from object /// -class UseSurfaceJob : public Job +class UpdateSurfaceVolumeJob : public Job { - UseSurfaceData m_input; + UpdateSurfaceVolumeData m_input; TriangleMesh m_result; public: // move params to private variable - UseSurfaceJob(UseSurfaceData &&input); + UpdateSurfaceVolumeJob(UpdateSurfaceVolumeData &&input); void process(Ctl &ctl) override; void finalize(bool canceled, std::exception_ptr &eptr) override; }; diff --git a/src/slic3r/Utils/EmbossStyleManager.cpp b/src/slic3r/Utils/EmbossStyleManager.cpp index 3fff460361..060d936d85 100644 --- a/src/slic3r/Utils/EmbossStyleManager.cpp +++ b/src/slic3r/Utils/EmbossStyleManager.cpp @@ -12,9 +12,10 @@ #include "slic3r/Utils/EmbossStylesSerializable.hpp" using namespace Slic3r; -using namespace Slic3r::GUI; +using namespace Slic3r::Emboss; +using namespace Slic3r::GUI::Emboss; -EmbossStyleManager::EmbossStyleManager(const ImWchar *language_glyph_range) +StyleManager::StyleManager(const ImWchar *language_glyph_range) : m_imgui_init_glyph_range(language_glyph_range) , m_exist_style_images(false) , m_temp_style_images(nullptr) @@ -22,12 +23,12 @@ EmbossStyleManager::EmbossStyleManager(const ImWchar *language_glyph_range) , m_last_style_index(std::numeric_limits::max()) {} -EmbossStyleManager::~EmbossStyleManager() { +StyleManager::~StyleManager() { clear_imgui_font(); free_style_images(); } -void EmbossStyleManager::init(AppConfig *app_config, const EmbossStyles &default_styles) +void StyleManager::init(AppConfig *app_config, const EmbossStyles &default_styles) { m_app_config = app_config; EmbossStyles styles = (app_config != nullptr) ? @@ -67,7 +68,7 @@ void EmbossStyleManager::init(AppConfig *app_config, const EmbossStyles &default } } -bool EmbossStyleManager::store_styles_to_app_config(bool use_modification, +bool StyleManager::store_styles_to_app_config(bool use_modification, bool store_activ_index) { assert(m_app_config != nullptr); @@ -102,7 +103,7 @@ bool EmbossStyleManager::store_styles_to_app_config(bool use_modification, return true; } -void EmbossStyleManager::add_style(const std::string &name) { +void StyleManager::add_style(const std::string &name) { EmbossStyle& style = m_style_cache.style; style.name = name; make_unique_name(style.name); @@ -112,7 +113,7 @@ void EmbossStyleManager::add_style(const std::string &name) { m_style_items.push_back({style}); } -void EmbossStyleManager::swap(size_t i1, size_t i2) { +void StyleManager::swap(size_t i1, size_t i2) { if (i1 >= m_style_items.size() || i2 >= m_style_items.size()) return; std::swap(m_style_items[i1], m_style_items[i2]); @@ -125,7 +126,7 @@ void EmbossStyleManager::swap(size_t i1, size_t i2) { } } -void EmbossStyleManager::discard_style_changes() { +void StyleManager::discard_style_changes() { if (exist_stored_style()) { if (load_style(m_style_cache.style_index)) return; // correct reload style @@ -138,7 +139,7 @@ void EmbossStyleManager::discard_style_changes() { load_first_valid_font(); } -void EmbossStyleManager::erase(size_t index) { +void StyleManager::erase(size_t index) { if (index >= m_style_items.size()) return; // fix selected index @@ -151,7 +152,7 @@ void EmbossStyleManager::erase(size_t index) { m_style_items.erase(m_style_items.begin() + index); } -void EmbossStyleManager::rename(const std::string& name) { +void StyleManager::rename(const std::string& name) { m_style_cache.style.name = name; m_style_cache.truncated_name.clear(); if (exist_stored_style()) { @@ -161,7 +162,7 @@ void EmbossStyleManager::rename(const std::string& name) { } } -bool EmbossStyleManager::load_style(size_t style_index) +bool StyleManager::load_style(size_t style_index) { if (style_index >= m_style_items.size()) return false; if (!load_style(m_style_items[style_index].style)) return false; @@ -171,14 +172,14 @@ bool EmbossStyleManager::load_style(size_t style_index) return true; } -bool EmbossStyleManager::load_style(const EmbossStyle &style) { +bool StyleManager::load_style(const EmbossStyle &style) { if (style.type == EmbossStyle::Type::file_path) { - std::unique_ptr font_ptr = - Emboss::create_font_file(style.path.c_str()); + std::unique_ptr font_ptr = + create_font_file(style.path.c_str()); if (font_ptr == nullptr) return false; m_style_cache.wx_font = {}; m_style_cache.font_file = - Emboss::FontFileWithCache(std::move(font_ptr)); + FontFileWithCache(std::move(font_ptr)); m_style_cache.style = style; // copy m_style_cache.style_index = std::numeric_limits::max(); m_style_cache.stored_wx_font = {}; @@ -190,7 +191,7 @@ bool EmbossStyleManager::load_style(const EmbossStyle &style) { return load_style(style, *wx_font_opt); } -bool EmbossStyleManager::load_style(const EmbossStyle &style, const wxFont &font) +bool StyleManager::load_style(const EmbossStyle &style, const wxFont &font) { if (!set_wx_font(font)) return false; m_style_cache.style = style; // copy @@ -200,9 +201,9 @@ bool EmbossStyleManager::load_style(const EmbossStyle &style, const wxFont &font return true; } -bool EmbossStyleManager::is_activ_font() { return m_style_cache.font_file.has_value(); } +bool StyleManager::is_activ_font() { return m_style_cache.font_file.has_value(); } -bool EmbossStyleManager::load_first_valid_font() { +bool StyleManager::load_first_valid_font() { while (!m_style_items.empty()) { if (load_style(0)) return true; // can't load so erase it from list @@ -211,22 +212,22 @@ bool EmbossStyleManager::load_first_valid_font() { return false; } -const EmbossStyle* EmbossStyleManager::get_stored_style() const +const EmbossStyle* StyleManager::get_stored_style() const { if (m_style_cache.style_index >= m_style_items.size()) return nullptr; return &m_style_items[m_style_cache.style_index].style; } -void EmbossStyleManager::clear_glyphs_cache() +void StyleManager::clear_glyphs_cache() { - Emboss::FontFileWithCache &ff = m_style_cache.font_file; + FontFileWithCache &ff = m_style_cache.font_file; if (!ff.has_value()) return; - ff.cache = std::make_shared(); + ff.cache = std::make_shared(); } -void EmbossStyleManager::clear_imgui_font() { m_style_cache.atlas.Clear(); } +void StyleManager::clear_imgui_font() { m_style_cache.atlas.Clear(); } -ImFont *EmbossStyleManager::get_imgui_font() +ImFont *StyleManager::get_imgui_font() { if (!is_activ_font()) return nullptr; @@ -242,16 +243,16 @@ ImFont *EmbossStyleManager::get_imgui_font() return font; } -const std::vector &EmbossStyleManager::get_styles() const{ return m_style_items; } +const std::vector &StyleManager::get_styles() const{ return m_style_items; } -ImFont* EmbossStyleManager::extend_imgui_font_range(size_t index, const std::string& text) +ImFont* StyleManager::extend_imgui_font_range(size_t index, const std::string& text) { // TODO: start using merge mode // ImFontConfig::MergeMode = true; return create_imgui_font(text); } -void EmbossStyleManager::make_unique_name(std::string &name) +void StyleManager::make_unique_name(std::string &name) { auto is_unique = [&](const std::string &name) -> bool { for (const Item &it : m_style_items) @@ -283,7 +284,7 @@ void EmbossStyleManager::make_unique_name(std::string &name) name = new_name; } -void EmbossStyleManager::init_trunc_names(float max_width) { +void StyleManager::init_trunc_names(float max_width) { for (auto &s : m_style_items) if (s.truncated_name.empty()) { std::string name = s.style.name; @@ -298,7 +299,7 @@ void EmbossStyleManager::init_trunc_names(float max_width) { #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" -void EmbossStyleManager::init_style_images(const Vec2i &max_size, +void StyleManager::init_style_images(const Vec2i &max_size, const std::string &text) { // check already initialized @@ -311,7 +312,7 @@ void EmbossStyleManager::init_style_images(const Vec2i &max_size, assert(m_temp_style_images->images.size() == m_temp_style_images->styles.size()); // copy images into styles - for (EmbossStyleManager::StyleImage &image : m_temp_style_images->images){ + for (StyleManager::StyleImage &image : m_temp_style_images->images){ size_t index = &image - &m_temp_style_images->images.front(); StyleImagesData::Item &style = m_temp_style_images->styles[index]; @@ -340,11 +341,11 @@ void EmbossStyleManager::init_style_images(const Vec2i &max_size, const EmbossStyle &style = item.style; std::optional wx_font_opt = WxFontUtils::load_wxFont(style.path); if (!wx_font_opt.has_value()) continue; - std::unique_ptr font_file = + std::unique_ptr font_file = WxFontUtils::create_font_file(*wx_font_opt); if (font_file == nullptr) continue; styles.push_back({ - Emboss::FontFileWithCache(std::move(font_file)), + FontFileWithCache(std::move(font_file)), style.name, style.prop }); @@ -354,7 +355,7 @@ void EmbossStyleManager::init_style_images(const Vec2i &max_size, queue_job(worker, std::make_unique(std::move(data))); } -void EmbossStyleManager::free_style_images() { +void StyleManager::free_style_images() { if (!m_exist_style_images) return; GLuint tex_id = 0; for (Item &it : m_style_items) { @@ -367,10 +368,10 @@ void EmbossStyleManager::free_style_images() { m_exist_style_images = false; } -float EmbossStyleManager::min_imgui_font_size = 18.f; -float EmbossStyleManager::max_imgui_font_size = 60.f; -float EmbossStyleManager::get_imgui_font_size(const FontProp &prop, - const Emboss::FontFile &file) +float StyleManager::min_imgui_font_size = 18.f; +float StyleManager::max_imgui_font_size = 60.f; +float StyleManager::get_imgui_font_size(const FontProp &prop, + const FontFile &file) { const auto &cn = prop.collection_number; unsigned int font_index = (cn.has_value()) ? *cn : 0; @@ -384,12 +385,12 @@ float EmbossStyleManager::get_imgui_font_size(const FontProp &prop, return c1 * std::abs(prop.size_in_mm) / 0.3528f; } -ImFont *EmbossStyleManager::create_imgui_font(const std::string &text) +ImFont *StyleManager::create_imgui_font(const std::string &text) { // inspiration inside of ImGuiWrapper::init_font auto& ff = m_style_cache.font_file; if (!ff.has_value()) return nullptr; - const Emboss::FontFile &font_file = *ff.font_file; + const FontFile &font_file = *ff.font_file; ImFontGlyphRangesBuilder builder; builder.AddRanges(m_imgui_init_glyph_range); @@ -464,18 +465,18 @@ ImFont *EmbossStyleManager::create_imgui_font(const std::string &text) return font; } -bool EmbossStyleManager::set_wx_font(const wxFont &wx_font) { - std::unique_ptr font_file = +bool StyleManager::set_wx_font(const wxFont &wx_font) { + std::unique_ptr font_file = WxFontUtils::create_font_file(wx_font); return set_wx_font(wx_font, std::move(font_file)); } -bool EmbossStyleManager::set_wx_font(const wxFont &wx_font, std::unique_ptr font_file) +bool StyleManager::set_wx_font(const wxFont &wx_font, std::unique_ptr font_file) { if (font_file == nullptr) return false; m_style_cache.wx_font = wx_font; // copy m_style_cache.font_file = - Emboss::FontFileWithCache(std::move(font_file)); + FontFileWithCache(std::move(font_file)); EmbossStyle &style = m_style_cache.style; style.type = WxFontUtils::get_actual_type(); diff --git a/src/slic3r/Utils/EmbossStyleManager.hpp b/src/slic3r/Utils/EmbossStyleManager.hpp index 0012bcd3cf..af915d1449 100644 --- a/src/slic3r/Utils/EmbossStyleManager.hpp +++ b/src/slic3r/Utils/EmbossStyleManager.hpp @@ -11,24 +11,24 @@ class wxFont; -namespace Slic3r::GUI { +namespace Slic3r::GUI::Emboss { /// /// Manage Emboss text styles /// Cache actual state of style /// + imgui font /// + wx font /// -class EmbossStyleManager +class StyleManager { friend class CreateFontStyleImagesJob; // access to StyleImagesData public: - EmbossStyleManager(const ImWchar *language_glyph_range); + StyleManager(const ImWchar *language_glyph_range); /// /// Release imgui font and style images from GPU /// - ~EmbossStyleManager(); + ~StyleManager(); /// /// Load font style list from config @@ -111,7 +111,7 @@ public: FontProp &get_font_prop() { return get_style().prop; } const std::optional &get_wx_font() const { return m_style_cache.wx_font; } const std::optional &get_stored_wx_font() const { return m_style_cache.stored_wx_font; } - Emboss::FontFileWithCache &get_font_file_with_cache() { return m_style_cache.font_file; } + Slic3r::Emboss::FontFileWithCache &get_font_file_with_cache() { return m_style_cache.font_file; } bool has_collections() const { return m_style_cache.font_file.font_file != nullptr && m_style_cache.font_file.font_file->infos.size() > 1; } @@ -132,7 +132,7 @@ public: /// Must be source of font file /// font file created by WxFontUtils::create_font_file(wx_font) /// True on success otherwise false - bool set_wx_font(const wxFont &wx_font, std::unique_ptr font_file ); + bool set_wx_font(const wxFont &wx_font, std::unique_ptr font_file); // Getter on acitve font pointer for imgui // Initialize imgui font(generate texture) when doesn't exist yet. @@ -191,7 +191,7 @@ public: // Value out of limits is crop static float min_imgui_font_size; static float max_imgui_font_size; - static float get_imgui_font_size(const FontProp& prop, const Emboss::FontFile& file); + static float get_imgui_font_size(const FontProp &prop, const Slic3r::Emboss::FontFile &file); private: // erase font when not possible to load @@ -207,7 +207,7 @@ private: struct StyleCache { // share font file data with emboss job thread - Emboss::FontFileWithCache font_file = {}; + Slic3r::Emboss::FontFileWithCache font_file = {}; // must live same as imgui_font inside of atlas ImVector ranges = {}; @@ -251,7 +251,7 @@ private: { struct Item { - Emboss::FontFileWithCache font; + Slic3r::Emboss::FontFileWithCache font; std::string text; FontProp prop; }; diff --git a/src/slic3r/Utils/WxFontUtils.hpp b/src/slic3r/Utils/WxFontUtils.hpp index d047740b1f..9732d2dc91 100644 --- a/src/slic3r/Utils/WxFontUtils.hpp +++ b/src/slic3r/Utils/WxFontUtils.hpp @@ -51,7 +51,7 @@ public: /// wx descriptor of font /// file described in wx font /// New created font fileon success otherwise nullptr - static std::unique_ptr set_italic(wxFont &font, const Emboss::FontFile& prev_font_file); + static std::unique_ptr set_italic(wxFont &font, const Slic3r::Emboss::FontFile &prev_font_file); /// /// Set boldness into wx font @@ -61,7 +61,7 @@ public: /// wx descriptor of font /// file described in wx font /// New created font fileon success otherwise nullptr - static std::unique_ptr set_bold(wxFont &font, const Emboss::FontFile& font_file); + static std::unique_ptr set_bold(wxFont &font, const Slic3r::Emboss::FontFile &font_file); // convert wxFont types to string and vice versa static const boost::bimap type_to_family;