diff --git a/src/libslic3r/CutSurface.cpp b/src/libslic3r/CutSurface.cpp index 6c00ed33d..fa61f6ce3 100644 --- a/src/libslic3r/CutSurface.cpp +++ b/src/libslic3r/CutSurface.cpp @@ -152,7 +152,7 @@ struct IntersectingElement /// CGAL mesh - half edge mesh CutMesh to_cgal(const indexed_triangle_set &its); -using Project = Emboss::IProject; +using Project = Emboss::IProjection; /// /// Covert 2d shape (e.g. Glyph) to CGAL model /// @@ -434,9 +434,9 @@ void store(const SurfaceCuts &cut, const std::string &dir); } // namespace privat -SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model, +SurfaceCut Slic3r::cut_surface(const indexed_triangle_set &model, const ExPolygons &shapes, - const Emboss::IProject &projection) + const Emboss::IProjection &projection) { priv::CutMesh cgal_model = priv::to_cgal(model); @@ -519,12 +519,11 @@ SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model, priv::store(result, DEBUG_OUTPUT_DIR + "cuts/"); // only debug #endif // DEBUG_OUTPUT_DIR - // TODO: Filter surfaceCuts to only the top most. - return result; + return merge(std::move(result)); } -indexed_triangle_set Slic3r::cuts2model(const SurfaceCuts &cuts, - const Emboss::IProject &projection) +indexed_triangle_set Slic3r::cuts2model(const SurfaceCuts &cuts, + const Emboss::IProject3f &projection) { indexed_triangle_set result; size_t count_vertices = 0; @@ -604,8 +603,8 @@ indexed_triangle_set Slic3r::cuts2model(const SurfaceCuts &cuts, return result; } -indexed_triangle_set Slic3r::cut2model(const SurfaceCut &cut, - const Emboss::IProject &projection) +indexed_triangle_set Slic3r::cut2model(const SurfaceCut &cut, + const Emboss::IProject3f &projection) { assert(!cut.empty()); size_t count_vertices = cut.vertices.size() * 2; @@ -707,7 +706,7 @@ priv::CutMesh priv::to_cgal(const ExPolygons &shapes, indices.reserve(polygon.points.size() * 2); size_t num_vertices_old = result.number_of_vertices(); for (const Point &p2 : polygon.points) { - auto p = projection.project(p2); + auto p = projection.create_front_back(p2); CutMesh::Point v_first{p.first.x(), p.first.y(), p.first.z()}; CutMesh::Point v_second{p.second.x(), p.second.y(), p.second.z()}; @@ -1388,7 +1387,7 @@ void priv::filter_cuts(CutAOIs &cuts, // compare distances of vertices Point p = get_point(*i); - Vec3f source_point = projection.project(p).first; + Vec3f source_point = projection.create_front_back(p).first; const auto &prev = mesh.point(ci.vi); Vec3f prev_point(prev.x(), prev.y(), prev.z()); float prev_sq_norm = (source_point - prev_point).squaredNorm(); diff --git a/src/libslic3r/CutSurface.hpp b/src/libslic3r/CutSurface.hpp index 0b65bccd2..a76d16b23 100644 --- a/src/libslic3r/CutSurface.hpp +++ b/src/libslic3r/CutSurface.hpp @@ -57,9 +57,9 @@ SurfaceCut merge(SurfaceCuts&& cuts); /// Multiple shape to cut from model /// Define transformation from 2d coordinate of shape to 3d /// Cutted surface from model -SurfaceCuts cut_surface(const indexed_triangle_set &model, - const ExPolygons &shapes, - const Emboss::IProject &projection); +SurfaceCut cut_surface(const indexed_triangle_set &model, + const ExPolygons &shapes, + const Emboss::IProjection &projection); /// /// Create model from surface cuts by projection @@ -67,16 +67,16 @@ SurfaceCuts cut_surface(const indexed_triangle_set &model, /// Surfaces from model /// Way of emboss /// Mesh -indexed_triangle_set cuts2model(const SurfaceCuts &cuts, - const Emboss::IProject &projection); +indexed_triangle_set cuts2model(const SurfaceCuts &cuts, + const Emboss::IProject3f &projection); /// /// Create model from surface cuts by projection /// /// Surface from model with outlines /// Way of emboss /// Mesh -indexed_triangle_set cut2model(const SurfaceCut &cut, - const Emboss::IProject &projection); +indexed_triangle_set cut2model(const SurfaceCut &cut, + const Emboss::IProject3f &projection); } // namespace Slic3r diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 07be4e026..b9741cfe2 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -769,7 +769,7 @@ std::string Emboss::create_range_text(const std::string &text, indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d, - const IProject &projection) + const IProjection &projection) { indexed_triangle_set result; size_t count_point = count_points(shape2d); @@ -782,7 +782,7 @@ indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d, auto insert_point = [&projection, &front_points, &back_points](const Polygon& polygon) { for (const Point& p : polygon.points) { - auto p2 = projection.project(p); + auto p2 = projection.create_front_back(p); front_points.emplace_back(p2.first); back_points.emplace_back(p2.second); } @@ -832,7 +832,7 @@ indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d, return result; } -std::pair Emboss::ProjectZ::project(const Point &p) const +std::pair Emboss::ProjectZ::create_front_back(const Point &p) const { Vec3f front( static_cast(p.x() * SHAPE_SCALE), @@ -902,7 +902,7 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position, // OrthoProject -std::pair Emboss::OrthoProject::project(const Point &p) const { +std::pair Emboss::OrthoProject::create_front_back(const Point &p) const { Vec3d front(p.x(), p.y(), 0.); Vec3f front_tr = (m_matrix * front).cast(); return std::make_pair(front_tr, project(front_tr)); diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index 143890108..1467eaef9 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -183,23 +183,12 @@ public: static std::string create_range_text(const std::string &text, const FontFile &font, unsigned int font_index, bool* exist_unknown = nullptr); /// - /// Project 2d point into space - /// Could be plane, sphere, cylindric, ... + /// Project spatial point /// - class IProject + class IProject3f { public: - virtual ~IProject() = default; - /// - /// convert 2d point to 3d point - /// - /// 2d coordinate - /// - /// first - front spatial point - /// second - back spatial point - /// - virtual std::pair project(const Point &p) const = 0; - + virtual ~IProject3f() = default; /// /// Move point with respect to projection direction /// e.g. Orthogonal projection will move with point by direction @@ -210,13 +199,33 @@ public: virtual Vec3f project(const Vec3f &point) const = 0; }; + /// + /// Project 2d point into space + /// Could be plane, sphere, cylindric, ... + /// + class IProjection : public IProject3f + { + public: + virtual ~IProjection() = default; + + /// + /// convert 2d point to 3d points + /// + /// 2d coordinate + /// + /// first - front spatial point + /// second - back spatial point + /// + virtual std::pair create_front_back(const Point &p) const = 0; + }; + /// /// Create triangle model for text /// /// text or image /// Define transformation from 2d to 3d(orientation, position, scale, ...) /// Projected shape into space - static indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProject& projection); + static indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection); /// /// Create transformation for emboss text object to lay on surface point @@ -228,29 +237,29 @@ public: static Transform3d create_transformation_onto_surface( const Vec3f &position, const Vec3f &normal, float up_limit = 0.9f); - class ProjectZ : public IProject + class ProjectZ : public IProjection { public: ProjectZ(float depth) : m_depth(depth) {} // Inherited via IProject - std::pair project(const Point &p) const override; + std::pair create_front_back(const Point &p) const override; Vec3f project(const Vec3f &point) const override; float m_depth; }; - class ProjectScale : public IProject + class ProjectScale : public IProjection { - std::unique_ptr core; + std::unique_ptr core; float m_scale; public: - ProjectScale(std::unique_ptr core, float scale) + ProjectScale(std::unique_ptr core, float scale) : core(std::move(core)), m_scale(scale) {} // Inherited via IProject - std::pair project(const Point &p) const override + std::pair create_front_back(const Point &p) const override { - auto res = core->project(p); + auto res = core->create_front_back(p); return std::make_pair(res.first * m_scale, res.second * m_scale); } Vec3f project(const Vec3f &point) const override{ @@ -258,7 +267,16 @@ public: } }; - class OrthoProject: public Emboss::IProject { + class OrthoProject3f : public Emboss::IProject3f + { + // size and direction of emboss for ortho projection + Vec3f m_direction; + public: + OrthoProject3f(Vec3f direction) : m_direction(direction) {} + Vec3f project(const Vec3f &point) const override{ return point + m_direction;} + }; + + class OrthoProject: public Emboss::IProjection { Transform3d m_matrix; // size and direction of emboss for ortho projection Vec3f m_direction; @@ -267,7 +285,7 @@ public: : m_matrix(matrix), m_direction(direction) {} // Inherited via IProject - std::pair project(const Point &p) const override; + std::pair create_front_back(const Point &p) const override; Vec3f project(const Vec3f &point) const override; }; }; diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index f8c0b8d97..b6bf9252a 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -157,6 +157,7 @@ static constexpr const char *CHAR_GAP_ATTR = "char_gap"; static constexpr const char *LINE_GAP_ATTR = "line_gap"; static constexpr const char *LINE_HEIGHT_ATTR = "line_height"; static constexpr const char *DEPTH_ATTR = "depth"; +static constexpr const char *USE_SURFACE_ATTR = "use_surface"; static constexpr const char *BOLDNESS_ATTR = "boldness"; static constexpr const char *SKEW_ATTR = "skew"; static constexpr const char *DISTANCE_ATTR = "distance"; @@ -3335,6 +3336,8 @@ void TextConfigurationSerialization::to_xml(std::stringstream &stream, const Tex stream << LINE_HEIGHT_ATTR << "=\"" << fp.size_in_mm << "\" "; stream << DEPTH_ATTR << "=\"" << fp.emboss << "\" "; + if (fp.use_surface) + stream << USE_SURFACE_ATTR << "=\"" << 1 << "\" "; if (fp.boldness.has_value()) stream << BOLDNESS_ATTR << "=\"" << *fp.boldness << "\" "; if (fp.skew.has_value()) @@ -3416,7 +3419,9 @@ std::optional TextConfigurationSerialization::read(const char fp.skew = skew; float distance = get_attribute_value_float(attributes, num_attributes, DISTANCE_ATTR); if (std::fabs(distance) > std::numeric_limits::epsilon()) - fp.distance = distance; + fp.distance = distance; + std::string use_surface = get_attribute_value_string(attributes, num_attributes, USE_SURFACE_ATTR); + if (!use_surface.empty()) fp.use_surface = true; float angle = get_attribute_value_float(attributes, num_attributes, ANGLE_ATTR); if (std::fabs(angle) > std::numeric_limits::epsilon()) fp.angle = angle; diff --git a/src/libslic3r/TextConfiguration.hpp b/src/libslic3r/TextConfiguration.hpp index 9e44f7a0d..53b76acd1 100644 --- a/src/libslic3r/TextConfiguration.hpp +++ b/src/libslic3r/TextConfiguration.hpp @@ -25,6 +25,12 @@ struct FontProp // Z depth of text float emboss; // [in mm] + // Flag that text should use surface cutted from object + // FontProp::distance should without value + // FontProp::emboss should be positive number + // Note: default value is false + bool use_surface; + // positive value mean wider character shape // negative value mean tiner character shape // When not set value is zero and is not stored @@ -85,13 +91,14 @@ struct FontProp /// Y size of text [in mm] /// Z size of text [in mm] FontProp(float line_height = 10.f, float depth = 2.f) - : emboss(depth), size_in_mm(line_height) + : emboss(depth), size_in_mm(line_height), use_surface(false) {} bool operator==(const FontProp& other) const { return char_gap == other.char_gap && line_gap == other.line_gap && + use_surface == other.use_surface && is_approx(emboss, other.emboss) && is_approx(size_in_mm, other.size_in_mm) && is_approx(boldness, other.boldness) && diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 95bdc15f1..7149b9256 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -603,13 +603,15 @@ void GLGizmoEmboss::initialize() int count_letter_M_in_input = 12; cfg.advanced_input_width = letter_m_size.x * count_letter_M_in_input; GuiCfg::Translations &tr = cfg.translations; - tr.font = _u8L("Font"); - tr.size = _u8L("Height"); - tr.depth = _u8L("Depth"); + tr.font = _u8L("Font"); + tr.size = _u8L("Height"); + tr.depth = _u8L("Depth"); + tr.use_surface = _u8L("Use surface"); float max_edit_text_width = std::max( {ImGui::CalcTextSize(tr.font.c_str()).x, ImGui::CalcTextSize(tr.size.c_str()).x, - ImGui::CalcTextSize(tr.depth.c_str()).x }); + ImGui::CalcTextSize(tr.depth.c_str()).x, + ImGui::CalcTextSize(tr.use_surface.c_str()).x }); cfg.edit_input_offset = 3 * space + ImGui::GetTreeNodeToLabelSpacing() + max_edit_text_width; @@ -646,7 +648,7 @@ void GLGizmoEmboss::initialize() float window_width = cfg.style_combobox_width + style.WindowPadding.x * 2; cfg.minimal_window_size = ImVec2(window_width, window_height); - float addition_edit_height = input_height * 3 + tree_header; + float addition_edit_height = input_height * 4 + tree_header; cfg.minimal_window_size_with_edit = ImVec2(cfg.minimal_window_size.x, cfg.minimal_window_size.y + addition_edit_height); @@ -869,11 +871,40 @@ bool GLGizmoEmboss::process() m_update_job_cancel = std::make_shared >(false); EmbossDataBase base{font, create_configuration(), create_volume_name()}; EmbossDataUpdate data{std::move(base), m_volume->id(), m_update_job_cancel}; + + std::unique_ptr job = nullptr; + + // check cutting from source mesh + const TextConfiguration &tc = data.text_configuration; + if (tc.font_item.prop.use_surface) { + // Model to cut surface from. + const ModelVolume *mesh = get_volume_to_cut_surface_from(); + if (mesh == nullptr) return false; + + Transform3d text_tr = m_volume->get_matrix(); + if (tc.fix_3mf_tr.has_value()) + text_tr = text_tr * tc.fix_3mf_tr->inverse(); + + bool is_outside = m_volume->is_model_part(); + // check that there is not unexpected volume type + assert(is_outside || m_volume->is_negative_volume() || + m_volume->is_modifier()); + UseSurfaceData surface_data{std::move(data), + text_tr, + is_outside, + mesh->mesh().its /*copy*/, + mesh->get_matrix(), + mesh->mesh().bounding_box()}; + job = std::make_unique(std::move(surface_data)); + } else { + job = std::make_unique(std::move(data)); + } + //* auto &worker = wxGetApp().plater()->get_ui_job_worker(); - queue_job(worker, std::make_unique(std::move(data))); + queue_job(worker, std::move(job)); /*/ // Run Job on main thread (blocking) - ONLY DEBUG - execute_job(std::make_shared(std::move(data))); + execute_job(std::move(job)); // */ // notification is removed befor object is changed by job @@ -913,49 +944,18 @@ void GLGizmoEmboss::select_stored_font_item() m_stored_font_item = it->second; } -/// -/// choose valid source object for cut surface -/// -/// Source model volume -/// triangle set OR nullptr -const indexed_triangle_set *get_source_object(const ModelVolume* mv) { - if (mv == nullptr) return nullptr; - if (!mv->text_configuration.has_value()) return nullptr; - const auto &volumes = mv->get_object()->volumes; - // no other volume in object - if (volumes.size() <= 1) return nullptr; - - // Improve create object from part or use gl_volume - // Get first model part in object - for (const ModelVolume *v : volumes) { - if (v->id() == mv->id()) continue; - if (!v->is_model_part()) continue; - const TriangleMesh &tm = v->mesh(); - if (tm.empty()) continue; - return &tm.its; - } - - // No valid source volume in objct volumes - return nullptr; -} - -/// -/// Choose valid source Volume to project on(cut surface from). -/// -/// Volume with text -/// ModelVolume to project on -const ModelVolume *get_source_volume(const ModelVolume *text_volume) +const ModelVolume * GLGizmoEmboss::get_volume_to_cut_surface_from() { - if (text_volume == nullptr) return nullptr; - if (!text_volume->text_configuration.has_value()) return nullptr; - const auto &volumes = text_volume->get_object()->volumes; + if (m_volume == nullptr) return nullptr; + if (!m_volume->text_configuration.has_value()) return nullptr; + const auto &volumes = m_volume->get_object()->volumes; // no other volume in object if (volumes.size() <= 1) return nullptr; // Improve create object from part or use gl_volume // Get first model part in object for (const ModelVolume *v : volumes) { - if (v->id() == text_volume->id()) continue; + if (v->id() == m_volume->id()) continue; if (!v->is_model_part()) continue; const TriangleMesh &tm = v->mesh(); if (tm.empty()) continue; @@ -967,169 +967,36 @@ const ModelVolume *get_source_volume(const ModelVolume *text_volume) return nullptr; } -double get_shape_scale(const FontProp &fp, const Emboss::FontFile &ff) -{ - const auto &cn = fp.collection_number; - unsigned int font_index = (cn.has_value()) ? *cn : 0; - int unit_per_em = ff.infos[font_index].unit_per_em; - double scale = fp.size_in_mm / unit_per_em; - // Shape is scaled for store point coordinate as integer - return scale * Emboss::SHAPE_SCALE; -} - -/// -/// Create cut_projection for cut surface -/// -/// Volume transformation in object -/// Configuration of embossig -/// Font file for size --> unit per em -/// Bounding box 2d of shape to center result volume -/// Bounding box 3d of model volume for projection ranges -/// Orthogonal cut_projection -std::unique_ptr create_projection_for_cut( - Transform3d tr, - const TextConfiguration &tc, - const Emboss::FontFile &ff, - const BoundingBox &shape_bb, - const std::pair& z_range) -{ - // create sure that emboss object is bigger than source object - const float safe_extension = 1.0f; - float min_z = z_range.first - safe_extension; - float max_z = z_range.second + safe_extension; - assert(min_z < max_z); - // range between min and max value - double projection_size = max_z - min_z; - Matrix3d transformation_for_vector = tr.linear(); - // Projection must be negative value. - // System of text coordinate - // X .. from left to right - // Y .. from bottom to top - // Z .. from text to eye - Vec3d untransformed_direction(0., 0., projection_size); - Vec3f project_direction = - (transformation_for_vector * untransformed_direction).cast(); - - // Projection is in direction from far plane - tr.translate(Vec3d(0., 0., min_z)); - - tr.scale(get_shape_scale(tc.font_item.prop, ff)); - // Text alignemnt to center 2D - Vec2d move = -(shape_bb.max + shape_bb.min).cast() / 2.; - tr.translate(Vec3d(move.x(), move.y(), 0.)); - return std::make_unique(tr, project_direction); -} - -#include "libslic3r/CutSurface.hpp" -/// -/// Create tranformation for emboss -/// -/// True .. raise, False .. engrave -/// Text configuration -/// Text voliume transformation inside object -/// Cutted surface from model -/// Projection -static std::unique_ptr create_emboss_projection( - bool is_outside, - const TextConfiguration &tc, - Transform3d tr, - SurfaceCut &cut) -{ - // Offset of clossed side to model - const float surface_offset = 1e-3f; // [in mm] - - const FontProp &fp = tc.font_item.prop; - float front_move, back_move; - if (is_outside) { - front_move = fp.emboss; - back_move = -surface_offset; - } else { - front_move = surface_offset; - back_move = -fp.emboss; - } - Matrix3d rot_i = tr.linear().inverse(); - its_transform(cut, rot_i); - - BoundingBoxf3 bb = Slic3r::bounding_box(cut); - float z_move = -bb.max.z(); - float x_center = (bb.max.x() + bb.min.x()) / 2; - float y_center = (bb.max.y() + bb.min.y()) / 2; - // move to front distance - Vec3f move(x_center, y_center, front_move + z_move); - - its_translate(cut, move); - - Vec3f from_front_to_back(0.f, 0.f, back_move - front_move); - return std::make_unique(Transform3d::Identity() /*not used*/, - from_front_to_back); -} - void GLGizmoEmboss::use_surface() { - // Model to cut surface from. - const ModelVolume *source = get_source_volume(m_volume); - if (source == nullptr) return; - // font face with glyph cache auto ffc = m_font_manager.get_font().font_file_with_cache; if (!ffc.has_value()) return; + // Model to cut surface from. + const ModelVolume *mesh = get_volume_to_cut_surface_from(); + if (mesh == nullptr) return; + + // Cancel previous job + if (m_update_job_cancel != nullptr) m_update_job_cancel->store(true); + // create new shared ptr to cancel new job + m_update_job_cancel = std::make_shared>(false); + EmbossDataBase base{ffc, create_configuration(), create_volume_name()}; + EmbossDataUpdate data{std::move(base), m_volume->id(), m_update_job_cancel}; + + assert(m_volume->text_configuration.has_value()); + if (!m_volume->text_configuration.has_value()) return; const TextConfiguration &tc = *m_volume->text_configuration; - const char *text = tc.text.c_str(); - const FontProp& fp = tc.font_item.prop; - ExPolygons shapes = Emboss::text2shapes(ffc, text, fp); - - if (shapes.empty()) return; - if (shapes.front().contour.empty()) return; - - BoundingBox bb = get_extents(shapes); - Transform3d text_tr = m_volume->get_matrix(); if (tc.fix_3mf_tr.has_value()) - text_tr = text_tr * tc.fix_3mf_tr->inverse(); - - Transform3d source_tr = source->get_matrix(); - Transform3d source_tr_inv = source_tr.inverse(); - Transform3d cut_projection_tr = source_tr_inv * text_tr; - //Transform3d cut_projection_tr = source_tr * text_tr.inverse(); - - BoundingBoxf3 source_bb = source->mesh().bounding_box(); - BoundingBoxf3 source_bb_tr = source_bb.transformed(cut_projection_tr.inverse()); - std::pair z_range{source_bb_tr.min.z(), source_bb_tr.max.z()}; - - const Emboss::FontFile &ff = *ffc.font_file; - auto cut_projection = create_projection_for_cut(cut_projection_tr, tc, ff, bb, z_range); - if (cut_projection == nullptr) return; - - SurfaceCuts cuts = cut_surface(source->mesh().its, shapes, *cut_projection); - if (cuts.empty()) return; - + text_tr = text_tr * tc.fix_3mf_tr->inverse(); bool is_outside = m_volume->is_model_part(); + // check that there is not unexpected volume type assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier()); - - SurfaceCut cut = merge(std::move(cuts)); - // !! Projection needs to transform cut - auto projection = create_emboss_projection(is_outside, tc, cut_projection_tr, cut); - if (projection == nullptr) return; + UseSurfaceData surface_data{std::move(data), text_tr, is_outside, + mesh->mesh().its /*copy*/, mesh->get_matrix(), mesh->mesh().bounding_box()}; - indexed_triangle_set new_its = cut2model(cut, *projection); - its_write_obj(new_its, "C:/data/temp/projected.obj"); // only debug - - TriangleMesh tm(std::move(new_its)); - // center triangle mesh - Vec3d shift = tm.bounding_box().center(); - // do not center in emboss direction - shift.z() = 0; - tm.translate(-shift.cast()); - - // discard fix transformation when exists - if (tc.fix_3mf_tr.has_value()) { - m_volume->set_transformation(text_tr); - m_volume->text_configuration->fix_3mf_tr = {}; - } - - m_volume->set_mesh(std::move(tm)); - m_volume->set_new_unique_id(); - wxGetApp().plater()->canvas3D()->reload_scene(true); + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + queue_job(worker, std::make_unique(std::move(surface_data))); } void GLGizmoEmboss::draw_window() @@ -1907,6 +1774,25 @@ bool GLGizmoEmboss::rev_input(const std::string &name, return revertible(name, value, default_value, exist_change, undo_tooltip, undo_offset, draw_offseted_input); } +bool GLGizmoEmboss::rev_checkbox(const std::string &name, + bool &value, + bool *default_value, + const std::string &undo_tooltip) +{ + // draw offseted input + auto draw_offseted_input = [&]() -> bool { + float input_offset = m_gui_cfg->edit_input_offset; + ImGui::SameLine(input_offset); + return ImGui::Checkbox(("##" + name).c_str(), &value); + }; + float undo_offset = ImGui::GetStyle().FramePadding.x; + bool exist_change = default_value != nullptr ? + (value != *default_value) : + false; + return revertible(name, value, default_value, exist_change, undo_tooltip, + undo_offset, draw_offseted_input); +} + void GLGizmoEmboss::draw_style_edit() { const GuiCfg::Translations &tr = m_gui_cfg->translations; @@ -2006,6 +1892,18 @@ void GLGizmoEmboss::draw_style_edit() { _u8L("Revert embossed depth."), 0.1f, 0.25, "%.2f mm")) process(); + bool *def_use_surface = m_stored_font_item.has_value() ? + &m_stored_font_item->prop.use_surface : nullptr; + if (rev_checkbox(tr.use_surface, font_prop.use_surface, def_use_surface, + _u8L("Revert using of model surface."))) { + if (font_prop.use_surface) { + font_prop.distance.reset(); + if (font_prop.emboss < 0.1) + font_prop.emboss = 1; + } + process(); + } + if (ImGui::TreeNode(_u8L("advanced").c_str())) { if (!m_is_advanced_edit_style) { set_minimal_window_size(true, true); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 27ae78892..7637e0ffd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -115,6 +115,12 @@ private: // TODO: only for developing - remove it void use_surface(); + /// + /// Choose valid source Volume to project on(cut surface from). + /// + /// ModelVolume to project on + const ModelVolume *get_volume_to_cut_surface_from(); + /// /// Reversible input float with option to restor default value /// TODO: make more general, static and move to ImGuiWrapper @@ -123,6 +129,7 @@ private: bool rev_input(const std::string &name, float &value, float *default_value, const std::string &undo_tooltip, float step, float step_fast, const char *format, ImGuiInputTextFlags flags = 0); + bool rev_checkbox(const std::string &name, bool &value, bool* default_value, const std::string &undo_tooltip); bool rev_slider(const std::string &name, std::optional& value, std::optional *default_value, const std::string &undo_tooltip, int v_min, int v_max, const std::string &format, const wxString &tooltip); bool rev_slider(const std::string &name, std::optional& value, std::optional *default_value, @@ -185,6 +192,7 @@ private: std::string font; std::string size; std::string depth; + std::string use_surface; // advanced std::string char_gap; diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index a31932d5e..899fed7a4 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -2,6 +2,7 @@ #include #include // load_obj for default mesh +#include // use surface cuts #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/NotificationManager.hpp" @@ -40,105 +41,46 @@ static TriangleMesh create_mesh(const char *text, /// /// Not empty model(index trinagle set - its) static TriangleMesh create_default_mesh(); + +/// +/// Must be called on main thread +/// +/// New mesh data +/// Text configuration, ... +static void update_volume(TriangleMesh &&mesh, const EmbossDataUpdate &data); + +/// +/// Create projection for cut surface from mesh +/// +/// Volume transformation in object +/// Configuration of embossig +/// Font file for size --> unit per em +/// Bounding box 2d of shape to center result +/// volume Bounding box 3d of model volume for +/// projection ranges Orthogonal cut_projection +static std::unique_ptr create_projection_for_cut( + Transform3d tr, + const TextConfiguration &tc, + const Emboss::FontFile &ff, + const BoundingBox &shape_bb, + const std::pair &z_range); + +/// +/// Create tranformation for emboss Cutted surface +/// +/// True .. raise, False .. engrave +/// Text configuration +/// Text voliume transformation inside object +/// Cutted surface from model +/// Projection +static std::unique_ptr create_emboss_projection( + bool is_outside, + const TextConfiguration &tc, + Transform3d tr, + SurfaceCut &cut); + } - -///////////////// -/// Update Volume -EmbossUpdateJob::EmbossUpdateJob(EmbossDataUpdate&& input) - : m_input(std::move(input)) -{ - assert(m_input.cancel != nullptr); - assert(m_input.font_file.has_value()); - assert(!m_input.text_configuration.text.empty()); - assert(!m_input.text_configuration.fix_3mf_tr.has_value()); -} - -void EmbossUpdateJob::process(Ctl &ctl) -{ - auto was_canceled = [&ctl, &cancel = m_input.cancel]()->bool { - if (cancel->load()) return true; - return ctl.was_canceled(); - }; - - // check if exist valid font - if (!m_input.font_file.has_value()) return; - - const TextConfiguration &cfg = m_input.text_configuration; - m_result = priv::create_mesh(cfg.text.c_str(), m_input.font_file, - cfg.font_item.prop, was_canceled); - if (m_result.its.empty()) return; - if (was_canceled()) return; - - // center triangle mesh - Vec3d shift = m_result.bounding_box().center(); - m_result.translate(-shift.cast()); -} - -void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &) -{ - if (canceled || m_input.cancel->load()) return; - - // for sure that some object is created from shape - if (m_result.its.indices.empty()) return; - - GUI_App & app = wxGetApp(); // may be move to input - Plater * plater = app.plater(); - GLCanvas3D * canvas = plater->canvas3D(); - - // Check emboss gizmo is still open - GLGizmosManager &manager = canvas->get_gizmos_manager(); - if (manager.get_current_type() != GLGizmosManager::Emboss) return; - - std::string snap_name = GUI::format(_L("Text: %1%"), m_input.text_configuration.text); - Plater::TakeSnapshot snapshot(plater, snap_name, UndoRedo::SnapshotType::GizmoAction); - - ModelVolume *volume = nullptr; - Model &model = plater->model(); - for (auto obj : model.objects) - for (auto vol : obj->volumes) - if (vol->id() == m_input.volume_id) { - volume = vol; - break; - } - - // could appear when user delete edited volume - if (volume == nullptr) - return; - - // apply fix matrix made by store to .3mf - const auto &tc = volume->text_configuration; - assert(tc.has_value()); - if (tc.has_value() && tc->fix_3mf_tr.has_value()) - volume->set_transformation(volume->get_matrix() * tc->fix_3mf_tr->inverse()); - - // update volume - volume->set_mesh(std::move(m_result)); - volume->set_new_unique_id(); - volume->calculate_convex_hull(); - volume->get_object()->invalidate_bounding_box(); - volume->name = m_input.volume_name; - volume->text_configuration = m_input.text_configuration; - - // update volume in right panel( volume / object name) - const Selection &selection = canvas->get_selection(); - const GLVolume * gl_volume = selection.get_volume( - *selection.get_volume_idxs().begin()); - int object_idx = gl_volume->object_idx(); - int volume_idx = gl_volume->volume_idx(); - ObjectList *obj_list = app.obj_list(); - obj_list->update_name_in_list(object_idx, volume_idx); - - // update printable state on canvas - if (volume->type() == ModelVolumeType::MODEL_PART) - canvas->update_instance_printable_state_for_object((size_t) object_idx); - - // redraw scene - bool refresh_immediately = false; - canvas->reload_scene(refresh_immediately); -} - - ///////////////// /// Create Volume EmbossCreateVolumeJob::EmbossCreateVolumeJob(EmbossDataCreateVolume &&input) @@ -296,6 +238,104 @@ void EmbossCreateObjectJob::finalize(bool canceled, std::exception_ptr &) canvas->reload_scene(true); } +///////////////// +/// Update Volume +EmbossUpdateJob::EmbossUpdateJob(EmbossDataUpdate&& input) + : m_input(std::move(input)) +{ + assert(m_input.cancel != nullptr); + assert(m_input.font_file.has_value()); + assert(!m_input.text_configuration.text.empty()); + assert(!m_input.text_configuration.fix_3mf_tr.has_value()); +} + +void EmbossUpdateJob::process(Ctl &ctl) +{ + auto was_canceled = [&ctl, &cancel = m_input.cancel]()->bool { + if (cancel->load()) return true; + return ctl.was_canceled(); + }; + + // check if exist valid font + if (!m_input.font_file.has_value()) return; + + const TextConfiguration &cfg = m_input.text_configuration; + m_result = priv::create_mesh(cfg.text.c_str(), m_input.font_file, + cfg.font_item.prop, was_canceled); + if (m_result.its.empty()) return; + if (was_canceled()) return; + + // center triangle mesh + Vec3d shift = m_result.bounding_box().center(); + m_result.translate(-shift.cast()); +} + +void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &) +{ + if (canceled || m_input.cancel->load()) return; + priv::update_volume(std::move(m_result), m_input); +} + +///////////////// +/// Cut Surface +UseSurfaceJob::UseSurfaceJob(UseSurfaceData &&input) + : m_input(std::move(input)) +{} + +void UseSurfaceJob::process(Ctl &ctl) { + // font face with glyph cache + if (!m_input.font_file.has_value()) return; + + // check cancelation of process + auto was_canceled = [&ctl, &cancel = m_input.cancel]()->bool { + if (cancel->load()) return true; + return ctl.was_canceled(); + }; + + const TextConfiguration &tc = m_input.text_configuration; + const char *text = tc.text.c_str(); + const FontProp &fp = tc.font_item.prop; + ExPolygons shapes = Emboss::text2shapes(m_input.font_file, text, fp); + if (shapes.empty()) return; + if (shapes.front().contour.empty()) return; + if (was_canceled()) return; + + BoundingBox bb = get_extents(shapes); + + Transform3d mesh_tr_inv = m_input.mesh_tr.inverse(); + Transform3d cut_projection_tr = mesh_tr_inv * m_input.text_tr; + BoundingBoxf3 mesh_bb_tr = m_input.mesh_bb.transformed(cut_projection_tr.inverse()); + std::pair z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()}; + + const Emboss::FontFile &ff = *m_input.font_file.font_file; + auto cut_projection = priv::create_projection_for_cut(cut_projection_tr, tc, ff, bb, z_range); + if (cut_projection == nullptr) return; + + // Use CGAL to cut surface from triangle mesh + SurfaceCut cut = cut_surface(m_input.mesh_its, shapes, *cut_projection); + if (cut.empty()) return; + if (was_canceled()) return; + + // !! Projection needs to transform cut + auto projection = priv::create_emboss_projection(m_input.is_outside, tc, cut_projection_tr, cut); + if (projection == nullptr) return; + + indexed_triangle_set new_its = cut2model(cut, *projection); + if (was_canceled()) return; + //its_write_obj(new_its, "C:/data/temp/projected.obj"); // only debug + + m_result = TriangleMesh(std::move(new_its)); + Vec3d shift = -m_result.bounding_box().center(); + // do not center in emboss direction + shift.z() = 0; + m_result.translate(shift.cast()); +} + +void UseSurfaceJob::finalize(bool canceled, std::exception_ptr &) +{ + if (canceled || m_input.cancel->load()) return; + priv::update_volume(std::move(m_result), m_input); +} //////////////////////////// /// private namespace implementation @@ -334,4 +374,149 @@ TriangleMesh priv::create_default_mesh() return TriangleMesh(its_make_cube(36., 4., 2.5)); } return triangle_mesh; -} \ No newline at end of file +} + +static ModelVolume *get_volume(const ObjectID &volume_id, Model &model) +{ + for (auto obj : model.objects) + for (auto vol : obj->volumes) + if (vol->id() == volume_id) + return vol; + return nullptr; +} + +static ModelVolume *get_volume(const ObjectID &volume_id) { + return get_volume(volume_id, wxGetApp().plater()->model()); +} + +void priv::update_volume(TriangleMesh &&mesh, + const EmbossDataUpdate &data) +{ + // for sure that some object will be created + if (mesh.its.empty()) return; + + GUI_App & app = wxGetApp(); // may be move to input + Plater * plater = app.plater(); + GLCanvas3D * canvas = plater->canvas3D(); + + // Check emboss gizmo is still open + GLGizmosManager &manager = canvas->get_gizmos_manager(); + if (manager.get_current_type() != GLGizmosManager::Emboss) return; + + std::string snap_name = GUI::format(_L("Text: %1%"), data.text_configuration.text); + Plater::TakeSnapshot snapshot(plater, snap_name, UndoRedo::SnapshotType::GizmoAction); + + ModelVolume *volume = get_volume(data.volume_id, plater->model()); + // could appear when user delete edited volume + if (volume == nullptr) + return; + + // apply fix matrix made by store to .3mf + const auto &tc = volume->text_configuration; + assert(tc.has_value()); + if (tc.has_value() && tc->fix_3mf_tr.has_value()) + volume->set_transformation(volume->get_matrix() * tc->fix_3mf_tr->inverse()); + + // update volume + volume->set_mesh(std::move(mesh)); + volume->set_new_unique_id(); + volume->calculate_convex_hull(); + volume->get_object()->invalidate_bounding_box(); + volume->name = data.volume_name; + volume->text_configuration = data.text_configuration; + + // update volume in right panel( volume / object name) + const Selection &selection = canvas->get_selection(); + const GLVolume * gl_volume = selection.get_volume( + *selection.get_volume_idxs().begin()); + int object_idx = gl_volume->object_idx(); + int volume_idx = gl_volume->volume_idx(); + ObjectList *obj_list = app.obj_list(); + obj_list->update_name_in_list(object_idx, volume_idx); + + // update printable state on canvas + if (volume->type() == ModelVolumeType::MODEL_PART) + canvas->update_instance_printable_state_for_object((size_t) object_idx); + + // redraw scene + bool refresh_immediately = false; + canvas->reload_scene(refresh_immediately); +} + +static double get_shape_scale(const FontProp &fp, const Emboss::FontFile &ff) +{ + const auto &cn = fp.collection_number; + unsigned int font_index = (cn.has_value()) ? *cn : 0; + int unit_per_em = ff.infos[font_index].unit_per_em; + double scale = fp.size_in_mm / unit_per_em; + // Shape is scaled for store point coordinate as integer + return scale * Emboss::SHAPE_SCALE; +} + +std::unique_ptr priv::create_projection_for_cut( + Transform3d tr, + const TextConfiguration &tc, + const Emboss::FontFile &ff, + const BoundingBox &shape_bb, + const std::pair &z_range) +{ + // create sure that emboss object is bigger than source object + const float safe_extension = 1.0f; + float min_z = z_range.first - safe_extension; + float max_z = z_range.second + safe_extension; + assert(min_z < max_z); + // range between min and max value + double projection_size = max_z - min_z; + Matrix3d transformation_for_vector = tr.linear(); + // Projection must be negative value. + // System of text coordinate + // X .. from left to right + // Y .. from bottom to top + // Z .. from text to eye + Vec3d untransformed_direction(0., 0., projection_size); + Vec3f project_direction = + (transformation_for_vector * untransformed_direction).cast(); + + // Projection is in direction from far plane + tr.translate(Vec3d(0., 0., min_z)); + + tr.scale(get_shape_scale(tc.font_item.prop, ff)); + // Text alignemnt to center 2D + Vec2d move = -(shape_bb.max + shape_bb.min).cast() / 2.; + tr.translate(Vec3d(move.x(), move.y(), 0.)); + return std::make_unique(tr, project_direction); +} + +std::unique_ptr priv::create_emboss_projection( + bool is_outside, + const TextConfiguration &tc, + Transform3d tr, + SurfaceCut &cut) +{ + // Offset of clossed side to model + const float surface_offset = 1e-3f; // [in mm] + + const FontProp &fp = tc.font_item.prop; + float front_move, back_move; + if (is_outside) { + front_move = fp.emboss; + back_move = -surface_offset; + } else { + front_move = surface_offset; + back_move = -fp.emboss; + } + Matrix3d rot_i = tr.linear().inverse(); + its_transform(cut, rot_i); + + BoundingBoxf3 bb = Slic3r::bounding_box(cut); + float z_move = -bb.max.z(); + float x_center = (bb.max.x() + bb.min.x()) / 2; + float y_center = (bb.max.y() + bb.min.y()) / 2; + // move to front distance + Vec3f move(x_center, y_center, front_move + z_move); + + its_translate(cut, move); + + Vec3f from_front_to_back(0.f, 0.f, back_move - front_move); + return std::make_unique(from_front_to_back); +} diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index cc7cae31b..e0a7aee3d 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -30,19 +30,6 @@ struct EmbossDataBase std::string volume_name; }; -/// -/// Hold neccessary data to update embossed text object in job -/// -struct EmbossDataUpdate : public EmbossDataBase -{ - // unique identifier of volume to change - ObjectID volume_id; - - // flag that job is canceled - // for time after process. - std::shared_ptr > cancel; -}; - /// /// Hold neccessary data to create ModelVolume in job /// Volume is created on the surface of existing volume in object. @@ -67,6 +54,22 @@ struct EmbossDataCreateVolume : public EmbossDataBase Transform3d hit_instance_tr; }; +/// +/// Create new TextVolume on the surface of ModelObject +/// Should not be stopped +/// +class EmbossCreateVolumeJob : public Job +{ + EmbossDataCreateVolume m_input; + TriangleMesh m_result; + Transform3d m_transformation; + +public: + EmbossCreateVolumeJob(EmbossDataCreateVolume&& input); + void process(Ctl &ctl) override; + void finalize(bool canceled, std::exception_ptr &) override; +}; + /// /// Hold neccessary data to create ModelObject in job /// Object is placed on bed under screen coor @@ -84,51 +87,6 @@ struct EmbossDataCreateObject : public EmbossDataBase std::vector bed_shape; }; -/// -/// Update text shape in existing text volume -/// Predict that there is only one runnig(not canceled) instance of it -/// -class EmbossUpdateJob : public Job -{ - EmbossDataUpdate m_input; - TriangleMesh m_result; -public: - // only move params to private variable - EmbossUpdateJob(EmbossDataUpdate&& input); - - /// - /// Create volume movel by input data - /// - /// Control containing cancel flag - void process(Ctl &ctl) override; - - /// - /// Update volume - change object_id - /// - /// Was process canceled. - /// NOTE: Be carefull it doesn't care about - /// time between finished process and started finalize part. - /// unused - void finalize(bool canceled, std::exception_ptr &) override; -}; - - -/// -/// Create new TextVolume on the surface of ModelObject -/// Should not be stopped -/// -class EmbossCreateVolumeJob : public Job -{ - EmbossDataCreateVolume m_input; - TriangleMesh m_result; - Transform3d m_transformation; - -public: - EmbossCreateVolumeJob(EmbossDataCreateVolume&& input); - void process(Ctl &ctl) override; - void finalize(bool canceled, std::exception_ptr &) override; -}; - /// /// Create new TextObject on the platter /// Should not be stopped @@ -138,13 +96,91 @@ class EmbossCreateObjectJob : public Job EmbossDataCreateObject m_input; TriangleMesh m_result; Transform3d m_transformation; - public: EmbossCreateObjectJob(EmbossDataCreateObject&& input); void process(Ctl &ctl) override; void finalize(bool canceled, std::exception_ptr &) override; }; +/// +/// Hold neccessary data to update embossed text object in job +/// +struct EmbossDataUpdate : public EmbossDataBase +{ + // unique identifier of volume to change + ObjectID volume_id; + + // flag that job is canceled + // for time after process. + std::shared_ptr> cancel; +}; + +/// +/// Update text shape in existing text volume +/// Predict that there is only one runnig(not canceled) instance of it +/// +class EmbossUpdateJob : public Job +{ + EmbossDataUpdate m_input; + TriangleMesh m_result; + +public: + // move params to private variable + EmbossUpdateJob(EmbossDataUpdate &&input); + + /// + /// Create new embossed volume by m_input data and store to m_result + /// + /// Control containing cancel flag + void process(Ctl &ctl) override; + + /// + /// Update volume - change object_id + /// + /// Was process canceled. + /// NOTE: Be carefull it doesn't care about + /// time between finished process and started finalize part. + /// unused + void finalize(bool canceled, std::exception_ptr &) override; +}; + +/// +/// Hold neccessary data to update embossed text object in job +/// +struct UseSurfaceData : public EmbossDataUpdate +{ + // Transformation of text volume inside of object + Transform3d text_tr; + + // Define projection move + // True (raised) .. move outside from surface + // False (engraved).. move into object + bool is_outside; + + // IMPROVE: copy of source mesh tringles + // copy could slow down on big meshes + indexed_triangle_set mesh_its; + // Transformation of volume inside of object + Transform3d mesh_tr; + // extract bounds for projection + BoundingBoxf3 mesh_bb; +}; + +/// +/// Update text volume to use surface from object +/// +class UseSurfaceJob : public Job +{ + UseSurfaceData m_input; + TriangleMesh m_result; + +public: + // move params to private variable + UseSurfaceJob(UseSurfaceData &&input); + void process(Ctl &ctl) override; + void finalize(bool canceled, std::exception_ptr &) override; +}; + } // namespace Slic3r::GUI #endif // slic3r_EmbossJob_hpp_ diff --git a/src/slic3r/Utils/FontListSerializable.cpp b/src/slic3r/Utils/FontListSerializable.cpp index 2203339f6..c79bee4c0 100644 --- a/src/slic3r/Utils/FontListSerializable.cpp +++ b/src/slic3r/Utils/FontListSerializable.cpp @@ -10,6 +10,7 @@ const std::string FontListSerializable::APP_CONFIG_FONT_NAME = "name"; const std::string FontListSerializable::APP_CONFIG_FONT_DESCRIPTOR = "descriptor"; const std::string FontListSerializable::APP_CONFIG_FONT_LINE_HEIGHT = "line_height"; const std::string FontListSerializable::APP_CONFIG_FONT_DEPTH = "depth"; +const std::string FontListSerializable::APP_CONFIG_FONT_USE_SURFACE = "use_surface"; const std::string FontListSerializable::APP_CONFIG_FONT_BOLDNESS = "boldness"; const std::string FontListSerializable::APP_CONFIG_FONT_SKEW = "skew"; const std::string FontListSerializable::APP_CONFIG_FONT_DISTANCE = "distance"; @@ -23,6 +24,15 @@ std::string FontListSerializable::create_section_name(unsigned index) return AppConfig::SECTION_FONT + ':' + std::to_string(index); } +// check only existence of flag +bool FontListSerializable::read(const std::map& section, const std::string& key, bool& value){ + auto item = section.find(key); + if (item == section.end()) return false; + + value = true; + return true; +} + #include "fast_float/fast_float.h" bool FontListSerializable::read(const std::map& section, const std::string& key, float& value){ auto item = section.find(key); @@ -92,6 +102,7 @@ std::optional FontListSerializable::load_font_item( FontProp fp; read(app_cfg_section, APP_CONFIG_FONT_LINE_HEIGHT, fp.size_in_mm); read(app_cfg_section, APP_CONFIG_FONT_DEPTH, fp.emboss); + read(app_cfg_section, APP_CONFIG_FONT_USE_SURFACE, fp.use_surface); read(app_cfg_section, APP_CONFIG_FONT_BOLDNESS, fp.boldness); read(app_cfg_section, APP_CONFIG_FONT_SKEW, fp.skew); read(app_cfg_section, APP_CONFIG_FONT_DISTANCE, fp.distance); @@ -115,6 +126,8 @@ void FontListSerializable::store_font_item(AppConfig & cfg, const FontProp &fp = fi.prop; cfg.set(section_name, APP_CONFIG_FONT_LINE_HEIGHT, std::to_string(fp.size_in_mm)); cfg.set(section_name, APP_CONFIG_FONT_DEPTH, std::to_string(fp.emboss)); + if (fp.use_surface) + cfg.set(section_name, APP_CONFIG_FONT_USE_SURFACE, "true"); if (fp.boldness.has_value()) cfg.set(section_name, APP_CONFIG_FONT_BOLDNESS, std::to_string(*fp.boldness)); if (fp.skew.has_value()) diff --git a/src/slic3r/Utils/FontListSerializable.hpp b/src/slic3r/Utils/FontListSerializable.hpp index 607efef85..16c8c7744 100644 --- a/src/slic3r/Utils/FontListSerializable.hpp +++ b/src/slic3r/Utils/FontListSerializable.hpp @@ -21,6 +21,7 @@ class FontListSerializable static const std::string APP_CONFIG_FONT_DESCRIPTOR; static const std::string APP_CONFIG_FONT_LINE_HEIGHT; static const std::string APP_CONFIG_FONT_DEPTH; + static const std::string APP_CONFIG_FONT_USE_SURFACE; static const std::string APP_CONFIG_FONT_BOLDNESS; static const std::string APP_CONFIG_FONT_SKEW; static const std::string APP_CONFIG_FONT_DISTANCE; @@ -37,10 +38,11 @@ public: private: // TODO: move to app config like read from section + static bool read(const std::map& section, const std::string& key, bool& value); static bool read(const std::map& section, const std::string& key, float& value); static bool read(const std::map& section, const std::string& key, std::optional& value); static bool read(const std::map& section, const std::string& key, std::optional& value); - static bool read(const std::map& section, const std::string& key, std::optional& value); + static bool read(const std::map& section, const std::string& key, std::optional& value); }; } // namespace Slic3r diff --git a/tests/libslic3r/test_emboss.cpp b/tests/libslic3r/test_emboss.cpp index 5ea785565..1ebc3e6c7 100644 --- a/tests/libslic3r/test_emboss.cpp +++ b/tests/libslic3r/test_emboss.cpp @@ -330,10 +330,9 @@ TEST_CASE("Cut surface", "[]") CHECK(!surfaces.empty()); Emboss::OrthoProject projection(Transform3d::Identity(), Vec3f(0.f, 0.f, 10.f)); - for (auto &surface : surfaces) - its_translate(surface, Vec3f(0.f, 0.f, 10)); + its_translate(surfaces, Vec3f(0.f, 0.f, 10)); - indexed_triangle_set its = cuts2model(surfaces, projection); + indexed_triangle_set its = cut2model(surfaces, projection); CHECK(!its.empty()); //its_write_obj(its, "C:/data/temp/projected.obj"); } @@ -479,12 +478,12 @@ namespace Slic3r::MeshBoolean::cgal2 { /// Name of property map to store conversion from face to contour /// Identify point on shape contour /// CGAL model of extruded shape - CGALMesh to_cgal(const ExPolygons &shape, - const Slic3r::Emboss::IProject &projection, - int32_t shape_id, - const std::string &edge_shape_map_name, - const std::string &face_shape_map_name, - std::vector &contour_indices) + CGALMesh to_cgal(const ExPolygons &shape, + const Slic3r::Emboss::IProjection &projection, + int32_t shape_id, + const std::string &edge_shape_map_name, + const std::string &face_shape_map_name, + std::vector &contour_indices) { CGALMesh result; if (shape.empty()) return result; @@ -500,7 +499,7 @@ namespace Slic3r::MeshBoolean::cgal2 { int32_t vertex_index = static_cast(contour_indices.size()); contour_indices.push_back({iexpoly, id, int32_t(num_vertices_old) }); for (const Point& p2 : polygon.points) { - auto p = projection.project(p2); + auto p = projection.create_front_back(p2); auto vi = result.add_vertex(typename CGALMesh::Point{ p.first.x(), p.first.y(), p.first.z() }); assert((size_t)vi == indices.size() + num_vertices_old); indices.emplace_back(vi); @@ -565,7 +564,7 @@ namespace Slic3r::MeshBoolean::cgal2 { /// Cutted surface, Its do not represent Volume indexed_triangle_set cut_shape(const indexed_triangle_set &source, const ExPolygon &shape, - const Emboss::IProject &projection) + const Emboss::IProjection &projection) { // NOT implemented yet return {}; @@ -580,7 +579,7 @@ indexed_triangle_set cut_shape(const indexed_triangle_set &source, /// Cutted surface, Its do not represent Volume indexed_triangle_set cut_shape(const indexed_triangle_set &source, const ExPolygons &shapes, - const Emboss::IProject &projection) + const Emboss::IProjection &projection) { indexed_triangle_set result; for (const ExPolygon &shape : shapes)