diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 8788d99f9a..4270f4d3cb 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -1930,6 +1930,18 @@ void align_shape(ExPolygonsWithIds &shapes, const std::wstring &text, const Font // Shapes have to match letters in text assert(shapes.size() == text.length()); + unsigned count_lines = get_count_lines(text); + 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; + } + BoundingBox shape_bb; for (const ExPolygonsWithId& shape: shapes) shape_bb.merge(get_extents(shape.expoly)); @@ -1940,21 +1952,6 @@ void align_shape(ExPolygonsWithIds &shapes, const std::wstring &text, const Font line_bb.merge(get_extents(shapes[j].expoly)); 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 (ExPolygonsWithId& shape : shapes) - for (ExPolygon &s : shape.expoly) - s.translate(Point(0, y_offset)); - return; - } // Align x line by line Point offset( diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 35dde4ef10..f4d3e03a49 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2687,16 +2687,6 @@ void GLGizmoEmboss::draw_advanced() } else if (!per_glyph && m_text_lines.is_init()) m_text_lines.reset(); m_imgui->disabled_end(); // !can_use_per_glyph - - m_imgui->disabled_begin(!per_glyph); - ImGui::SameLine(); - ImGui::SetNextItemWidth(m_gui_cfg->input_width); - if (m_imgui->slider_float("##base_line_y_offset", &m_text_lines.offset, -10.f, 10.f, "%f mm")) { - reinit_text_lines(m_text_lines.get_lines().size()); - process(); - } else if (ImGui::IsItemHovered()) - ImGui::SetTooltip("TEST PURPOSE ONLY\nMove base line (up/down) for allign letters"); - m_imgui->disabled_end(); // !per_glyph auto draw_align = [&align = font_prop.align, input_offset = m_gui_cfg->advanced_input_offset, &icons = m_icons]() { bool is_change = false; @@ -2761,6 +2751,8 @@ void GLGizmoEmboss::draw_advanced() } // input gap between lines + bool is_multiline = m_text_lines.get_lines().size() > 1; + m_imgui->disabled_begin(!is_multiline); auto def_line_gap = stored_style ? &stored_style->prop.line_gap : nullptr; int min_line_gap = -half_ascent; @@ -2778,6 +2770,7 @@ void GLGizmoEmboss::draw_advanced() exist_change = true; } } + m_imgui->disabled_end(); // !is_multiline // input boldness auto def_boldness = stored_style ? diff --git a/src/slic3r/GUI/TextLines.cpp b/src/slic3r/GUI/TextLines.cpp index 88c5de5e4f..bf4511afd5 100644 --- a/src/slic3r/GUI/TextLines.cpp +++ b/src/slic3r/GUI/TextLines.cpp @@ -45,50 +45,8 @@ const Slic3r::Polygon *largest(const Slic3r::Polygons &polygons) return result; } -indexed_triangle_set its_create_belt(const Slic3r::Polygon &polygon, float width_half) { - // Improve: Create torus instead of flat belt path (with model overlaps) - assert(!polygon.empty()); - if (polygon.empty()) - return {}; - - // add a small positive offset to avoid z-fighting - float offset = static_cast(scale_(0.015f)); - Polygons polygons_expanded = expand(polygon, offset); - const Slic3r::Polygon *polygon_expanded_ptr = largest(polygons_expanded); - assert(polygon_expanded_ptr != nullptr); - if (polygon_expanded_ptr == nullptr || polygon_expanded_ptr->empty()) - return {}; - const Slic3r::Polygon &polygon_expanded = *polygon_expanded_ptr; - - // inspired by 3DScene.cpp void GLVolume::SinkingContours::update() - indexed_triangle_set model; - size_t count = polygon_expanded.size(); - model.vertices.reserve(2 * count); - model.indices.reserve(2 * count); - - for (const Point &point : polygon_expanded.points) { - Vec2f point_d = unscale(point).cast(); - Vec3f vertex(point_d.x(), point_d.y(), width_half); - model.vertices.push_back(vertex); - vertex.z() *= -1; - model.vertices.push_back(vertex); - } - - unsigned int prev_i = count - 1; - for (unsigned int i = 0; i < count; ++i) { - // t .. top - // b .. bottom - unsigned int t1 = prev_i * 2; - unsigned int b1 = t1 + 1; - unsigned int t2 = i * 2; - unsigned int b2 = t2 + 1; - model.indices.emplace_back(t1, b1, t2); - model.indices.emplace_back(b2, t2, b1); - prev_i = i; - } - return model; -} - +// Be careful it is not water tide and contain self intersections +// It is only for visualization purposes indexed_triangle_set its_create_torus(const Slic3r::Polygon &polygon, float radius, size_t steps = 20) { assert(!polygon.empty()); @@ -117,12 +75,7 @@ indexed_triangle_set its_create_torus(const Slic3r::Polygon &polygon, float radi Vec2f dir = prev + next; return Vec2f(-dir.x(), dir.y()); }; - std::vector points_norm(points_d.size()); - points_norm.front() = calc_norm(line_norm.back(), line_norm[1]); - for (size_t i = 1; i < points_d.size() - 1; ++i) - points_norm[i] = calc_norm(line_norm[i - 1], line_norm[i + 1]); - points_norm.back() = calc_norm(line_norm[points_d.size() - 2], line_norm.front()); - + // precalculate sinus and cosinus double angle_step = 2 * M_PI / steps; std::vector> sin_cos; @@ -135,38 +88,73 @@ indexed_triangle_set its_create_torus(const Slic3r::Polygon &polygon, float radi ); } + indexed_triangle_set sphere = its_make_sphere(radius, 2 * PI / steps); + // create torus model along polygon path indexed_triangle_set model; - model.vertices.reserve(steps * count); - model.indices.reserve(2 * steps * count); + model.vertices.reserve(2 * steps * count + sphere.vertices.size()*count); + model.indices.reserve(2 * steps * count + sphere.indices.size()*count); + + const Vec2f *prev_prev_point_d = &points_d[count-2]; // one before back + const Vec2f *prev_point_d = &points_d.back(); + + auto calc_angle = [](const Vec2f &d0, const Vec2f &d1) { + double dot = d0.dot(d1); + double det = d0.x() * d1.y() - d0.y() * d1.x(); // Determinant + return std::atan2(det, dot); // atan2(y, x) or atan2(sin, cos) + }; + + // opposit previos direction of line - for calculate angle + Vec2f opposit_prev_dir = (*prev_prev_point_d) - (*prev_point_d); for (size_t i = 0; i < count; ++i) { - const Vec2f point_d = points_d[i]; - const Vec2f norm = points_norm[i]; + + const Vec2f & point_d = points_d[i]; + // line segment direction + Vec2f dir = point_d - (*prev_point_d); + + double angle = calc_angle(opposit_prev_dir, dir); + double allowed_preccission = 1e-6; + if (angle >= (PI - allowed_preccission) || + angle <= (-PI + allowed_preccission)) + continue; // it is almost line + + // perpendicular direction to line + Vec2d p_dir(dir.y(), -dir.x()); + p_dir.normalize(); // Should done with double preccission + // p_dir is tube unit side vector + // tube unit top vector is z direction + + // Tube + int prev_index = model.vertices.size() + 2 * sin_cos.size() - 2; for (const auto &[s, c] : sin_cos) { - Vec2f xy = s * norm + point_d; - model.vertices.emplace_back(xy.x(), xy.y(), c); + Vec2f side = (s * p_dir).cast(); + Vec2f xy0 = side + (*prev_point_d); + Vec2f xy1 = side + point_d; + model.vertices.emplace_back(xy0.x(), xy0.y(), c); // pointing of prev index + model.vertices.emplace_back(xy1.x(), xy1.y(), c); + + // create triangle indices + int f0 = prev_index; + int s0 = f0 + 1; + int f1 = model.vertices.size() - 2; + int s1 = f1 + 1; + prev_index = f1; + model.indices.emplace_back(s0, f0, s1); + model.indices.emplace_back(f1, s1, f0); } + + prev_prev_point_d = prev_point_d; + prev_point_d = &point_d; + opposit_prev_dir = -dir; } - unsigned int prev_i = count - 1; - for (unsigned int i = 0; i < count; ++i) { - // TODO: solve <180, =180 and >180 angle - // to not create self intersection - - // t .. top - // b .. bottom - unsigned int prev_t = (prev_i+1) * steps - 1; - unsigned int t = (i+1) * steps - 1; - for (size_t s = 0; s < steps; ++s) { - unsigned int prev_b = prev_i * steps + s; - unsigned int b = i * steps + s; - model.indices.emplace_back(prev_t, prev_b, t); - model.indices.emplace_back(b, t, prev_b); - prev_t = prev_b; - t = b; - } - prev_i = i; + // sphere on each point + for (Vec2f& p: points_d){ + indexed_triangle_set sphere_copy = sphere; + its_translate(sphere_copy, Vec3f(p.x(), p.y(), 0.f)); + its_merge(model, sphere_copy); } + return model; } @@ -194,7 +182,8 @@ TextLines select_closest_contour(const std::vector &line_contours) { size_t line_idx; Vec2d hit_point; - double distance = AABBTreeLines::squared_distance_to_indexed_lines(linesf, tree, zero, line_idx, hit_point); + // double distance = + AABBTreeLines::squared_distance_to_indexed_lines(linesf, tree, zero, line_idx, hit_point); // conversion between index of point and expolygon ExPolygonsIndices cvt(expolygons); @@ -213,16 +202,14 @@ TextLines select_closest_contour(const std::vector &line_contours) { inline Eigen::AngleAxis get_rotation() { return Eigen::AngleAxis(-M_PI_2, Vec3d::UnitX()); } -indexed_triangle_set create_its(const TextLines &lines) -{ - const float model_half_width = 0.75; // [in volume mm] +indexed_triangle_set create_its(const TextLines &lines, float radius) +{ indexed_triangle_set its; // create model from polygons for (const TextLine &line : lines) { const Slic3r::Polygon &polygon = line.polygon; if (polygon.empty()) continue; - indexed_triangle_set line_its = its_create_belt(polygon, model_half_width); - //indexed_triangle_set line_its = its_create_torus(polygon, model_half_width); + indexed_triangle_set line_its = its_create_torus(polygon, radius); auto transl = Eigen::Translation3d(0., line.y, 0.); Transform3d tr = transl * get_rotation(); its_transform(line_its, tr); @@ -231,9 +218,9 @@ indexed_triangle_set create_its(const TextLines &lines) return its; } -GLModel::Geometry create_geometry(const TextLines &lines) +GLModel::Geometry create_geometry(const TextLines &lines, float radius) { - indexed_triangle_set its = create_its(lines); + indexed_triangle_set its = create_its(lines, radius); GLModel::Geometry geometry; geometry.format = {GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3}; @@ -278,9 +265,11 @@ void TextLinesModel::init(const Transform3d &text_tr, return; m_model.reset(); - m_lines.clear(); + m_lines.clear(); - double first_line_center = this->offset + line_height_mm / 3 + get_align_y_offset_in_mm(align, count_lines, ff, fp); + // size_in_mm .. contain volume scale and should be ascent value in mm + double line_offset = fp.size_in_mm * ascent_ratio_offset; + double first_line_center = line_offset + get_align_y_offset_in_mm(align, count_lines, ff, fp); std::vector line_centers(count_lines); for (size_t i = 0; i < count_lines; ++i) line_centers[i] = static_cast(first_line_center - i * line_height_mm); @@ -326,8 +315,9 @@ void TextLinesModel::init(const Transform3d &text_tr, for (size_t i = 0; i < count_lines; ++i) m_lines[i].y = line_centers[i]; + float radius = static_cast(line_height_mm / 20.); //* - GLModel::Geometry geometry = create_geometry(m_lines); + GLModel::Geometry geometry = create_geometry(m_lines, radius); if (geometry.vertices_count() == 0 || geometry.indices_count() == 0) return; m_model.init_from(std::move(geometry)); diff --git a/src/slic3r/GUI/TextLines.hpp b/src/slic3r/GUI/TextLines.hpp index a5a1df5bc1..f6e297cc22 100644 --- a/src/slic3r/GUI/TextLines.hpp +++ b/src/slic3r/GUI/TextLines.hpp @@ -17,9 +17,6 @@ namespace Slic3r::GUI { class TextLinesModel { public: - // line offset in y direction (up/down) - float offset = 0; - /// /// Initialize model and lines /// @@ -41,6 +38,11 @@ private: // Keep model for visualization text lines GLModel m_model; + + // Used to move slice (text line) on place where is approx vertical center of text + // When copy value const double ASCENT_CENTER from Emboss.cpp and Vertical align is center than + // text line will cross object center + const double ascent_ratio_offset = 1/3.; }; } // namespace Slic3r::GUI