Separate horizontal and vertical align

Vertically align on base line of text to be able set base line of per glyph independent on align
This commit is contained in:
Filip Sykala - NTB T15p 2023-05-26 16:50:27 +02:00
parent 6751bba96e
commit 733b70b26f
5 changed files with 71 additions and 138 deletions

View File

@ -1287,12 +1287,14 @@ ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache, const char *t
namespace { namespace {
/// <summary> /// <summary>
/// Align expolygons by type /// Align shape against pivot
/// </summary> /// </summary>
/// <param name="type">Type of alignment</param> /// <param name="type">Horizontal and vertical alignment</param>
/// <param name="shape">shapes to align</param> /// <param name="shapes">Shapes to align
/// <param name="text">Same size as shape for align per line(detect of end line - '\n')</param> /// Prerequisities: shapes are aligned left top</param>
void align_shape(FontProp::Align type, std::vector<ExPolygons> &shape, const std::wstring &text); /// <param name="text">To detect end of lines</param>
/// <param name="line_height">Height of line for align[in font points]</param>
void align_shape(FontProp::Align type, std::vector<ExPolygons> &shape, const std::wstring &text, int line_height);
} }
std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache, const std::wstring& text, const FontProp &font_prop, const std::function<bool()>& was_canceled){ std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache, const std::wstring& text, const FontProp &font_prop, const std::function<bool()>& was_canceled){
@ -1317,7 +1319,7 @@ std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache,
result.emplace_back(letter2shapes(letter, cursor, font_with_cache, font_prop, font_info_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; return result;
} }
@ -1927,47 +1929,37 @@ PolygonPoints Emboss::sample_slice(const TextLine &slice, const BoundingBoxes &b
} }
namespace { namespace {
int32_t get_align_y_offset(FontProp::Align type, const BoundingBox &bb){ int32_t get_align_y_offset(FontProp::VerticalAlign align, int count_lines, int line_height)
switch (type) { {
case Slic3r::FontProp::Align::first_line_left: // direction of Y in 2d is from top to bottom
case Slic3r::FontProp::Align::first_line_right: // zero is on base line of first line
case Slic3r::FontProp::Align::first_line_center: break; // No change switch (align) {
case Slic3r::FontProp::Align::center_left: case FontProp::VerticalAlign::center:
case Slic3r::FontProp::Align::center_right: return ((count_lines-1) / 2) * line_height
case Slic3r::FontProp::Align::center_center: return -bb.center().y(); + ((count_lines % 2 == 0) ? (line_height / 2) : 0);
case Slic3r::FontProp::Align::top_left: case FontProp::VerticalAlign::bottom:
case Slic3r::FontProp::Align::top_right: return (count_lines-1) * line_height;
case Slic3r::FontProp::Align::top_center: return -bb.max.y(); break; // direction of Y in 2d is from top to bottom case FontProp::VerticalAlign::top: // no change
case Slic3r::FontProp::Align::bottom_left: default:
case Slic3r::FontProp::Align::bottom_right: break;
case Slic3r::FontProp::Align::bottom_center: return -bb.min.y(); // direction of Y in 2d is from top to bottom
default: break;
} }
return 0; 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) { switch (align) {
case Slic3r::FontProp::Align::first_line_center: case FontProp::HorizontalAlign::right: return -shape_bb.max.x() + (shape_bb.size().x() - line_bb.size().x());
case Slic3r::FontProp::Align::center_center: case FontProp::HorizontalAlign::center: return -shape_bb.center().x() + (shape_bb.size().x() - line_bb.size().x()) / 2;
case Slic3r::FontProp::Align::top_center: case FontProp::HorizontalAlign::left: // no change
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());
default: break; default: break;
} }
return 0; return 0;
} }
void align_shape(FontProp::Align type, std::vector<ExPolygons> &shapes, const std::wstring &text) void align_shape(FontProp::Align type, std::vector<ExPolygons> &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 return; // no alignment
BoundingBox shape_bb; BoundingBox shape_bb;
@ -1982,13 +1974,13 @@ void align_shape(FontProp::Align type, std::vector<ExPolygons> &shapes, const st
}; };
Point offset( Point offset(
get_align_x_offset(type, shape_bb, get_line_bb(0)), get_align_x_offset(type.first, shape_bb, get_line_bb(0)),
get_align_y_offset(type, shape_bb)); get_align_y_offset(type.second, get_count_lines(text), line_height));
assert(shapes.size() == text.length()); assert(shapes.size() == text.length());
for (size_t i = 0; i < shapes.size(); ++i) { for (size_t i = 0; i < shapes.size(); ++i) {
wchar_t letter = text[i]; wchar_t letter = text[i];
if (letter == '\n'){ 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; continue;
} }
ExPolygons &shape = shapes[i]; ExPolygons &shape = shapes[i];

View File

@ -164,7 +164,8 @@ static constexpr const char *SKEW_ATTR = "skew";
static constexpr const char *DISTANCE_ATTR = "distance"; static constexpr const char *DISTANCE_ATTR = "distance";
static constexpr const char *ANGLE_ATTR = "angle"; static constexpr const char *ANGLE_ATTR = "angle";
static constexpr const char *PER_GLYPH_ATTR = "per_glyph"; 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 *COLLECTION_NUMBER_ATTR = "collection";
static constexpr const char *FONT_FAMILY_ATTR = "family"; 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 << "\" "; stream << ANGLE_ATTR << "=\"" << *fp.angle << "\" ";
if (fp.per_glyph) if (fp.per_glyph)
stream << PER_GLYPH_ATTR << "=\"" << 1 << "\" "; stream << PER_GLYPH_ATTR << "=\"" << 1 << "\" ";
if (fp.align != FontProp().align) // differ to default value? back compatibility stream << HORIZONTAL_ALIGN_ATTR << "=\"" << static_cast<int>(fp.align.first) << "\" ";
stream << ALIGN_ATTR << "=\"" << static_cast<int>(fp.align) << "\" "; stream << VERTICAL_ALIGN_ATTR << "=\"" << static_cast<int>(fp.align.second) << "\" ";
if (fp.collection_number.has_value()) if (fp.collection_number.has_value())
stream << COLLECTION_NUMBER_ATTR << "=\"" << *fp.collection_number << "\" "; stream << COLLECTION_NUMBER_ATTR << "=\"" << *fp.collection_number << "\" ";
// font descriptor // font descriptor
@ -3617,8 +3618,12 @@ std::optional<TextConfiguration> TextConfigurationSerialization::read(const char
fp.angle = angle; fp.angle = angle;
int per_glyph = get_attribute_value_int(attributes, num_attributes, PER_GLYPH_ATTR); int per_glyph = get_attribute_value_int(attributes, num_attributes, PER_GLYPH_ATTR);
if (per_glyph == 1) fp.per_glyph = true; if (per_glyph == 1) fp.per_glyph = true;
int align = get_attribute_value_int(attributes, num_attributes, ALIGN_ATTR);
fp.align = static_cast<FontProp::Align>(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<FontProp::HorizontalAlign>(horizontal),
static_cast<FontProp::VerticalAlign>(vertical));
int collection_number = get_attribute_value_int(attributes, num_attributes, COLLECTION_NUMBER_ATTR); int collection_number = get_attribute_value_int(attributes, num_attributes, COLLECTION_NUMBER_ATTR);
if (collection_number > 0) fp.collection_number = static_cast<unsigned int>(collection_number); if (collection_number > 0) fp.collection_number = static_cast<unsigned int>(collection_number);

View File

@ -63,27 +63,14 @@ struct FontProp
// Distiguish projection per glyph // Distiguish projection per glyph
bool per_glyph; bool per_glyph;
// Enumerate type of allowed text align // NOTE: way of serialize to 3mf force that zero must be default value
enum class Align { enum class HorizontalAlign { left = 0, center, right };
// NOTE: default value must be zero - 3mf store enum class VerticalAlign { top = 0, center, bottom };
first_line_center = 0, // use Y zero same as first letter using Align = std::pair<HorizontalAlign, VerticalAlign>;
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
};
// change pivot of text // change pivot of text
// When not set, center is used and is not stored // 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 // Duplicit data to wxFontDescriptor
// used for store/load .3mf file // used for store/load .3mf file
@ -126,7 +113,7 @@ struct FontProp
// undo / redo stack recovery // undo / redo stack recovery
template<class Archive> void save(Archive &ar) const template<class Archive> 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, char_gap);
cereal::save(ar, line_gap); cereal::save(ar, line_gap);
cereal::save(ar, boldness); cereal::save(ar, boldness);
@ -141,7 +128,7 @@ struct FontProp
} }
template<class Archive> void load(Archive &ar) template<class Archive> 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, char_gap);
cereal::load(ar, line_gap); cereal::load(ar, line_gap);
cereal::load(ar, boldness); cereal::load(ar, boldness);

View File

@ -3063,39 +3063,6 @@ void GLGizmoEmboss::do_rotate(float relative_z_angle)
m_parent.do_rotate(snapshot_name); 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() void GLGizmoEmboss::draw_advanced()
{ {
const auto &ff = m_style_manager.get_font_file_with_cache(); 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"); ImGui::SetTooltip("TEST PURPOSE ONLY\nMove base line (up/down) for allign letters");
m_imgui->disabled_end(); // !per_glyph 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]() { auto draw_align = [&align = font_prop.align, gui_cfg = m_gui_cfg, &icons = m_icons]() {
bool is_change = false; bool is_change = false;
ImGui::SameLine(gui_cfg->advanced_input_offset); ImGui::SameLine(gui_cfg->advanced_input_offset);
if (is_left(align)) Slic3r::GUI::draw(get_icon(icons, IconType::align_horizontal_left, IconState::hovered)); 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)) { to_left(align); is_change = true; } 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(); ImGui::SameLine();
if (is_center_h(align)) Slic3r::GUI::draw(get_icon(icons, IconType::align_horizontal_center, IconState::hovered)); 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)) { to_center_h(align); is_change = true; } 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(); ImGui::SameLine();
if (is_right(align)) Slic3r::GUI::draw(get_icon(icons, IconType::align_horizontal_right, IconState::hovered)); 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)) { to_right(align); is_change = true; } 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(); ImGui::SameLine();
if (is_top(align)) Slic3r::GUI::draw(get_icon(icons, IconType::align_vertical_top, IconState::hovered)); 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)) { to_top(align); is_change = true; } 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(); ImGui::SameLine();
if (is_center_v(align)) Slic3r::GUI::draw(get_icon(icons, IconType::align_vertical_center, IconState::hovered)); 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)) { to_center_v(align); is_change = true; } 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(); ImGui::SameLine();
if (is_bottom(align)) Slic3r::GUI::draw(get_icon(icons, IconType::align_vertical_bottom, IconState::hovered)); 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)) { to_bottom(align); is_change = true; } 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; 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 (revertible(tr.alignment, font_prop.align, def_align, _u8L("Revert alignment."), undo_offset, draw_align)) {
if (font_prop.per_glyph) if (font_prop.per_glyph)
reinit_text_lines(m_text_lines.get_lines().size()); reinit_text_lines(m_text_lines.get_lines().size());

View File

@ -247,10 +247,6 @@ void UpdateJob::process(Ctl &ctl)
if (was_canceled()) return; if (was_canceled()) return;
if (m_result.its.empty()) if (m_result.its.empty())
throw priv::JobException("Created text volume is empty. Change text or font."); 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<float>());
} }
void UpdateJob::finalize(bool canceled, std::exception_ptr &eptr) void UpdateJob::finalize(bool canceled, std::exception_ptr &eptr)