From 733b70b26f3405a34ba0f5e78e44295592642aa8 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 26 May 2023 16:50:27 +0200 Subject: [PATCH] Separate horizontal and vertical align Vertically align on base line of text to be able set base line of per glyph independent on align --- src/libslic3r/Emboss.cpp | 74 ++++++++++----------- src/libslic3r/Format/3mf.cpp | 15 +++-- src/libslic3r/TextConfiguration.hpp | 29 +++------ src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 87 ++++++------------------- src/slic3r/GUI/Jobs/EmbossJob.cpp | 4 -- 5 files changed, 71 insertions(+), 138 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index bfd377fa02..7352516633 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -1287,12 +1287,14 @@ ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache, const char *t namespace { /// -/// Align expolygons by type +/// Align shape against pivot /// -/// Type of alignment -/// shapes to align -/// Same size as shape for align per line(detect of end line - '\n') -void align_shape(FontProp::Align type, std::vector &shape, const std::wstring &text); +/// Horizontal and vertical alignment +/// Shapes to align +/// Prerequisities: shapes are aligned left top +/// To detect end of lines +/// Height of line for align[in font points] +void align_shape(FontProp::Align type, std::vector &shape, const std::wstring &text, int line_height); } std::vector Emboss::text2vshapes(FontFileWithCache &font_with_cache, const std::wstring& text, const FontProp &font_prop, const std::function& was_canceled){ @@ -1317,7 +1319,7 @@ std::vector Emboss::text2vshapes(FontFileWithCache &font_with_cache, result.emplace_back(letter2shapes(letter, cursor, font_with_cache, font_prop, font_info_cache)); } - align_shape(font_prop.align, result, text); + align_shape(font_prop.align, result, text, get_line_height(font, font_prop)); return result; } @@ -1927,47 +1929,37 @@ PolygonPoints Emboss::sample_slice(const TextLine &slice, const BoundingBoxes &b } namespace { -int32_t get_align_y_offset(FontProp::Align type, const BoundingBox &bb){ - switch (type) { - case Slic3r::FontProp::Align::first_line_left: - case Slic3r::FontProp::Align::first_line_right: - case Slic3r::FontProp::Align::first_line_center: break; // No change - case Slic3r::FontProp::Align::center_left: - case Slic3r::FontProp::Align::center_right: - case Slic3r::FontProp::Align::center_center: return -bb.center().y(); - case Slic3r::FontProp::Align::top_left: - case Slic3r::FontProp::Align::top_right: - case Slic3r::FontProp::Align::top_center: return -bb.max.y(); break; // direction of Y in 2d is from top to bottom - case Slic3r::FontProp::Align::bottom_left: - case Slic3r::FontProp::Align::bottom_right: - case Slic3r::FontProp::Align::bottom_center: return -bb.min.y(); // direction of Y in 2d is from top to bottom - default: break; +int32_t get_align_y_offset(FontProp::VerticalAlign align, int count_lines, int line_height) +{ + // direction of Y in 2d is from top to bottom + // zero is on base line of first line + switch (align) { + case FontProp::VerticalAlign::center: + return ((count_lines-1) / 2) * line_height + + ((count_lines % 2 == 0) ? (line_height / 2) : 0); + case FontProp::VerticalAlign::bottom: + return (count_lines-1) * line_height; + case FontProp::VerticalAlign::top: // no change + default: + break; } return 0; } -int32_t get_align_x_offset(FontProp::Align type, const BoundingBox &shape_bb, const BoundingBox &line_bb) +int32_t get_align_x_offset(FontProp::HorizontalAlign align, const BoundingBox &shape_bb, const BoundingBox &line_bb) { - switch (type) { - case Slic3r::FontProp::Align::first_line_center: - case Slic3r::FontProp::Align::center_center: - case Slic3r::FontProp::Align::top_center: - case Slic3r::FontProp::Align::bottom_center: return -shape_bb.center().x() + (shape_bb.size().x() - line_bb.size().x())/2; - case Slic3r::FontProp::Align::first_line_left: break; // special case do not use offset - case Slic3r::FontProp::Align::center_left: - case Slic3r::FontProp::Align::top_left: - case Slic3r::FontProp::Align::bottom_left: return -shape_bb.min.x(); - case Slic3r::FontProp::Align::first_line_right: - case Slic3r::FontProp::Align::center_right: - case Slic3r::FontProp::Align::top_right: - case Slic3r::FontProp::Align::bottom_right: return -shape_bb.max.x() + (shape_bb.size().x() - line_bb.size().x()); + switch (align) { + case FontProp::HorizontalAlign::right: return -shape_bb.max.x() + (shape_bb.size().x() - line_bb.size().x()); + case FontProp::HorizontalAlign::center: return -shape_bb.center().x() + (shape_bb.size().x() - line_bb.size().x()) / 2; + case FontProp::HorizontalAlign::left: // no change default: break; } return 0; } -void align_shape(FontProp::Align type, std::vector &shapes, const std::wstring &text) +void align_shape(FontProp::Align type, std::vector &shapes, const std::wstring &text, int line_height) { - if (type == FontProp::Align::first_line_left) + constexpr FontProp::Align no_change(FontProp::HorizontalAlign::left, FontProp::VerticalAlign::top); + if (type == no_change) return; // no alignment BoundingBox shape_bb; @@ -1982,13 +1974,13 @@ void align_shape(FontProp::Align type, std::vector &shapes, const st }; Point offset( - get_align_x_offset(type, shape_bb, get_line_bb(0)), - get_align_y_offset(type, shape_bb)); + get_align_x_offset(type.first, shape_bb, get_line_bb(0)), + get_align_y_offset(type.second, get_count_lines(text), line_height)); assert(shapes.size() == text.length()); for (size_t i = 0; i < shapes.size(); ++i) { - wchar_t letter = text[i]; + wchar_t letter = text[i]; if (letter == '\n'){ - offset.x() = get_align_x_offset(type, shape_bb, get_line_bb(i+1)); + offset.x() = get_align_x_offset(type.first, shape_bb, get_line_bb(i+1)); continue; } ExPolygons &shape = shapes[i]; diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index dfe2a90a4c..f3ad9535d4 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -164,7 +164,8 @@ 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 *HORIZONTAL_ALIGN_ATTR = "horizontal"; +static constexpr const char *VERTICAL_ALIGN_ATTR = "vertical"; static constexpr const char *COLLECTION_NUMBER_ATTR = "collection"; static constexpr const char *FONT_FAMILY_ATTR = "family"; @@ -3534,8 +3535,8 @@ void TextConfigurationSerialization::to_xml(std::stringstream &stream, const Tex 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) << "\" "; + stream << HORIZONTAL_ALIGN_ATTR << "=\"" << static_cast(fp.align.first) << "\" "; + stream << VERTICAL_ALIGN_ATTR << "=\"" << static_cast(fp.align.second) << "\" "; if (fp.collection_number.has_value()) stream << COLLECTION_NUMBER_ATTR << "=\"" << *fp.collection_number << "\" "; // font descriptor @@ -3617,8 +3618,12 @@ std::optional TextConfigurationSerialization::read(const char 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 horizontal = get_attribute_value_int(attributes, num_attributes, HORIZONTAL_ALIGN_ATTR); + int vertical = get_attribute_value_int(attributes, num_attributes, VERTICAL_ALIGN_ATTR); + fp.align = FontProp::Align( + static_cast(horizontal), + static_cast(vertical)); 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 79712b8795..f60096edad 100644 --- a/src/libslic3r/TextConfiguration.hpp +++ b/src/libslic3r/TextConfiguration.hpp @@ -63,27 +63,14 @@ 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 - center_center, - center_left, - center_right, - top_center, - top_left, - top_right, - bottom_center, - bottom_left, - bottom_right - }; - + // NOTE: way of serialize to 3mf force that zero must be default value + enum class HorizontalAlign { left = 0, center, right }; + enum class VerticalAlign { top = 0, center, bottom }; + using Align = std::pair; // change pivot of text // When not set, center is used and is not stored - Align align = Align::first_line_center; - + Align align = Align(HorizontalAlign::left, VerticalAlign::top); + ////// // Duplicit data to wxFontDescriptor // used for store/load .3mf file @@ -126,7 +113,7 @@ struct FontProp // undo / redo stack recovery template void save(Archive &ar) const { - ar(emboss, use_surface, size_in_mm, per_glyph, align); + ar(emboss, use_surface, size_in_mm, per_glyph, align.first, align.second); cereal::save(ar, char_gap); cereal::save(ar, line_gap); cereal::save(ar, boldness); @@ -141,7 +128,7 @@ struct FontProp } template void load(Archive &ar) { - ar(emboss, use_surface, size_in_mm, per_glyph, align); + ar(emboss, use_surface, size_in_mm, per_glyph, align.first, align.second); 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 ffbb3c1e98..bd78b66193 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3063,39 +3063,6 @@ void GLGizmoEmboss::do_rotate(float relative_z_angle) m_parent.do_rotate(snapshot_name); } -namespace{ -bool is_left( FontProp::Align align){ return align == FontProp::Align::bottom_left || align == FontProp::Align::center_left || align == FontProp::Align::top_left; } -bool is_center_h(FontProp::Align align){ return align == FontProp::Align::bottom_center || align == FontProp::Align::center_center || align == FontProp::Align::top_center; } -bool is_right( FontProp::Align align){ return align == FontProp::Align::bottom_right || align == FontProp::Align::center_right || align == FontProp::Align::top_right; } -bool is_top( FontProp::Align align){ return align == FontProp::Align::top_left || align == FontProp::Align::top_center || align == FontProp::Align::top_right; } -bool is_center_v(FontProp::Align align){ return align == FontProp::Align::center_left || align == FontProp::Align::center_center || align == FontProp::Align::center_right; } -bool is_bottom( FontProp::Align align){ return align == FontProp::Align::bottom_left || align == FontProp::Align::bottom_center || align == FontProp::Align::bottom_right; } -void to_left(FontProp::Align &align){ - align = (align == FontProp::Align::bottom_right || align == FontProp::Align::bottom_center) ? FontProp::Align::bottom_left : - (align == FontProp::Align::center_right || align == FontProp::Align::center_center) ? FontProp::Align::center_left : - FontProp::Align::top_left;} -void to_center_h(FontProp::Align &align){ - align = (align == FontProp::Align::bottom_right || align == FontProp::Align::bottom_left) ? FontProp::Align::bottom_center : - (align == FontProp::Align::center_right || align == FontProp::Align::center_left) ? FontProp::Align::center_center : - FontProp::Align::top_center;} -void to_right(FontProp::Align &align){ - align = (align == FontProp::Align::bottom_left || align == FontProp::Align::bottom_center) ? FontProp::Align::bottom_right : - (align == FontProp::Align::center_left || align == FontProp::Align::center_center) ? FontProp::Align::center_right : - FontProp::Align::top_right;} -void to_top(FontProp::Align &align){ - align = (align == FontProp::Align::bottom_left || align == FontProp::Align::center_left) ? FontProp::Align::top_left : - (align == FontProp::Align::bottom_right || align == FontProp::Align::center_right) ? FontProp::Align::top_right : - FontProp::Align::top_center;} -void to_center_v(FontProp::Align &align){ - align = (align == FontProp::Align::bottom_left || align == FontProp::Align::top_left) ? FontProp::Align::center_left : - (align == FontProp::Align::bottom_right || align == FontProp::Align::top_right) ? FontProp::Align::center_right : - FontProp::Align::center_center;} -void to_bottom(FontProp::Align &align){ - align = (align == FontProp::Align::top_left || align == FontProp::Align::center_left) ? FontProp::Align::bottom_left : - (align == FontProp::Align::top_right || align == FontProp::Align::center_right) ? FontProp::Align::bottom_right : - FontProp::Align::bottom_center;} -} - void GLGizmoEmboss::draw_advanced() { const auto &ff = m_style_manager.get_font_file_with_cache(); @@ -3186,51 +3153,37 @@ void GLGizmoEmboss::draw_advanced() ImGui::SetTooltip("TEST PURPOSE ONLY\nMove base line (up/down) for allign letters"); m_imgui->disabled_end(); // !per_glyph - const FontProp::Align * def_align = stored_style ? &stored_style->prop.align : nullptr; - float undo_offset = ImGui::GetStyle().FramePadding.x; - //auto draw = [&selected_align, gui_cfg = m_gui_cfg]() { - // // order must match align enum - // const char* align_names[] = { "first_line_center", - // "first_line_left", - // "first_line_right", - // "center_center", - // "center_left", - // "center_right", - // "top_center", - // "top_left", - // "top_right", - // "bottom_center", - // "bottom_left", - // "bottom_right"}; - // ImGui::SameLine(gui_cfg->advanced_input_offset); - // ImGui::SetNextItemWidth(gui_cfg->input_width); - // return ImGui::Combo("##text_alignment", &selected_align, align_names, IM_ARRAYSIZE(align_names)); - //}; - auto draw_align = [&align = font_prop.align, gui_cfg = m_gui_cfg, &icons = m_icons]() { bool is_change = false; ImGui::SameLine(gui_cfg->advanced_input_offset); - if (is_left(align)) Slic3r::GUI::draw(get_icon(icons, IconType::align_horizontal_left, IconState::hovered)); - else if (draw_button(icons, IconType::align_horizontal_left)) { to_left(align); is_change = true; } + if (align.first==FontProp::HorizontalAlign::left) draw(get_icon(icons, IconType::align_horizontal_left, IconState::hovered)); + else if (draw_button(icons, IconType::align_horizontal_left)) { align.first=FontProp::HorizontalAlign::left; is_change = true; } + else if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _u8L("Set left alignment").c_str()); ImGui::SameLine(); - if (is_center_h(align)) Slic3r::GUI::draw(get_icon(icons, IconType::align_horizontal_center, IconState::hovered)); - else if (draw_button(icons, IconType::align_horizontal_center)) { to_center_h(align); is_change = true; } + if (align.first==FontProp::HorizontalAlign::center) draw(get_icon(icons, IconType::align_horizontal_center, IconState::hovered)); + else if (draw_button(icons, IconType::align_horizontal_center)) { align.first=FontProp::HorizontalAlign::center; is_change = true; } + else if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _u8L("Set horizont center alignment").c_str()); ImGui::SameLine(); - if (is_right(align)) Slic3r::GUI::draw(get_icon(icons, IconType::align_horizontal_right, IconState::hovered)); - else if (draw_button(icons, IconType::align_horizontal_right)) { to_right(align); is_change = true; } + if (align.first==FontProp::HorizontalAlign::right) draw(get_icon(icons, IconType::align_horizontal_right, IconState::hovered)); + else if (draw_button(icons, IconType::align_horizontal_right)) { align.first=FontProp::HorizontalAlign::right; is_change = true; } + else if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _u8L("Set right alignment").c_str()); ImGui::SameLine(); - if (is_top(align)) Slic3r::GUI::draw(get_icon(icons, IconType::align_vertical_top, IconState::hovered)); - else if (draw_button(icons, IconType::align_vertical_top)) { to_top(align); is_change = true; } + if (align.second==FontProp::VerticalAlign::top) draw(get_icon(icons, IconType::align_vertical_top, IconState::hovered)); + else if (draw_button(icons, IconType::align_vertical_top)) { align.second=FontProp::VerticalAlign::top; is_change = true; } + else if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _u8L("Set top alignment").c_str()); ImGui::SameLine(); - if (is_center_v(align)) Slic3r::GUI::draw(get_icon(icons, IconType::align_vertical_center, IconState::hovered)); - else if (draw_button(icons, IconType::align_vertical_center)) { to_center_v(align); is_change = true; } + if (align.second==FontProp::VerticalAlign::center) draw(get_icon(icons, IconType::align_vertical_center, IconState::hovered)); + else if (draw_button(icons, IconType::align_vertical_center)) { align.second=FontProp::VerticalAlign::center; is_change = true; } + else if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _u8L("Set vertical center alignment").c_str()); ImGui::SameLine(); - if (is_bottom(align)) Slic3r::GUI::draw(get_icon(icons, IconType::align_vertical_bottom, IconState::hovered)); - else if (draw_button(icons, IconType::align_vertical_bottom)) { to_bottom(align); is_change = true; } + if (align.second==FontProp::VerticalAlign::bottom) draw(get_icon(icons, IconType::align_vertical_bottom, IconState::hovered)); + else if (draw_button(icons, IconType::align_vertical_bottom)) { align.second=FontProp::VerticalAlign::bottom; is_change = true; } + else if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _u8L("Set bottom alignment").c_str()); return is_change; }; - + const FontProp::Align * def_align = stored_style ? &stored_style->prop.align : nullptr; + float undo_offset = ImGui::GetStyle().FramePadding.x; if (revertible(tr.alignment, font_prop.align, def_align, _u8L("Revert alignment."), undo_offset, draw_align)) { if (font_prop.per_glyph) reinit_text_lines(m_text_lines.get_lines().size()); diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index b3afec427a..3b631b86ca 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -247,10 +247,6 @@ void UpdateJob::process(Ctl &ctl) if (was_canceled()) return; if (m_result.its.empty()) throw priv::JobException("Created text volume is empty. Change text or font."); - - // center triangle mesh - //Vec3d shift = m_result.bounding_box().center(); - //m_result.translate(-shift.cast()); } void UpdateJob::finalize(bool canceled, std::exception_ptr &eptr)