Partialy fix align:

whitechar in center
enters at begining
enters at end
Align
This commit is contained in:
Filip Sykala - NTB T15p 2023-05-19 14:11:15 +02:00
parent bf9bf2c626
commit 4be73de02f
8 changed files with 145 additions and 130 deletions

View File

@ -1271,32 +1271,30 @@ const int CANCEL_CHECK = 10;
ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache, const char *text, const FontProp &font_prop, const std::function<bool()>& was_canceled)
{
assert(font_with_cache.has_value());
const FontFile& font = *font_with_cache.font_file;
unsigned int font_index = font_prop.collection_number.value_or(0);
if (!priv::is_valid(font, font_index)) return {};
unsigned counter = 0;
Point cursor(0, 0);
std::wstring text_w = boost::nowide::widen(text);
std::vector<ExPolygons> vshapes = text2vshapes(font_with_cache, text_w, font_prop, was_canceled);
// unify to one expolygon
ExPolygons result;
fontinfo_opt font_info_cache;
std::wstring ws = boost::nowide::widen(text);
for (wchar_t wc: ws){
if (++counter == CANCEL_CHECK) {
counter = 0;
if (was_canceled())
return {};
}
ExPolygons expolygons = letter2shapes(wc, cursor, font_with_cache, font_prop, font_info_cache);
if (expolygons.empty())
for (ExPolygons &shapes : vshapes) {
if (shapes.empty())
continue;
expolygons_append(result, std::move(expolygons));
expolygons_append(result, std::move(shapes));
}
result = Slic3r::union_ex(result);
heal_shape(result);
return result;
}
namespace {
/// <summary>
/// Align expolygons by type
/// </summary>
/// <param name="type">Type of alignement</param>
/// <param name="shape">shapes to align</param>
/// <param name="text">Same size as shape for align per line(detect of end line - '\n')</param>
void align_shape(FontProp::Align type, std::vector<ExPolygons> &shape, const std::wstring &text);
}
std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache, const std::wstring& text, const FontProp &font_prop, const std::function<bool()>& was_canceled){
assert(font_with_cache.has_value());
const FontFile &font = *font_with_cache.font_file;
@ -1307,8 +1305,8 @@ std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache,
unsigned counter = 0;
Point cursor(0, 0);
std::vector<ExPolygons> result;
fontinfo_opt font_info_cache;
std::vector<ExPolygons> result;
result.reserve(text.size());
for (wchar_t letter : text) {
if (++counter == CANCEL_CHECK) {
@ -1318,6 +1316,8 @@ 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);
return result;
}
@ -1326,26 +1326,27 @@ unsigned Emboss::get_count_lines(const std::wstring& ws)
{
if (ws.empty())
return 0;
unsigned prev_count = 0;
for (wchar_t wc : ws)
if (wc == '\n')
++prev_count;
else
break;
unsigned post_count = 0;
for (wchar_t wc : boost::adaptors::reverse(ws))
if (wc == '\n')
++post_count;
else
break;
unsigned count = 1;
for (wchar_t wc : ws)
if (wc == '\n')
++count;
return count;
return count - prev_count - post_count;
// unsigned prev_count = 0;
// for (wchar_t wc : ws)
// if (wc == '\n')
// ++prev_count;
// else
// break;
//
// unsigned post_count = 0;
// for (wchar_t wc : boost::adaptors::reverse(ws))
// if (wc == '\n')
// ++post_count;
// else
// break;
//return count - prev_count - post_count;
}
unsigned Emboss::get_count_lines(const std::string &text)
@ -1875,11 +1876,12 @@ PolygonPoints Emboss::sample_slice(const TextLine &slice, const BoundingBoxes &b
// find BB in center of line
size_t first_right_index = 0;
for (const BoundingBox &bb : bbs)
if (bb.min.x() >= 0) {
break;
} else {
if (!bb.defined) // white char do not have bb
continue;
else if (bb.min.x() < 0)
++first_right_index;
}
else
break;
PolygonPoints samples(bbs.size());
int32_t shapes_x_cursor = 0;
@ -1925,71 +1927,76 @@ PolygonPoints Emboss::sample_slice(const TextLine &slice, const BoundingBoxes &b
}
namespace {
Point get_align_offset(FontProp::Align type, const BoundingBox &bb)
{
Point offset;
int32_t get_align_y_offset(FontProp::Align type, const BoundingBox &bb){
switch (type) {
// case Slic3r::FontProp::Align::start_first_line: break;
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: offset.y() = bb.center().y(); break;
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: offset.y() = bb.min.y(); break;
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: offset.y() = bb.max.y(); 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;
}
int32_t get_align_x_offset(FontProp::Align type, const BoundingBox &shape_bb, const BoundingBox &line_bb)
{
switch (type) {
// case Slic3r::FontProp::Align::start_first_line: break;
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: offset.x() = bb.center().x(); break;
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: offset.x() = bb.min.x(); break;
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: offset.x() = bb.max.x(); break;
case Slic3r::FontProp::Align::bottom_right: return -shape_bb.max.x() + (shape_bb.size().x() - line_bb.size().x());
default: break;
}
return -offset;
}
} // namespace
void Emboss::align_shape(FontProp::Align type, ExPolygons &shape, BoundingBox *bb)
{
if (type == FontProp::Align::start_first_line)
return; // no alignement
BoundingBox shape_bb_data;
BoundingBox &shape_bb = (bb != nullptr) ? *bb : shape_bb_data;
if (!shape_bb.defined)
shape_bb = get_extents(shape);
Point offset = get_align_offset(type, shape_bb);
for (ExPolygon &s : shape)
s.translate(offset);
return 0;
}
void Emboss::align_shape(FontProp::Align type, std::vector<ExPolygons> &shapes, BoundingBox *bb)
void align_shape(FontProp::Align type, std::vector<ExPolygons> &shapes, const std::wstring &text)
{
if (type == FontProp::Align::start_first_line)
if (type == FontProp::Align::first_line_left)
return; // no alignement
BoundingBox shape_bb_data;
BoundingBox &shape_bb = (bb != nullptr) ? *bb : shape_bb_data;
if (!shape_bb.defined)
for (const ExPolygons& shape: shapes)
shape_bb.merge(get_extents(shape));
BoundingBox shape_bb;
for (const ExPolygons& shape: shapes)
shape_bb.merge(get_extents(shape));
Point offset = get_align_offset(type, shape_bb);
for (ExPolygons &shape : shapes)
auto get_line_bb = [&](size_t j) {
BoundingBox line_bb;
for (; j < text.length() && text[j] != '\n'; ++j)
line_bb.merge(get_extents(shapes[j]));
return line_bb;
};
Point offset(
get_align_x_offset(type, shape_bb, get_line_bb(0)),
get_align_y_offset(type, shape_bb));
assert(shapes.size() == text.length());
for (size_t i = 0; i < shapes.size(); ++i) {
wchar_t letter = text[i];
if (letter == '\n'){
offset.x() = get_align_x_offset(type, shape_bb, get_line_bb(i+1));
continue;
}
ExPolygons &shape = shapes[i];
for (ExPolygon &s : shape)
s.translate(offset);
}
}
} // namespace
#ifdef REMOVE_SPIKES
#include <Geometry.hpp>

View File

@ -441,15 +441,6 @@ namespace Emboss
double calculate_angle(int32_t distance, PolygonPoint polygon_point, const Polygon &polygon);
std::vector<double> calculate_angles(int32_t distance, const PolygonPoints& polygon_points, const Polygon &polygon);
/// <summary>
/// Align expolygons by type
/// </summary>
/// <param name="type">Type of alignement</param>
/// <param name="shape">shapes to align</param>
/// <param name="bb">extents of shape</param>
void align_shape(FontProp::Align type, ExPolygons& shape, BoundingBox* bb = nullptr);
void align_shape(FontProp::Align type, std::vector<ExPolygons> &shape, BoundingBox *bb = nullptr);
} // namespace Emboss
} // namespace Slic3r
#endif // slic3r_Emboss_hpp_

View File

@ -64,7 +64,9 @@ struct FontProp
bool per_glyph;
enum class Align {
start_first_line, // it depends on position of zero for 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_left,
center_right,
center_center,
@ -77,7 +79,7 @@ struct FontProp
};
// change pivot of text
// When not set, center is used and is not stored
Align align = Align::center_center;
Align align = Align::first_line_center;
//////
// Duplicit data to wxFontDescriptor

View File

@ -447,7 +447,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
if (gl_volume == nullptr || !m_style_manager.is_active_font())
return res;
m_style_manager.get_style().prop.angle = calc_up(gl_volume->world_matrix(), priv::up_limit);
m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), priv::up_limit);
}
volume_transformation_changing();
@ -559,7 +559,7 @@ void GLGizmoEmboss::volume_transformation_changing()
}
const FontProp &prop = m_volume->text_configuration->style.prop;
if (prop.per_glyph)
init_text_lines();
init_text_lines(m_text_lines.get_lines().size());
}
void GLGizmoEmboss::volume_transformation_changed()
@ -571,7 +571,7 @@ void GLGizmoEmboss::volume_transformation_changed()
const FontProp &prop = m_volume->text_configuration->style.prop;
if (prop.per_glyph)
init_text_lines();
init_text_lines(m_text_lines.get_lines().size());
// Update surface by new position
if (prop.use_surface || prop.per_glyph)
@ -1047,7 +1047,7 @@ EmbossStyles GLGizmoEmboss::create_default_styles()
return styles;
}
void GLGizmoEmboss::init_text_lines(){
void GLGizmoEmboss::init_text_lines(unsigned count_lines){
assert(m_style_manager.is_active_font());
if (!m_style_manager.is_active_font())
return;
@ -1067,7 +1067,7 @@ void GLGizmoEmboss::init_text_lines(){
const FontFile &ff = *ff_ptr;
double line_height = TextLinesModel::calc_line_height(ff, fp);
m_text_lines.init(m_parent.get_selection(), line_height);
m_text_lines.init(m_parent.get_selection(), line_height, count_lines);
}
void GLGizmoEmboss::set_volume_by_selection()
@ -1306,6 +1306,9 @@ bool GLGizmoEmboss::process()
if (use_surface && is_object)
use_surface = false;
assert(!data.text_configuration.style.prop.per_glyph ||
get_count_lines(m_text) == m_text_lines.get_lines().size());
if (use_surface) {
// Model to cut surface from.
SurfaceVolumeData::ModelSources sources = create_volume_sources(m_volume);
@ -1540,6 +1543,12 @@ void GLGizmoEmboss::draw_text_input()
ImVec2 input_size(m_gui_cfg->text_size.x, m_gui_cfg->text_size.y + extra_height);
const ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_AutoSelectAll;
if (ImGui::InputTextMultiline("##Text", &m_text, input_size, flags)) {
if (m_style_manager.get_font_prop().per_glyph) {
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);
}
process();
range_text = create_range_text_prep();
}
@ -2801,7 +2810,7 @@ bool GLGizmoEmboss::rev_checkbox(const std::string &name,
}
bool GLGizmoEmboss::set_height() {
float &value = m_style_manager.get_style().prop.size_in_mm;
float &value = m_style_manager.get_font_prop().size_in_mm;
// size can't be zero or negative
priv::Limits::apply(value, priv::limits.size_in_mm);
@ -2829,7 +2838,7 @@ bool GLGizmoEmboss::set_height() {
void GLGizmoEmboss::draw_height(bool use_inch)
{
float &value = m_style_manager.get_style().prop.size_in_mm;
float &value = m_style_manager.get_font_prop().size_in_mm;
const EmbossStyle* stored_style = m_style_manager.get_stored_style();
const float *stored = (stored_style != nullptr)? &stored_style->prop.size_in_mm : nullptr;
const char *size_format = use_inch ? "%.2f in" : "%.1f mm";
@ -2842,7 +2851,7 @@ void GLGizmoEmboss::draw_height(bool use_inch)
bool GLGizmoEmboss::set_depth()
{
float &value = m_style_manager.get_style().prop.emboss;
float &value = m_style_manager.get_font_prop().emboss;
// size can't be zero or negative
priv::Limits::apply(value, priv::limits.emboss);
@ -2853,7 +2862,7 @@ bool GLGizmoEmboss::set_depth()
void GLGizmoEmboss::draw_depth(bool use_inch)
{
float &value = m_style_manager.get_style().prop.emboss;
float &value = m_style_manager.get_font_prop().emboss;
const EmbossStyle* stored_style = m_style_manager.get_stored_style();
const float *stored = ((stored_style)? &stored_style->prop.emboss : nullptr);
const std::string revert_emboss_depth = _u8L("Revert embossed depth.");
@ -2971,7 +2980,7 @@ void GLGizmoEmboss::draw_advanced()
return;
}
FontProp &font_prop = m_style_manager.get_style().prop;
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];
@ -3210,7 +3219,7 @@ void GLGizmoEmboss::draw_advanced()
if (ImGui::Button(_u8L("Set text to face camera").c_str())) {
assert(get_selected_volume(m_parent.get_selection()) == m_volume);
const Camera &cam = wxGetApp().plater()->get_camera();
bool use_surface = m_style_manager.get_style().prop.use_surface;
bool use_surface = m_style_manager.get_font_prop().use_surface;
if (priv::apply_camera_dir(cam, m_parent, m_keep_up) && use_surface)
process();
} else if (ImGui::IsItemHovered()) {
@ -3239,24 +3248,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();
init_text_lines(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[] = {
"start_first_line",
"center_left",
"center_right",
"center_center",
"top_left",
"top_right",
"top_center",
"bottom_left",
"bottom_right",
"bottom_center"
};
const char* align_names[] = {"first_line_left",
"first_line_right",
"first_line_center",
"center_left",
"center_right",
"center_center",
"top_left",
"top_right",
"top_center",
"bottom_left",
"bottom_right",
"bottom_center"};
int selected_align = static_cast<int>(font_prop.align);
if (ImGui::Combo("align", &selected_align, align_names, IM_ARRAYSIZE(align_names))) {
font_prop.align = static_cast<FontProp::Align>(selected_align);
@ -3359,7 +3368,7 @@ bool GLGizmoEmboss::choose_font_by_wxdialog()
const auto&ff = m_style_manager.get_font_file_with_cache();
if (WxFontUtils::is_italic(wx_font) &&
!Emboss::is_italic(*ff.font_file, font_collection)) {
m_style_manager.get_style().prop.skew = 0.2;
m_style_manager.get_font_prop().skew = 0.2;
}
return true;
}
@ -3428,7 +3437,7 @@ bool GLGizmoEmboss::choose_svg_file()
BoundingBox bb;
for (const auto &p : polys) bb.merge(p.contour.points);
const FontProp &fp = m_style_manager.get_style().prop;
const FontProp &fp = m_style_manager.get_font_prop();
float scale = fp.size_in_mm / std::max(bb.max.x(), bb.max.y());
auto project = std::make_unique<ProjectScale>(
std::make_unique<ProjectZ>(fp.emboss / scale), scale);

View File

@ -317,7 +317,7 @@ private:
// Keep information about curvature of text line around surface
TextLinesModel m_text_lines;
void init_text_lines();
void init_text_lines(unsigned count_lines = 0);
// Rotation gizmo
GLGizmoRotate m_rotate_gizmo;

View File

@ -362,10 +362,15 @@ bool priv::check(const DataBase &input, bool check_fontfile, bool use_surface)
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;
assert(input.text_configuration.style.prop.per_glyph == !input.text_lines.empty());
res &= input.text_configuration.style.prop.per_glyph == !input.text_lines.empty();
const FontProp& prop = input.text_configuration.style.prop;
assert(prop.use_surface == use_surface);
res &= prop.use_surface == use_surface;
assert(prop.per_glyph == !input.text_lines.empty());
res &= prop.per_glyph == !input.text_lines.empty();
if (prop.per_glyph) {
assert(get_count_lines(input.text_configuration.text) == input.text_lines.size());
res &= get_count_lines(input.text_configuration.text) == input.text_lines.size();
}
return res;
}
bool priv::check(const DataCreateVolume &input, bool is_main_thread) {
@ -432,7 +437,6 @@ ExPolygons priv::create_shape(DataBase &input, Fnc was_canceled) {
if (shapes.empty())
return {};
align_shape(input.text_configuration.style.prop.align, shapes);
return shapes;
}
@ -452,7 +456,6 @@ std::vector<ExPolygons> priv::create_shapes(DataBase &input, Fnc was_canceled) {
if (shapes.empty())
return {};
align_shape(prop.align, shapes);
if (was_canceled())
return {};
@ -591,8 +594,8 @@ TriangleMesh priv::try_create_mesh(DataBase &input, Fnc was_canceled)
{
if (!input.text_lines.empty()) {
TriangleMesh tm = create_mesh_per_glyph(input, was_canceled);
if (!tm.empty())
return tm;
if (was_canceled()) return {};
if (!tm.empty()) return tm;
}
ExPolygons shapes = priv::create_shape(input, was_canceled);

View File

@ -251,7 +251,7 @@ GLModel::Geometry create_geometry(const TextLines &lines)
}
} // namespace
void TextLinesModel::init(const Selection &selection, double line_height)
void TextLinesModel::init(const Selection &selection, double line_height, unsigned count_lines)
{
const GLVolume *gl_volume_ptr = selection.get_first_volume();
if (gl_volume_ptr == nullptr)
@ -268,13 +268,15 @@ void TextLinesModel::init(const Selection &selection, double line_height)
return;
const ModelVolume &mv = *mv_ptr;
const std::optional<TextConfiguration> tc_opt = mv.text_configuration;
if (!tc_opt.has_value())
return;
unsigned count_lines = Emboss::get_count_lines(tc_opt->text);
if (count_lines == 0)
return;
// calculate count lines when not set
if (count_lines == 0) {
const std::optional<TextConfiguration> tc_opt = mv.text_configuration;
if (!tc_opt.has_value())
return;
count_lines = Emboss::get_count_lines(tc_opt->text);
if (count_lines == 0)
return;
}
double first_line_center = offset + (count_lines / 2) * line_height - ((count_lines % 2 == 0) ? line_height / 2. : 0.);
std::vector<float> line_centers(count_lines);

View File

@ -20,7 +20,8 @@ public:
/// <param name="selection">Must be selected text volume</param>
/// <param name="line_height">Height of text line with spacing [in mm]</param>
/// <param name="line_offset">Offset of base line from center [in mm]</param>
void init(const Selection &selection, double line_height);
/// <param name="count_lines">[Optional] Count lines when not set it is calculated from vodel volume text</param>
void init(const Selection &selection, double line_height, unsigned count_lines = 0);
void render(const Transform3d &text_world);
bool is_init() const { return m_model.is_initialized(); }