diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 68a6f87e65..dd156da8ad 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -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& 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 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 { +/// +/// Align expolygons by type +/// +/// Type of alignement +/// 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); +} + std::vector Emboss::text2vshapes(FontFileWithCache &font_with_cache, const std::wstring& text, const FontProp &font_prop, const std::function& was_canceled){ assert(font_with_cache.has_value()); const FontFile &font = *font_with_cache.font_file; @@ -1307,8 +1305,8 @@ std::vector Emboss::text2vshapes(FontFileWithCache &font_with_cache, unsigned counter = 0; Point cursor(0, 0); - std::vector result; fontinfo_opt font_info_cache; + std::vector result; result.reserve(text.size()); for (wchar_t letter : text) { if (++counter == CANCEL_CHECK) { @@ -1318,6 +1316,8 @@ 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); 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 &shapes, BoundingBox *bb) +void align_shape(FontProp::Align type, std::vector &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 diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index fc0a1831d3..cb6aac8df9 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -441,15 +441,6 @@ namespace Emboss double calculate_angle(int32_t distance, PolygonPoint polygon_point, const Polygon &polygon); std::vector calculate_angles(int32_t distance, const PolygonPoints& polygon_points, const Polygon &polygon); - /// - /// Align expolygons by type - /// - /// Type of alignement - /// shapes to align - /// extents of shape - void align_shape(FontProp::Align type, ExPolygons& shape, BoundingBox* bb = nullptr); - void align_shape(FontProp::Align type, std::vector &shape, BoundingBox *bb = nullptr); - } // namespace Emboss } // namespace Slic3r #endif // slic3r_Emboss_hpp_ diff --git a/src/libslic3r/TextConfiguration.hpp b/src/libslic3r/TextConfiguration.hpp index bdc9371a3d..634284bf73 100644 --- a/src/libslic3r/TextConfiguration.hpp +++ b/src/libslic3r/TextConfiguration.hpp @@ -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 diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index f536c70909..13eee61f83 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -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(font_prop.align); if (ImGui::Combo("align", &selected_align, align_names, IM_ARRAYSIZE(align_names))) { font_prop.align = static_cast(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( std::make_unique(fp.emboss / scale), scale); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 36ceb68fe0..c7a4f3ef3e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -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; diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index ca2f04327b..4d14008893 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -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 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); diff --git a/src/slic3r/GUI/TextLines.cpp b/src/slic3r/GUI/TextLines.cpp index a61133055e..2cca79f4b3 100644 --- a/src/slic3r/GUI/TextLines.cpp +++ b/src/slic3r/GUI/TextLines.cpp @@ -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 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 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 line_centers(count_lines); diff --git a/src/slic3r/GUI/TextLines.hpp b/src/slic3r/GUI/TextLines.hpp index 741577c143..449c913f42 100644 --- a/src/slic3r/GUI/TextLines.hpp +++ b/src/slic3r/GUI/TextLines.hpp @@ -20,7 +20,8 @@ public: /// Must be selected text volume /// Height of text line with spacing [in mm] /// Offset of base line from center [in mm] - void init(const Selection &selection, double line_height); + /// [Optional] Count lines when not set it is calculated from vodel volume text + 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(); }