diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 0c4720c6b5..a313ed0d37 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -148,12 +148,14 @@ namespace priv { /// Keep actual selected style /// Needed when transform per glyph /// Needed for transform per glyph +/// Define type of volume - side of surface(in / out) /// Cancel for previous job /// Base data for emboss text static DataBase create_emboss_data_base(const std::string &text, StyleManager &style_manager, TextLinesModel &text_lines, const Selection &selection, + ModelVolumeType type, std::shared_ptr> &cancel); /// @@ -176,6 +178,8 @@ static void start_create_volume_job(const ModelObject *object, /// Mouse position which define position /// Volume to find surface for create /// Ability to ray cast to model +/// Per glyph transformation +/// Line height need font file/param> /// Contain already used scene RayCasters /// True when start creation, False when there is no hit surface by screen coor static bool start_create_volume_on_surface_job(DataBase &emboss_data, @@ -183,6 +187,8 @@ static bool start_create_volume_on_surface_job(DataBase &emboss_data, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster, + TextLinesModel &text_lines, + /*const */ StyleManager &style_manager, GLCanvas3D &canvas); /// @@ -249,7 +255,29 @@ static bool apply_camera_dir(const Camera &camera, GLCanvas3D &canvas, bool keep } // namespace priv namespace { +// for existing volume which is selected(could init different(to volume text) lines count when edit text) void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines=0); +// before text volume is created +void init_new_text_line(TextLinesModel &text_lines, const Transform3d& new_text_tr, const ModelObject& mo, /* const*/ StyleManager &style_manager) +{ + // prepare volumes to slice + ModelVolumePtrs volumes; + volumes.reserve(mo.volumes.size()); + for (ModelVolume *volume : mo.volumes) { + // only part could be surface for volumes + if (!volume->is_model_part()) + continue; + volumes.push_back(volume); + } + + double line_height = style_manager.get_line_height(); + if (line_height < 0) + return; + + FontProp::Align align = style_manager.get_font_prop().align; + unsigned count_lines = 1; + text_lines.init(new_text_tr, volumes, align, line_height, count_lines); +} } void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos) @@ -258,11 +286,11 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous return; const GLVolume *gl_volume = get_first_hovered_gl_volume(m_parent); - DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_job_cancel); + DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), volume_type, m_job_cancel); bool is_simple_mode = wxGetApp().get_mode() == comSimple; if (gl_volume != nullptr && !is_simple_mode) { // Try to cast ray into scene and find object for add volume - if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager, m_parent)) { + if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_manager, m_text_lines, m_style_manager, m_parent)) { // When model is broken. It could appear that hit miss the object. // So add part near by in simmilar manner as right panel do create_volume(volume_type); @@ -285,7 +313,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type) Size s = m_parent.get_canvas_size(); Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.); - DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_job_cancel); + DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), volume_type, m_job_cancel); const ModelObjectPtrs &objects = selection.get_model()->objects; bool is_simple_mode = wxGetApp().get_mode() == comSimple; // No selected object so create new object @@ -303,7 +331,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type) priv::find_closest_volume(selection, screen_center, camera, objects, &coor, &vol); if (vol == nullptr) { priv::start_create_object_job(emboss_data, screen_center); - } else if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager, m_parent)) { + } else if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager, m_text_lines, m_style_manager, m_parent)) { // in centroid of convex hull is not hit with object // soo create transfomation on border of object @@ -321,7 +349,11 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type) - instance_bb.size().y() / 2 - prop.size_in_mm / 2, // under prop.emboss / 2 - instance_bb.size().z() / 2 // lay on bed ); - Transform3d volume_trmat = tr * Eigen::Translation3d(offset_tr); + Transform3d volume_trmat = tr * Eigen::Translation3d(offset_tr); + if (prop.per_glyph) { + init_new_text_line(m_text_lines, volume_trmat, *obj, m_style_manager); + emboss_data.text_lines = m_text_lines.get_lines(); + } priv::start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); } } @@ -1062,26 +1094,65 @@ EmbossStyles GLGizmoEmboss::create_default_styles() namespace { void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines) { - assert(style_manager.is_active_font()); - if (!style_manager.is_active_font()) + double line_height = style_manager.get_line_height(); + if (line_height < 0) return; - const auto &ffc = style_manager.get_font_file_with_cache(); - assert(ffc.has_value()); - if (!ffc.has_value()) + + const GLVolume *gl_volume_ptr = selection.get_first_volume(); + if (gl_volume_ptr == nullptr) return; - const auto &ff_ptr = ffc.font_file; - assert(ff_ptr != nullptr); - if (ff_ptr == nullptr) + const GLVolume &gl_volume = *gl_volume_ptr; + const ModelObjectPtrs &objects = selection.get_model()->objects; + const ModelObject *mo_ptr = get_model_object(gl_volume, objects); + if (mo_ptr == nullptr) + return; + const ModelObject &mo = *mo_ptr; + + const ModelVolume *mv_ptr = get_model_volume(gl_volume, objects); + if (mv_ptr == nullptr) + return; + const ModelVolume &mv = *mv_ptr; + if (mv.is_the_only_one_part()) return; + const std::optional &tc_opt = mv.text_configuration; + if (!tc_opt.has_value()) + return; + const TextConfiguration &tc = *tc_opt; + + // calculate count lines when not set + if (count_lines == 0) { + count_lines = get_count_lines(tc.text); + if (count_lines == 0) + return; + } + + // prepare volumes to slice + ModelVolumePtrs volumes; + volumes.reserve(mo.volumes.size()); + for (ModelVolume *volume : mo.volumes) { + // only part could be surface for volumes + if (!volume->is_model_part()) + continue; + + // is selected volume + if (mv.id() == volume->id()) + continue; + + volumes.push_back(volume); + } + + // For interactivity during drag over surface it must be from gl_volume not volume. + const Transform3d &mv_trafo = gl_volume.get_volume_transformation().get_matrix(); const FontProp &fp = style_manager.get_font_prop(); - const FontFile &ff = *ff_ptr; - - double line_height = TextLinesModel::calc_line_height(ff, fp); - text_lines.init(selection, line_height, count_lines); + text_lines.init(mv_trafo, volumes, fp.align, line_height, count_lines); } } +void GLGizmoEmboss::reinit_text_lines(unsigned count_lines) { + init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, count_lines); +} + void GLGizmoEmboss::set_volume_by_selection() { const Selection &selection = m_parent.get_selection(); @@ -1221,7 +1292,7 @@ void GLGizmoEmboss::set_volume_by_selection() m_volume_id = volume->id(); if (tc.style.prop.per_glyph) - init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager); + reinit_text_lines(); // Calculate current angle of up vector assert(m_style_manager.is_active_font()); @@ -1304,7 +1375,7 @@ bool GLGizmoEmboss::process() // exist loaded font file? if (!m_style_manager.is_active_font()) return false; - DataUpdate data{priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_job_cancel), + DataUpdate data{priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_volume->type(), m_job_cancel), m_volume->id()}; std::unique_ptr job = nullptr; @@ -1338,7 +1409,7 @@ bool GLGizmoEmboss::process() // check that there is not unexpected volume type assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier()); - UpdateSurfaceVolumeData surface_data{std::move(data), {text_tr, is_outside, std::move(sources)}}; + UpdateSurfaceVolumeData surface_data{std::move(data), {text_tr, std::move(sources)}}; job = std::make_unique(std::move(surface_data)); } else { job = std::make_unique(std::move(data)); @@ -1555,7 +1626,7 @@ void GLGizmoEmboss::draw_text_input() 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(m_text_lines, m_parent.get_selection(), m_style_manager, count_lines); + reinit_text_lines(count_lines); } process(); range_text = create_range_text_prep(); @@ -1982,6 +2053,8 @@ void GLGizmoEmboss::draw_font_list_line() if (exist_change) { m_style_manager.clear_glyphs_cache(); + if (m_style_manager.get_font_prop().per_glyph) + reinit_text_lines(m_text_lines.get_lines().size()); process(); } } @@ -2206,7 +2279,8 @@ void GLGizmoEmboss::draw_model_type() m_volume->set_type(*new_type); // Update volume position when switch from part or into part - if (m_volume->text_configuration->style.prop.use_surface) { + const FontProp& prop = m_volume->text_configuration->style.prop; + if (prop.use_surface || prop.per_glyph) { // move inside bool is_volume_move_inside = (type == part); bool is_volume_move_outside = (*new_type == part); @@ -2835,6 +2909,9 @@ bool GLGizmoEmboss::set_height() { if (is_approx(value, m_volume->text_configuration->style.prop.size_in_mm)) return false; + if (m_style_manager.get_font_prop().per_glyph) + reinit_text_lines(m_text_lines.get_lines().size()); + #ifdef USE_PIXEL_SIZE_IN_WX_FONT // store font size into path serialization const wxFont &wx_font = m_style_manager.get_wx_font(); @@ -3047,6 +3124,8 @@ void GLGizmoEmboss::draw_advanced() const bool *def_per_glyph = stored_style ? &stored_style->prop.per_glyph : nullptr; if (rev_checkbox(tr.per_glyph, per_glyph, def_per_glyph, _u8L("Revert Transformation per glyph."))) { + if (per_glyph && !m_text_lines.is_init()) + reinit_text_lines(); process(); } else if (ImGui::IsItemHovered()) { if (per_glyph) { @@ -3054,7 +3133,7 @@ void GLGizmoEmboss::draw_advanced() } else { ImGui::SetTooltip("%s", _u8L("Set position and orientation per Glyph.").c_str()); if (!m_text_lines.is_init()) - init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager); + reinit_text_lines(); } } else if (!per_glyph && m_text_lines.is_init()) m_text_lines.reset(); @@ -3064,10 +3143,10 @@ void GLGizmoEmboss::draw_advanced() 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")) { - init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, m_text_lines.get_lines().size()); + reinit_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()); + ImGui::SetTooltip("TEST PURPOSE ONLY\nMove base line (up/down) for allign letters"); m_imgui->disabled_end(); // !per_glyph int selected_align = static_cast(font_prop.align); @@ -3094,6 +3173,8 @@ void GLGizmoEmboss::draw_advanced() }; if (revertible(tr.alignment, selected_align, def_align, _u8L("Revert alignment."), undo_offset, draw)){ font_prop.align = static_cast(selected_align); + if (font_prop.per_glyph) + reinit_text_lines(m_text_lines.get_lines().size()); // TODO: move with text in finalize to not change position process(); } @@ -3132,6 +3213,8 @@ void GLGizmoEmboss::draw_advanced() m_volume->text_configuration->style.prop.line_gap != font_prop.line_gap) { // line gap is planed to be stored inside of imgui font atlas m_style_manager.clear_imgui_font(); + if (font_prop.per_glyph) + reinit_text_lines(m_text_lines.get_lines().size()); exist_change = true; } } @@ -3198,9 +3281,13 @@ void GLGizmoEmboss::draw_advanced() } if (is_moved){ - m_volume->text_configuration->style.prop.distance = font_prop.distance; - float act_distance = font_prop.distance.has_value() ? *font_prop.distance : .0f; - do_translate(Vec3d::UnitZ() * (act_distance - prev_distance)); + if (font_prop.per_glyph){ + process(); + } else { + m_volume->text_configuration->style.prop.distance = font_prop.distance; + float act_distance = font_prop.distance.has_value() ? *font_prop.distance : .0f; + do_translate(Vec3d::UnitZ() * (act_distance - prev_distance)); + } } m_imgui->disabled_end(); @@ -3234,8 +3321,11 @@ void GLGizmoEmboss::draw_advanced() if (m_style_manager.is_active_font() && gl_volume != nullptr) m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), priv::up_limit); + if (font_prop.per_glyph) + reinit_text_lines(m_text_lines.get_lines().size()); + // recalculate for surface cut - if (font_prop.use_surface) + if (font_prop.use_surface || font_prop.per_glyph) process(); } @@ -3283,14 +3373,19 @@ void GLGizmoEmboss::draw_advanced() if (exist_change) { m_style_manager.clear_glyphs_cache(); + if (m_style_manager.get_font_prop().per_glyph) + reinit_text_lines(); + else + m_text_lines.reset(); process(); } 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_font_prop().use_surface; - if (priv::apply_camera_dir(cam, m_parent, m_keep_up) && use_surface) + const FontProp &prop = m_style_manager.get_font_prop(); + if (priv::apply_camera_dir(cam, m_parent, m_keep_up) && + prop.use_surface || prop.per_glyph) process(); } else if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", _u8L("Orient the text towards the camera.").c_str()); @@ -3565,7 +3660,12 @@ bool priv::draw_button(const IconManager::VIcons &icons, IconType type, bool dis // priv namespace implementation /////////////// -DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &style_manager, TextLinesModel& text_lines, const Selection& selection, std::shared_ptr>& cancel) +DataBase priv::create_emboss_data_base(const std::string &text, + StyleManager &style_manager, + TextLinesModel &text_lines, + const Selection &selection, + ModelVolumeType type, + std::shared_ptr> &cancel) { // create volume_name std::string volume_name = text; // copy @@ -3594,6 +3694,8 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &st } else text_lines.reset(); + bool is_outside = (type == ModelVolumeType::MODEL_PART); + // Cancel previous Job, when it is in process // worker.cancel(); --> Use less in this case I want cancel only previous EmbossJob no other jobs // Cancel only EmbossUpdateJob no others @@ -3601,7 +3703,7 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &st cancel->store(true); // create new shared ptr to cancel new job cancel = std::make_shared>(false); - return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), tc, volume_name, cancel, text_lines.get_lines()}; + return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), tc, volume_name, is_outside, cancel, text_lines.get_lines()}; } void priv::start_create_object_job(DataBase &emboss_data, const Vec2d &coor) @@ -3639,10 +3741,7 @@ void priv::start_create_volume_job(const ModelObject *object, if (sources.empty()) { use_surface = false; } else { - bool is_outside = volume_type == ModelVolumeType::MODEL_PART; - // check that there is not unexpected volume type - assert(is_outside || volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::PARAMETER_MODIFIER); - SurfaceVolumeData sfvd{volume_trmat, is_outside, std::move(sources)}; + SurfaceVolumeData sfvd{volume_trmat, std::move(sources)}; CreateSurfaceVolumeData surface_data{std::move(emboss_data), std::move(sfvd), volume_type, object->id()}; job = std::make_unique(std::move(surface_data)); } @@ -3658,8 +3757,14 @@ void priv::start_create_volume_job(const ModelObject *object, queue_job(worker, std::move(job)); } -bool priv::start_create_volume_on_surface_job( - DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster, GLCanvas3D& canvas) +bool priv::start_create_volume_on_surface_job(DataBase &emboss_data, + ModelVolumeType volume_type, + const Vec2d &screen_coor, + const GLVolume *gl_volume, + RaycastManager &raycaster, + TextLinesModel &text_lines, + StyleManager &style_manager, + GLCanvas3D &canvas) { assert(gl_volume != nullptr); if (gl_volume == nullptr) return false; @@ -3670,12 +3775,14 @@ bool priv::start_create_volume_on_surface_job( int object_idx = gl_volume->object_idx(); if (object_idx < 0 || static_cast(object_idx) >= objects.size()) return false; - ModelObject *obj = objects[object_idx]; - size_t vol_id = obj->volumes[gl_volume->volume_idx()]->id().id; + const ModelObject *obj_ptr = objects[object_idx]; + if (obj_ptr == nullptr) return false; + const ModelObject &obj = *obj_ptr; + size_t vol_id = obj.volumes[gl_volume->volume_idx()]->id().id; auto cond = RaycastManager::AllowVolumes({vol_id}); RaycastManager::Meshes meshes = create_meshes(canvas, cond); - raycaster.actualize(*obj, &cond, &meshes); + raycaster.actualize(obj, &cond, &meshes); const Camera &camera = plater->get_camera(); std::optional hit = ray_from_camera(raycaster, screen_coor, camera, &cond); @@ -3691,8 +3798,13 @@ bool priv::start_create_volume_on_surface_job( const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); - Transform3d volume_trmat = instance.inverse() * surface_trmat; - start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); + Transform3d volume_trmat = instance.inverse() * surface_trmat; + + if (font_prop.per_glyph){ + init_new_text_line(text_lines, volume_trmat, obj, style_manager); + emboss_data.text_lines = text_lines.get_lines(); + } + start_create_volume_job(obj_ptr, volume_trmat, emboss_data, volume_type); return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 05ce3875b9..2ac0a9eda3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -318,6 +318,7 @@ private: // Keep information about curvature of text line around surface TextLinesModel m_text_lines; + void reinit_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 368d494372..c1c14b6459 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -29,6 +29,9 @@ namespace priv{ // create sure that emboss object is bigger than source object [in mm] constexpr float safe_extension = 1.0f; +// Offset of clossed side to model +constexpr float SAFE_SURFACE_OFFSET = 0.015f; // [in mm] + /// /// Assert check of inputs data /// @@ -513,7 +516,6 @@ template TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w double shape_scale = get_shape_scale(prop, *font.font_file); double projec_scale = shape_scale / SHAPE_SCALE; double depth = prop.emboss / projec_scale; - auto projectZ = std::make_unique(depth); auto scale_tr = Eigen::Scaling(projec_scale); // half of font em size for direction of letter emboss @@ -534,16 +536,20 @@ template TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w continue; Vec2d to_zero_vec = letter_bb.center().cast() * shape_scale; // [in mm] - auto to_zero = Eigen::Translation(-to_zero_vec.x(), 0., 0.); + float surface_offset = input.is_outside ? -priv::SAFE_SURFACE_OFFSET : (-prop.emboss + priv::SAFE_SURFACE_OFFSET); + if (prop.distance.has_value()) + surface_offset += *prop.distance; + + Eigen::Translation to_zero(-to_zero_vec.x(), 0., static_cast(surface_offset)); const double &angle = angles[i]; - auto rotate = Eigen::AngleAxisd(angle + M_PI_2, Vec3d::UnitY()); + Eigen::AngleAxisd rotate(angle + M_PI_2, Vec3d::UnitY()); const PolygonPoint &sample = samples[i]; Vec2d offset_vec = unscale(sample.point); // [in mm] - auto offset_tr = Eigen::Translation(offset_vec.x(), 0., -offset_vec.y()); - + Eigen::Translation offset_tr(offset_vec.x(), 0., -offset_vec.y()); Transform3d tr = offset_tr * rotate * to_zero * scale_tr; + const ExPolygons &letter_shape = shapes[s_i_offset + i]; assert(get_extents(letter_shape) == letter_bb); auto projectZ = std::make_unique(depth); @@ -883,11 +889,9 @@ OrthoProject priv::create_projection_for_cut( OrthoProject3d priv::create_emboss_projection( bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut) { - // Offset of clossed side to model - const float surface_offset = 0.015f; // [in mm] float - front_move = (is_outside) ? emboss : surface_offset, - back_move = -((is_outside) ? surface_offset : emboss); + front_move = (is_outside) ? emboss : SAFE_SURFACE_OFFSET, + back_move = -((is_outside) ? SAFE_SURFACE_OFFSET : emboss); its_transform(cut, tr.pretranslate(Vec3d(0., 0., front_move))); Vec3d from_front_to_back(0., 0., back_move - front_move); return OrthoProject3d(from_front_to_back); @@ -895,7 +899,7 @@ OrthoProject3d priv::create_emboss_projection( namespace { -indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transform3d& tr,const SurfaceVolumeData::ModelSources &sources, bool is_outside, DataBase& input, std::function was_canceled) { +indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transform3d& tr,const SurfaceVolumeData::ModelSources &sources, DataBase& input, std::function was_canceled) { assert(!sources.empty()); BoundingBox bb = get_extents(shapes); const FontFile &ff = *input.font_file.font_file; @@ -985,7 +989,7 @@ indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transfor if (was_canceled()) return {}; // !! Projection needs to transform cut - OrthoProject3d projection = priv::create_emboss_projection(is_outside, fp.emboss, emboss_tr, cut); + OrthoProject3d projection = priv::create_emboss_projection(input.is_outside, fp.emboss, emboss_tr, cut); return cut2model(cut, projection); } @@ -1041,7 +1045,7 @@ TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &in Transform3d modify = offset_tr * rotate; Transform3d tr = input2.text_tr * modify; - indexed_triangle_set glyph_its = cut_surface_to_its(glyph_shape, tr, input2.sources, input2.is_outside, input1, was_canceled); + indexed_triangle_set glyph_its = cut_surface_to_its(glyph_shape, tr, input2.sources, input1, was_canceled); // move letter in volume on the right position its_transform(glyph_its, modify); @@ -1074,7 +1078,7 @@ TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2 if (shapes.empty()) throw JobException(_u8L("Font doesn't have any shape for given text.").c_str()); - indexed_triangle_set its = cut_surface_to_its(shapes, input2.text_tr, input2.sources, input2.is_outside, input1, was_canceled); + indexed_triangle_set its = cut_surface_to_its(shapes, input2.text_tr, input2.sources, input1, was_canceled); if (was_canceled()) return {}; if (its.empty()) throw JobException(_u8L("There is no valid surface for text projection.").c_str()); diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index bbe9c940c1..c15381416a 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -29,6 +29,11 @@ struct DataBase // new volume name created from text std::string volume_name; + // Define projection move + // True (raised) .. move outside from surface + // False (engraved).. move into object + bool is_outside; + // flag that job is canceled // for time after process. std::shared_ptr> cancel; @@ -158,11 +163,6 @@ struct SurfaceVolumeData // 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; - struct ModelSource { // source volumes diff --git a/src/slic3r/GUI/TextLines.cpp b/src/slic3r/GUI/TextLines.cpp index 785b21d52c..7e6e71a580 100644 --- a/src/slic3r/GUI/TextLines.cpp +++ b/src/slic3r/GUI/TextLines.cpp @@ -251,62 +251,24 @@ GLModel::Geometry create_geometry(const TextLines &lines) } } // namespace -void TextLinesModel::init(const Selection &selection, double line_height, unsigned count_lines) +void TextLinesModel::init(const Transform3d &text_tr, const ModelVolumePtrs &volumes_to_slice, FontProp::Align align, double line_height, unsigned count_lines) { m_model.reset(); - m_lines.clear(); + m_lines.clear(); - const GLVolume *gl_volume_ptr = selection.get_first_volume(); - if (gl_volume_ptr == nullptr) - return; - const GLVolume &gl_volume = *gl_volume_ptr; - const ModelObjectPtrs &objects = selection.get_model()->objects; - const ModelObject *mo_ptr = get_model_object(gl_volume, objects); - if (mo_ptr == nullptr) - return; - const ModelObject &mo = *mo_ptr; - - const ModelVolume *mv_ptr = get_model_volume(gl_volume, objects); - if (mv_ptr == nullptr) - return; - const ModelVolume &mv = *mv_ptr; - if (mv.is_the_only_one_part()) - 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); for (size_t i = 0; i < count_lines; ++i) line_centers[i] = static_cast(first_line_center - i * line_height); - const Transform3d &mv_trafo = gl_volume.get_volume_transformation().get_matrix(); - // contour transformation - Transform3d c_trafo = mv_trafo * get_rotation(); + Transform3d c_trafo = text_tr * get_rotation(); Transform3d c_trafo_inv = c_trafo.inverse(); std::vector line_contours(count_lines); - for (const ModelVolume *volume : mo.volumes) { - // only part could be surface for volumes - if (!volume->is_model_part()) - continue; - - // is selected volume - if (mv.id() == volume->id()) - continue; - + for (const ModelVolume *volume : volumes_to_slice) { MeshSlicingParams slicing_params; slicing_params.trafo = c_trafo_inv * volume->get_matrix(); - for (size_t i = 0; i < count_lines; ++i) { const Polygons polys = Slic3r::slice_mesh(volume->mesh().its, line_centers[i], slicing_params); if (polys.empty()) @@ -328,6 +290,7 @@ void TextLinesModel::init(const Selection &selection, double line_height, unsign return; m_model.init_from(std::move(geometry)); /*/ + // slower solution ColorRGBA color(.7f, .7f, .7f, .7f); // Transparent Gray m_model.set_color(color); m_model.init_from(create_its(m_lines)); diff --git a/src/slic3r/GUI/TextLines.hpp b/src/slic3r/GUI/TextLines.hpp index 58f27fe22c..bef7e2262f 100644 --- a/src/slic3r/GUI/TextLines.hpp +++ b/src/slic3r/GUI/TextLines.hpp @@ -7,27 +7,35 @@ #include #include "slic3r/GUI/GLModel.hpp" +namespace Slic3r { +class ModelVolume; +typedef std::vector ModelVolumePtrs; +} + namespace Slic3r::GUI { -class Selection; class TextLinesModel { public: // line offset in y direction (up/down) float offset = 0; + /// /// Initialize model and lines /// - /// Must be selected text volume - /// Height of text line with spacing [in mm] - /// Offset of base line from center [in mm] - /// [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); + /// Transformation of text volume inside object (aka inside of instance) + /// Vector of volumes to be sliced + /// Vertical (Y) align of the text + /// Distance between lines [in mm] + /// Count lines(slices over volumes) + void init(const Transform3d &text_tr, const ModelVolumePtrs& volumes_to_slice, FontProp::Align align, double line_height, unsigned count_lines); + void render(const Transform3d &text_world); bool is_init() const { return m_model.is_initialized(); } void reset() { m_model.reset(); m_lines.clear(); } const Slic3r::Emboss::TextLines &get_lines() const { return m_lines; } - static double calc_line_height(const Slic3r::Emboss::FontFile& ff, const FontProp& fp); + + static double calc_line_height(const Slic3r::Emboss::FontFile& ff, const FontProp& fp); // return lineheight in mm private: Slic3r::Emboss::TextLines m_lines; diff --git a/src/slic3r/Utils/EmbossStyleManager.cpp b/src/slic3r/Utils/EmbossStyleManager.cpp index 4f066b9c85..623c147176 100644 --- a/src/slic3r/Utils/EmbossStyleManager.cpp +++ b/src/slic3r/Utils/EmbossStyleManager.cpp @@ -282,6 +282,25 @@ void StyleManager::clear_glyphs_cache() void StyleManager::clear_imgui_font() { m_style_cache.atlas.Clear(); } +#include "slic3r/GUI/TextLines.hpp" +double StyleManager::get_line_height() +{ + assert(is_active_font()); + if (!is_active_font()) + return -1; + const auto &ffc = get_font_file_with_cache(); + assert(ffc.has_value()); + if (!ffc.has_value()) + return -1; + const auto &ff_ptr = ffc.font_file; + assert(ff_ptr != nullptr); + if (ff_ptr == nullptr) + return -1; + const FontProp &fp = get_font_prop(); + const FontFile &ff = *ff_ptr; + return TextLinesModel::calc_line_height(ff, fp); +} + ImFont *StyleManager::get_imgui_font() { if (!is_active_font()) return nullptr; diff --git a/src/slic3r/Utils/EmbossStyleManager.hpp b/src/slic3r/Utils/EmbossStyleManager.hpp index dd6b9ca129..1f3032ac70 100644 --- a/src/slic3r/Utils/EmbossStyleManager.hpp +++ b/src/slic3r/Utils/EmbossStyleManager.hpp @@ -106,6 +106,10 @@ public: // remove cached imgui font for actual selected font void clear_imgui_font(); + // calculate line height + // not const because access to font file which could be created. + double get_line_height(); /* const */ + // getters for private data const EmbossStyle *get_stored_style() const;