Vertical alignemnt center is now in 1/3 of the font ascent

Default vertical alignement is center (for closer result to previous unaligneg version)
This commit is contained in:
Filip Sykala - NTB T15p 2023-08-07 15:59:55 +02:00
parent 6c0d7b79e3
commit 36d5353c67
8 changed files with 129 additions and 117 deletions

View File

@ -19,6 +19,10 @@
#include "libslic3r/Line.hpp"
#include "libslic3r/BoundingBox.hpp"
// Experimentaly suggested ration of font ascent by multiple fonts
// to get approx center of normal text line
const double ASCENT_CENTER = 1/3.; // 0.5 is above small letter
using namespace Slic3r;
using namespace Emboss;
using fontinfo_opt = std::optional<stbtt_fontinfo>;
@ -1207,17 +1211,21 @@ std::optional<Glyph> Emboss::letter2glyph(const FontFile &font,
return priv::get_glyph(*font_info_opt, letter, flatness);
}
int Emboss::get_line_height(const FontFile &font, const FontProp &prop) {
const FontFile::Info &Emboss::get_font_info(const FontFile &font, const FontProp &prop)
{
unsigned int font_index = prop.collection_number.value_or(0);
assert(priv::is_valid(font, font_index));
const FontFile::Info &info = font.infos[font_index];
return font.infos[font_index];
}
int Emboss::get_line_height(const FontFile &font, const FontProp &prop) {
const FontFile::Info &info = get_font_info(font, prop);
int line_height = info.ascent - info.descent + info.linegap;
line_height += prop.line_gap.value_or(0);
return static_cast<int>(line_height / SHAPE_SCALE);
}
namespace {
ExPolygons letter2shapes(
wchar_t letter, Point &cursor, FontFileWithCache &font_with_cache, const FontProp &font_prop, fontinfo_opt& font_info_cache)
{
@ -1289,12 +1297,12 @@ namespace {
/// <summary>
/// Align shape against pivot
/// </summary>
/// <param name="type">Horizontal and vertical alignment</param>
/// <param name="shapes">Shapes to align
/// Prerequisities: shapes are aligned left top</param>
/// <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);
/// <param name="text">To detect end of lines - to be able horizontal center the line</param>
/// <param name="prop">Containe Horizontal and vertical alignment</param>
/// <param name="font">Needed for scale and font size</param>
void align_shape(std::vector<ExPolygons> &shapes, const std::wstring &text, const FontProp &prop, const FontFile &font);
}
std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache, const std::wstring& text, const FontProp &font_prop, const std::function<bool()>& was_canceled){
@ -1319,7 +1327,7 @@ std::vector<ExPolygons> 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, get_line_height(font, font_prop));
align_shape(result, text, font_prop, font);
return result;
}
@ -1453,8 +1461,7 @@ std::string Emboss::create_range_text(const std::string &text,
double Emboss::get_shape_scale(const FontProp &fp, const FontFile &ff)
{
size_t font_index = fp.collection_number.value_or(0);
const FontFile::Info &info = ff.infos[font_index];
const FontFile::Info &info = get_font_info(ff, fp);
double scale = fp.size_in_mm / (double) info.unit_per_em;
// Shape is scaled for store point coordinate as integer
return scale * SHAPE_SCALE;
@ -1929,20 +1936,22 @@ PolygonPoints Emboss::sample_slice(const TextLine &slice, const BoundingBoxes &b
}
namespace {
template<typename T> T get_align_y_offset(FontProp::VerticalAlign align, unsigned count_lines, T line_height)
float get_align_y_offset(FontProp::VerticalAlign align, unsigned count_lines, const FontFile &ff, const FontProp &fp)
{
if (count_lines == 0)
return 0;
assert(count_lines != 0);
int line_height = get_line_height(ff, fp);
int ascent = get_font_info(ff, fp).ascent / SHAPE_SCALE;
float line_center = static_cast<float>(std::round(ascent * ASCENT_CENTER));
// 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;
case FontProp::VerticalAlign::bottom: return line_height * (count_lines - 1);
case FontProp::VerticalAlign::top: return -ascent;
case FontProp::VerticalAlign::center:
default:
return -line_center + line_height * (count_lines - 1) / 2.;
}
return 0;
}
int32_t get_align_x_offset(FontProp::HorizontalAlign align, const BoundingBox &shape_bb, const BoundingBox &line_bb)
@ -1956,11 +1965,10 @@ int32_t get_align_x_offset(FontProp::HorizontalAlign align, const BoundingBox &s
return 0;
}
void align_shape(FontProp::Align type, std::vector<ExPolygons> &shapes, const std::wstring &text, int line_height)
void align_shape(std::vector<ExPolygons> &shapes, const std::wstring &text, const FontProp &prop, const FontFile &font)
{
constexpr FontProp::Align no_change(FontProp::HorizontalAlign::left, FontProp::VerticalAlign::top);
if (type == no_change)
return; // no alignment
// Shapes have to match letters in text
assert(shapes.size() == text.length());
BoundingBox shape_bb;
for (const ExPolygons& shape: shapes)
@ -1972,15 +1980,30 @@ void align_shape(FontProp::Align type, std::vector<ExPolygons> &shapes, const st
line_bb.merge(get_extents(shapes[j]));
return line_bb;
};
int line_height = get_line_height(font, prop);
unsigned count_lines = get_count_lines(text);
int center_line = get_font_info(font, prop).ascent * ASCENT_CENTER;
int y_offset = get_align_y_offset(prop.align.second, count_lines, font, prop);
// Speed up for left aligned text
if (prop.align.first == FontProp::HorizontalAlign::left){
// already horizontaly aligned
for (ExPolygons shape : shapes)
for (ExPolygon &s : shape)
s.translate(Point(0, y_offset));
return;
}
// Align x line by line
Point offset(
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());
get_align_x_offset(prop.align.first, shape_bb, get_line_bb(0)),
y_offset);
for (size_t i = 0; i < shapes.size(); ++i) {
wchar_t letter = text[i];
if (letter == '\n'){
offset.x() = get_align_x_offset(type.first, shape_bb, get_line_bb(i+1));
offset.x() = get_align_x_offset(prop.align.first, shape_bb, get_line_bb(i + 1));
continue;
}
ExPolygons &shape = shapes[i];
@ -1990,8 +2013,10 @@ void align_shape(FontProp::Align type, std::vector<ExPolygons> &shapes, const st
}
} // namespace
double Emboss::get_align_y_offset(FontProp::VerticalAlign align, unsigned count_lines, double line_height){
return ::get_align_y_offset<double>(align, count_lines, line_height);
double Emboss::get_align_y_offset_in_mm(FontProp::VerticalAlign align, unsigned count_lines, const FontFile &ff, const FontProp &fp){
float offset_in_font_point = get_align_y_offset(align, count_lines, ff, fp);
double scale = get_shape_scale(fp, ff);
return scale * offset_in_font_point;
}
#ifdef REMOVE_SPIKES

View File

@ -228,6 +228,14 @@ namespace Emboss
/// <returns>Conversion to mm</returns>
double get_shape_scale(const FontProp &fp, const FontFile &ff);
/// <summary>
/// getter of font info by collection defined in prop
/// </summary>
/// <param name="font">Contain infos about all fonts(collections) in file</param>
/// <param name="prop">Index of collection</param>
/// <returns>Ascent, descent, line gap</returns>
const FontFile::Info &get_font_info(const FontFile &font, const FontProp &prop);
/// <summary>
/// Read from font file and properties height of line with spacing
/// </summary>
@ -239,12 +247,10 @@ namespace Emboss
/// <summary>
/// Calculate Vertical align
/// </summary>
/// <typeparam name="T">double for mm</typeparam>
/// <param name="align">type</param>
/// <param name="align">Top | Center | Bottom</param>
/// <param name="count_lines"></param>
/// <param name="line_height"></param>
/// <returns>In same unit as line height</returns>
double get_align_y_offset(FontProp::VerticalAlign align, unsigned count_lines, double line_height);
/// <returns>Return align Y offset in mm</returns>
double get_align_y_offset_in_mm(FontProp::VerticalAlign align, unsigned count_lines, const FontFile &ff, const FontProp &fp);
/// <summary>
/// Project spatial point

View File

@ -69,7 +69,7 @@ struct FontProp
using Align = std::pair<HorizontalAlign, VerticalAlign>;
// change pivot of text
// When not set, center is used and is not stored
Align align = Align(HorizontalAlign::center, VerticalAlign::top);
Align align = Align(HorizontalAlign::center, VerticalAlign::center);
//////
// Duplicit data to wxFontDescriptor

View File

@ -1093,41 +1093,7 @@ EmbossStyles GLGizmoEmboss::create_default_styles()
return styles;
}
namespace {
bool get_line_height_offset(/* const*/ StyleManager &style_manager, double &line_height_mm, double &line_offset_mm)
{
assert(style_manager.is_active_font());
if (!style_manager.is_active_font())
return false;
const auto &ffc = style_manager.get_font_file_with_cache();
assert(ffc.has_value());
if (!ffc.has_value())
return false;
const auto &ff_ptr = ffc.font_file;
assert(ff_ptr != nullptr);
if (ff_ptr == nullptr)
return false;
const FontProp &fp = style_manager.get_font_prop();
const FontFile &ff = *ff_ptr;
double third_ascent_shape_size = ff.infos[fp.collection_number.value_or(0)].ascent / 3.;
int line_height_shape_size = get_line_height(ff, fp); // In shape size
double scale = get_shape_scale(fp, ff);
line_offset_mm = third_ascent_shape_size * scale / SHAPE_SCALE;
line_height_mm = line_height_shape_size * scale;
if (line_height_mm < 0)
return false;
// fix for bad filled ascent in font file
if (line_offset_mm <= 0)
line_offset_mm = line_height_mm / 3;
return true;
}
namespace{
void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines)
{
const GLVolume *gl_volume_ptr = selection.get_first_volume();
@ -1178,12 +1144,7 @@ void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /*
Transform3d mv_trafo = gl_volume.get_volume_transformation().get_matrix();
if (tc.fix_3mf_tr.has_value())
mv_trafo = mv_trafo * (tc.fix_3mf_tr->inverse());
FontProp::VerticalAlign align = style_manager.get_font_prop().align.second;
double line_height_mm, line_offset_mm;
if (!get_line_height_offset(style_manager, line_height_mm, line_offset_mm))
return;
text_lines.init(mv_trafo, volumes, align, line_height_mm, line_offset_mm, count_lines);
text_lines.init(mv_trafo, volumes, style_manager, count_lines);
}
void init_new_text_line(TextLinesModel &text_lines, const Transform3d& new_text_tr, const ModelObject& mo, /* const*/ StyleManager &style_manager)
@ -1197,13 +1158,8 @@ void init_new_text_line(TextLinesModel &text_lines, const Transform3d& new_text_
continue;
volumes.push_back(volume);
}
FontProp::VerticalAlign align = style_manager.get_font_prop().align.second;
double line_height_mm, line_offset_mm;
if (!get_line_height_offset(style_manager, line_height_mm, line_offset_mm))
return;
unsigned count_lines = 1;
text_lines.init(new_text_tr, volumes, align, line_height_mm, line_offset_mm, count_lines);
text_lines.init(new_text_tr, volumes, style_manager, count_lines);
}
}
@ -3129,10 +3085,7 @@ void GLGizmoEmboss::draw_advanced()
}
FontProp &font_prop = m_style_manager.get_font_prop();
const auto &cn = m_style_manager.get_font_prop().collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
const auto &font_info = ff.font_file->infos[font_index];
const FontFile::Info &font_info = get_font_info(*ff.font_file, font_prop);
#ifdef SHOW_FONT_FILE_PROPERTY
ImGui::SameLine();
int cache_size = ff.has_value()? (int)ff.cache->size() : 0;

View File

@ -47,10 +47,8 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
// calculate conversion from FontPoint to screen pixels by size of font
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 * SHAPE_SCALE * m_input.ppm;
const FontFile::Info &info = get_font_info(*item.font.font_file, item.prop);
double scale = item.prop.size_in_mm / info.unit_per_em * SHAPE_SCALE * m_input.ppm;
scales[index] = scale;
//double scale = font_prop.size_in_mm * SCALING_FACTOR;

View File

@ -249,22 +249,60 @@ GLModel::Geometry create_geometry(const TextLines &lines)
geometry.add_triangle(t[0], t[1], t[2]);
return geometry;
}
bool get_line_height_offset(const FontProp &fp, const FontFile &ff, double &line_height_mm, double &line_offset_mm)
{
double third_ascent_shape_size = get_font_info(ff, fp).ascent / 3.;
int line_height_shape_size = get_line_height(ff, fp); // In shape size
double scale = get_shape_scale(fp, ff);
line_offset_mm = third_ascent_shape_size * scale / SHAPE_SCALE;
line_height_mm = line_height_shape_size * scale;
if (line_height_mm < 0)
return false;
// fix for bad filled ascent in font file
if (line_offset_mm <= 0)
line_offset_mm = line_height_mm / 3;
return true;
}
} // namespace
void TextLinesModel::init(const Transform3d &text_tr,
const ModelVolumePtrs &volumes_to_slice,
FontProp::VerticalAlign align,
double line_height,
double offset,
/*const*/ Emboss::StyleManager &style_manager,
unsigned count_lines)
{
assert(style_manager.is_active_font());
if (!style_manager.is_active_font())
return;
const auto &ffc = style_manager.get_font_file_with_cache();
assert(ffc.has_value());
if (!ffc.has_value())
return;
const auto &ff_ptr = ffc.font_file;
assert(ff_ptr != nullptr);
if (ff_ptr == nullptr)
return;
const FontFile &ff = *ff_ptr;
const FontProp &fp = style_manager.get_font_prop();
FontProp::VerticalAlign align = fp.align.second;
double line_height_mm, line_offset_mm;
if (!get_line_height_offset(fp, ff, line_height_mm, line_offset_mm))
return;
m_model.reset();
m_lines.clear();
double first_line_center = offset + this->offset + get_align_y_offset(align, count_lines, line_height);
double first_line_center = offset + this->offset + get_align_y_offset_in_mm(align, count_lines, ff, fp);
std::vector<float> line_centers(count_lines);
for (size_t i = 0; i < count_lines; ++i)
line_centers[i] = static_cast<float>(first_line_center - i * line_height);
line_centers[i] = static_cast<float>(first_line_center - i * line_height_mm);
// contour transformation
Transform3d c_trafo = text_tr * get_rotation();
@ -357,7 +395,7 @@ void TextLinesModel::render(const Transform3d &text_world)
double TextLinesModel::calc_line_height(const Slic3r::Emboss::FontFile &ff, const FontProp &fp)
{
int line_height = Emboss::get_line_height(ff, fp); // In shape size
double scale = Emboss::get_shape_scale(fp, ff);
int line_height = Slic3r::Emboss::get_line_height(ff, fp); // In shape size
double scale = Slic3r::Emboss::get_shape_scale(fp, ff);
return line_height * scale;
}

View File

@ -6,6 +6,7 @@
#include <libslic3r/Point.hpp>
#include <libslic3r/Emboss.hpp>
#include "slic3r/GUI/GLModel.hpp"
#include "slic3r/Utils/EmbossStyleManager.hpp"
namespace Slic3r {
class ModelVolume;
@ -24,11 +25,9 @@ public:
/// </summary>
/// <param name="text_tr">Transformation of text volume inside object (aka inside of instance)</param>
/// <param name="volumes_to_slice">Vector of volumes to be sliced</param>
/// <param name="align">Vertical (Y) align of the text</param>
/// <param name="line_height">Distance between lines [in mm]</param>
/// <param name="line_height">Offset from baseline [in mm]</param>
/// <param name="count_lines">Count lines(slices over volumes)</param>
void init(const Transform3d &text_tr, const ModelVolumePtrs& volumes_to_slice, FontProp::VerticalAlign align, double line_height, double offset, unsigned count_lines);
/// <param name="style_manager">Contain Font file, size and align</param>
/// <param name="count_lines">Count lines of embossed text(for veritcal alignment)</param>
void init(const Transform3d &text_tr, const ModelVolumePtrs &volumes_to_slice, /*const*/ Emboss::StyleManager &style_manager, unsigned count_lines);
void render(const Transform3d &text_world);

View File

@ -449,12 +449,10 @@ 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, double scale)
{
const auto &cn = prop.collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
const auto &font_info = file.infos[font_index];
const FontFile::Info& info = get_font_info(file, prop);
// coeficient for convert line height to font size
float c1 = (font_info.ascent - font_info.descent + font_info.linegap) /
(float) font_info.unit_per_em;
float c1 = (info.ascent - info.descent + info.linegap) /
(float) info.unit_per_em;
// The point size is defined as 1/72 of the Anglo-Saxon inch (25.4 mm):
// It is approximately 0.0139 inch or 352.8 um.
@ -490,17 +488,12 @@ ImFont *StyleManager::create_imgui_font(const std::string &text, double scale)
ImFontConfig font_config;
// TODO: start using merge mode
//font_config.MergeMode = true;
unsigned int font_index = font_prop.collection_number.value_or(0);
const auto &font_info = font_file.infos[font_index];
if (font_prop.char_gap.has_value()) {
float coef = font_size / (double) font_info.unit_per_em;
font_config.GlyphExtraSpacing.x = coef * (*font_prop.char_gap);
}
if (font_prop.line_gap.has_value()) {
float coef = font_size / (double) font_info.unit_per_em;
font_config.GlyphExtraSpacing.y = coef * (*font_prop.line_gap);
}
int unit_per_em = get_font_info(font_file, font_prop).unit_per_em;
float coef = font_size / (double) unit_per_em;
if (font_prop.char_gap.has_value())
font_config.GlyphExtraSpacing.x = coef * (*font_prop.char_gap);
if (font_prop.line_gap.has_value())
font_config.GlyphExtraSpacing.y = coef * (*font_prop.line_gap);
font_config.FontDataOwnedByAtlas = false;