From 35044e29c364a02f7655b385a52576e34441aab4 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 23 May 2023 10:30:14 +0200 Subject: [PATCH] Create text volume with feature per letter transformation. --- src/libslic3r/Format/3mf.cpp | 11 ++++ src/libslic3r/TextConfiguration.hpp | 19 +++--- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 82 ++++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 1 - src/slic3r/GUI/TextLines.cpp | 6 +- src/slic3r/GUI/TextLines.hpp | 2 +- 6 files changed, 76 insertions(+), 45 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index f7f68f43f7..dfe2a90a4c 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -163,6 +163,8 @@ static constexpr const char *BOLDNESS_ATTR = "boldness"; static constexpr const char *SKEW_ATTR = "skew"; static constexpr const char *DISTANCE_ATTR = "distance"; static constexpr const char *ANGLE_ATTR = "angle"; +static constexpr const char *PER_GLYPH_ATTR = "per_glyph"; +static constexpr const char *ALIGN_ATTR = "align"; static constexpr const char *COLLECTION_NUMBER_ATTR = "collection"; static constexpr const char *FONT_FAMILY_ATTR = "family"; @@ -3530,6 +3532,10 @@ void TextConfigurationSerialization::to_xml(std::stringstream &stream, const Tex stream << DISTANCE_ATTR << "=\"" << *fp.distance << "\" "; if (fp.angle.has_value()) stream << ANGLE_ATTR << "=\"" << *fp.angle << "\" "; + if (fp.per_glyph) + stream << PER_GLYPH_ATTR << "=\"" << 1 << "\" "; + if (fp.align != FontProp().align) // differ to default value? back compatibility + stream << ALIGN_ATTR << "=\"" << static_cast(fp.align) << "\" "; if (fp.collection_number.has_value()) stream << COLLECTION_NUMBER_ATTR << "=\"" << *fp.collection_number << "\" "; // font descriptor @@ -3609,6 +3615,11 @@ std::optional TextConfigurationSerialization::read(const char float angle = get_attribute_value_float(attributes, num_attributes, ANGLE_ATTR); if (std::fabs(angle) > std::numeric_limits::epsilon()) fp.angle = angle; + int per_glyph = get_attribute_value_int(attributes, num_attributes, PER_GLYPH_ATTR); + if (per_glyph == 1) fp.per_glyph = true; + int align = get_attribute_value_int(attributes, num_attributes, ALIGN_ATTR); + fp.align = static_cast(align); + int collection_number = get_attribute_value_int(attributes, num_attributes, COLLECTION_NUMBER_ATTR); if (collection_number > 0) fp.collection_number = static_cast(collection_number); diff --git a/src/libslic3r/TextConfiguration.hpp b/src/libslic3r/TextConfiguration.hpp index 634284bf73..79712b8795 100644 --- a/src/libslic3r/TextConfiguration.hpp +++ b/src/libslic3r/TextConfiguration.hpp @@ -63,20 +63,23 @@ struct FontProp // Distiguish projection per glyph bool per_glyph; + // Enumerate type of allowed text align enum class Align { + // NOTE: default value must be zero - 3mf store + first_line_center = 0, // use Y zero same as first letter first_line_left, // it depends on position of zero for first letter (no shape move) first_line_right, // use Y zero same as first letter - first_line_center, // use Y zero same as first letter + center_center, center_left, center_right, - center_center, + top_center, top_left, top_right, - top_center, + bottom_center, bottom_left, - bottom_right, - bottom_center + bottom_right }; + // change pivot of text // When not set, center is used and is not stored Align align = Align::first_line_center; @@ -110,6 +113,8 @@ struct FontProp char_gap == other.char_gap && line_gap == other.line_gap && use_surface == other.use_surface && + per_glyph == other.per_glyph && + align == other.align && is_approx(emboss, other.emboss) && is_approx(size_in_mm, other.size_in_mm) && is_approx(boldness, other.boldness) && @@ -121,7 +126,7 @@ struct FontProp // undo / redo stack recovery template void save(Archive &ar) const { - ar(emboss, use_surface, size_in_mm); + ar(emboss, use_surface, size_in_mm, per_glyph, align); cereal::save(ar, char_gap); cereal::save(ar, line_gap); cereal::save(ar, boldness); @@ -136,7 +141,7 @@ struct FontProp } template void load(Archive &ar) { - ar(emboss, use_surface, size_in_mm); + ar(emboss, use_surface, size_in_mm, per_glyph, align); cereal::load(ar, char_gap); cereal::load(ar, line_gap); cereal::load(ar, boldness); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 13eee61f83..e0498cba3e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -146,9 +146,15 @@ namespace priv { /// /// Text to emboss /// Keep actual selected style +/// Needed when transform per glyph +/// Needed for transform per glyph /// Cancel for previous job /// Base data for emboss text -static DataBase create_emboss_data_base(const std::string &text, StyleManager &style_manager, std::shared_ptr> &cancel); +static DataBase create_emboss_data_base(const std::string &text, + StyleManager &style_manager, + TextLinesModel &text_lines, + const Selection &selection, + std::shared_ptr> &cancel); /// /// Start job for add new volume to object with given transformation @@ -242,13 +248,17 @@ static bool apply_camera_dir(const Camera &camera, GLCanvas3D &canvas, bool keep } // namespace priv +namespace { +void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines=0); +} + void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos) { if (!init_create(volume_type)) return; const GLVolume *gl_volume = get_first_hovered_gl_volume(m_parent); - DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); + DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_job_cancel); bool is_simple_mode = wxGetApp().get_mode() == comSimple; if (gl_volume != nullptr && !is_simple_mode) { // Try to cast ray into scene and find object for add volume @@ -275,7 +285,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type) Size s = m_parent.get_canvas_size(); Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.); - DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); + DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_job_cancel); const ModelObjectPtrs &objects = selection.get_model()->objects; bool is_simple_mode = wxGetApp().get_mode() == comSimple; // No selected object so create new object @@ -559,7 +569,7 @@ void GLGizmoEmboss::volume_transformation_changing() } const FontProp &prop = m_volume->text_configuration->style.prop; if (prop.per_glyph) - init_text_lines(m_text_lines.get_lines().size()); + init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, m_text_lines.get_lines().size()); } void GLGizmoEmboss::volume_transformation_changed() @@ -571,7 +581,7 @@ void GLGizmoEmboss::volume_transformation_changed() const FontProp &prop = m_volume->text_configuration->style.prop; if (prop.per_glyph) - init_text_lines(m_text_lines.get_lines().size()); + init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, m_text_lines.get_lines().size()); // Update surface by new position if (prop.use_surface || prop.per_glyph) @@ -1047,11 +1057,13 @@ EmbossStyles GLGizmoEmboss::create_default_styles() return styles; } -void GLGizmoEmboss::init_text_lines(unsigned count_lines){ - assert(m_style_manager.is_active_font()); - if (!m_style_manager.is_active_font()) +namespace { +void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines) +{ + assert(style_manager.is_active_font()); + if (!style_manager.is_active_font()) return; - const auto& ffc = m_style_manager.get_font_file_with_cache(); + const auto &ffc = style_manager.get_font_file_with_cache(); assert(ffc.has_value()); if (!ffc.has_value()) return; @@ -1060,14 +1072,12 @@ void GLGizmoEmboss::init_text_lines(unsigned count_lines){ if (ff_ptr == nullptr) return; - if (m_volume->is_the_only_one_part()) - return; - - const FontProp& fp = m_style_manager.get_font_prop(); + const FontProp &fp = style_manager.get_font_prop(); const FontFile &ff = *ff_ptr; - double line_height = TextLinesModel::calc_line_height(ff, fp); - m_text_lines.init(m_parent.get_selection(), line_height, count_lines); + double line_height = TextLinesModel::calc_line_height(ff, fp); + text_lines.init(selection, line_height, count_lines); +} } void GLGizmoEmboss::set_volume_by_selection() @@ -1209,7 +1219,7 @@ void GLGizmoEmboss::set_volume_by_selection() m_volume_id = volume->id(); if (tc.style.prop.per_glyph) - init_text_lines(); + init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager); // Calculate current angle of up vector assert(m_style_manager.is_active_font()); @@ -1292,12 +1302,8 @@ bool GLGizmoEmboss::process() // exist loaded font file? if (!m_style_manager.is_active_font()) return false; - DataUpdate data{priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel), m_volume->id()}; - if (data.text_configuration.style.prop.per_glyph){ - if (!m_text_lines.is_init()) - init_text_lines(); - data.text_lines = m_text_lines.get_lines(); // copy - } + DataUpdate data{priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_job_cancel), + m_volume->id()}; std::unique_ptr job = nullptr; // check cutting from source mesh @@ -1547,7 +1553,7 @@ void GLGizmoEmboss::draw_text_input() unsigned count_lines = get_count_lines(m_text); if (count_lines != m_text_lines.get_lines().size()) // Necesarry to initialize count by given number (differ from stored in volume at the moment) - init_text_lines(count_lines); + init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, count_lines); } process(); range_text = create_range_text_prep(); @@ -3231,7 +3237,7 @@ void GLGizmoEmboss::draw_advanced() if (ImGui::Checkbox("##PerGlyph", per_glyph)) { if (*per_glyph) { if (!m_text_lines.is_init()) - init_text_lines(); + init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager); } process(); } else if (ImGui::IsItemHovered()) { @@ -3240,7 +3246,7 @@ void GLGizmoEmboss::draw_advanced() } else { ImGui::SetTooltip("%s", _u8L("Set position and orientation of projection per Glyph.").c_str()); if (!m_text_lines.is_init()) - init_text_lines(); + init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager); } } else if (!*per_glyph && m_text_lines.is_init()) m_text_lines.reset(); @@ -3248,24 +3254,24 @@ void GLGizmoEmboss::draw_advanced() ImGui::SameLine(); ImGui::SetNextItemWidth(100); if (ImGui::SliderFloat("##base_line_y_offset", &m_text_lines.offset, -10.f, 10.f, "%f mm")) { - init_text_lines(m_text_lines.get_lines().size()); + init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, m_text_lines.get_lines().size()); process(); } else if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _u8L("Move base line (up/down) for allign letters").c_str()); // order must match align enum - const char* align_names[] = {"first_line_left", + const char* align_names[] = { "first_line_center", + "first_line_left", "first_line_right", - "first_line_center", + "center_center", "center_left", "center_right", - "center_center", - "top_left", - "top_right", "top_center", + "top_left", + "top_right", + "bottom_center", "bottom_left", - "bottom_right", - "bottom_center"}; + "bottom_right"}; int selected_align = static_cast(font_prop.align); if (ImGui::Combo("align", &selected_align, align_names, IM_ARRAYSIZE(align_names))) { font_prop.align = static_cast(selected_align); @@ -3542,7 +3548,7 @@ bool priv::draw_button(const IconManager::VIcons &icons, IconType type, bool dis // priv namespace implementation /////////////// -DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &style_manager, std::shared_ptr>& cancel) +DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &style_manager, TextLinesModel& text_lines, const Selection& selection, std::shared_ptr>& cancel) { // create volume_name std::string volume_name = text; // copy @@ -3565,6 +3571,12 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &st assert(es.path.compare(WxFontUtils::store_wxFont(style_manager.get_wx_font())) == 0); TextConfiguration tc{es, text}; + if (es.prop.per_glyph) { + if (!text_lines.is_init()) + init_text_lines(text_lines, selection, style_manager); + } else + text_lines.reset(); + // Cancel previous Job, when it is in process // worker.cancel(); --> Use less in this case I want cancel only previous EmbossJob no other jobs // Cancel only EmbossUpdateJob no others @@ -3572,7 +3584,7 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &st cancel->store(true); // create new shared ptr to cancel new job cancel = std::make_shared>(false); - return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), tc, volume_name, cancel}; + return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), tc, volume_name, cancel, text_lines.get_lines()}; } void priv::start_create_object_job(DataBase &emboss_data, const Vec2d &coor) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index c7a4f3ef3e..34b7dd5de4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -317,7 +317,6 @@ private: // Keep information about curvature of text line around surface TextLinesModel m_text_lines; - void init_text_lines(unsigned count_lines = 0); // Rotation gizmo GLGizmoRotate m_rotate_gizmo; diff --git a/src/slic3r/GUI/TextLines.cpp b/src/slic3r/GUI/TextLines.cpp index 2cca79f4b3..785b21d52c 100644 --- a/src/slic3r/GUI/TextLines.cpp +++ b/src/slic3r/GUI/TextLines.cpp @@ -253,6 +253,9 @@ GLModel::Geometry create_geometry(const TextLines &lines) void TextLinesModel::init(const Selection &selection, double line_height, unsigned count_lines) { + m_model.reset(); + m_lines.clear(); + const GLVolume *gl_volume_ptr = selection.get_first_volume(); if (gl_volume_ptr == nullptr) return; @@ -267,6 +270,8 @@ void TextLinesModel::init(const Selection &selection, double line_height, unsign if (mv_ptr == nullptr) return; const ModelVolume &mv = *mv_ptr; + if (mv.is_the_only_one_part()) + return; // calculate count lines when not set if (count_lines == 0) { @@ -317,7 +322,6 @@ void TextLinesModel::init(const Selection &selection, double line_height, unsign for (size_t i = 0; i < count_lines; ++i) m_lines[i].y = line_centers[i]; - m_model.reset(); //* GLModel::Geometry geometry = create_geometry(m_lines); if (geometry.vertices_count() == 0 || geometry.indices_count() == 0) diff --git a/src/slic3r/GUI/TextLines.hpp b/src/slic3r/GUI/TextLines.hpp index 449c913f42..58f27fe22c 100644 --- a/src/slic3r/GUI/TextLines.hpp +++ b/src/slic3r/GUI/TextLines.hpp @@ -25,7 +25,7 @@ public: void render(const Transform3d &text_world); bool is_init() const { return m_model.is_initialized(); } - void reset() { m_model.reset(); } + void reset() { m_model.reset(); m_lines.clear(); } const Slic3r::Emboss::TextLines &get_lines() const { return m_lines; } static double calc_line_height(const Slic3r::Emboss::FontFile& ff, const FontProp& fp); private: