diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 737070607a..99e5bcdc7d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -678,7 +678,7 @@ void GLGizmoEmboss::on_render() { if (m_text_lines.is_init()) { const Transform3d& tr = gl_volume_ptr->world_matrix(); - const auto &fix = m_volume->text_configuration->fix_3mf_tr; + const auto &fix = m_volume->emboss_shape->fix_3mf_tr; if (fix.has_value()) m_text_lines.render(tr * fix->inverse()); else @@ -1096,6 +1096,11 @@ void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* if (mv.is_the_only_one_part()) return; + const std::optional &es_opt = mv.emboss_shape; + if (!es_opt.has_value()) + return; + const EmbossShape &es = *es_opt; + const std::optional &tc_opt = mv.text_configuration; if (!tc_opt.has_value()) return; @@ -1125,8 +1130,8 @@ void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* // For interactivity during drag over surface it must be from gl_volume not volume. Transform3d mv_trafo = gl_volume.get_volume_transformation().get_matrix(); - if (tc.fix_3mf_tr.has_value()) - mv_trafo = mv_trafo * (tc.fix_3mf_tr->inverse()); + if (es.fix_3mf_tr.has_value()) + mv_trafo = mv_trafo * (es.fix_3mf_tr->inverse()); FontProp::VerticalAlign align = style_manager.get_font_prop().align.second; double line_height_mm, line_offset_mm; if (!get_line_height_offset(style_manager, line_height_mm, line_offset_mm)) @@ -1357,13 +1362,18 @@ bool GLGizmoEmboss::process() if (sources.empty()) return false; + const std::optional &es_opt = m_volume->emboss_shape; + if (!es_opt.has_value()) + return false; + const EmbossShape &es = *es_opt; + Transform3d text_tr = m_volume->get_matrix(); - auto& fix_3mf = m_volume->text_configuration->fix_3mf_tr; + auto& fix_3mf = es.fix_3mf_tr; if (fix_3mf.has_value()) text_tr = text_tr * fix_3mf->inverse(); // when it is new applying of use surface than move origin onto surfaca - if (!m_volume->text_configuration->style.prop.use_surface) { + if (!es.projection.use_surface) { auto offset = calc_surface_offset(m_parent.get_selection(), m_raycast_manager); if (offset.has_value()) text_tr *= Eigen::Translation(*offset); @@ -3377,9 +3387,11 @@ std::unique_ptr create_emboss_data_base(const std::string cancel->store(true); // create new shared ptr to cancel new job cancel = std::make_shared>(false); + DataBase base(volume_name, cancel); base.is_outside = is_outside; base.text_lines = text_lines.get_lines(); + base.from_surface = style.distance; FontFileWithCache &font = style_manager.get_font_file_with_cache(); TextConfiguration tc{static_cast(style), text}; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp index b96ea0ac39..6306b0fb7c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp @@ -147,6 +147,16 @@ GuiCfg create_gui_configuration(); // use private definition struct GLGizmoSVG::GuiCfg: public ::GuiCfg{}; +namespace Slic3r { +BoundingBox get_extents(const ExPolygonsWithIds &expoly_ids) +{ + BoundingBox result; + for (const ExPolygonsWithId &expoly_id : expoly_ids) + result.merge(get_extents(expoly_id.expoly)); + return result; +} +} // namespace Slic3r + bool GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos) { CreateVolumeParams input = create_input(m_parent, m_raycast_manager, volume_type); @@ -433,6 +443,79 @@ void GLGizmoSVG::on_stop_dragging() } void GLGizmoSVG::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); } +#include "slic3r/GUI/BitmapCache.hpp" +#include "nanosvg/nanosvgrast.h" +namespace{ +bool init_texture(Texture &texture, const ModelVolume &mv) { + if (!mv.emboss_shape.has_value()) + return false; + + const EmbossShape &es = *mv.emboss_shape; + const std::string &filepath = es.svg_file_path; + if (filepath.empty()) + return false; + + unsigned max_size_px = 256; + // inspired by: + // GLTexture::load_from_svg_file(filepath, false, false, false, max_size_px); + NSVGimage *image = BitmapCache::nsvgParseFromFileWithReplace(filepath.c_str(), "px", 96.0f, {}); + if (image == nullptr) + return false; + ScopeGuard sg_image([image]() { nsvgDelete(image); }); + + // NOTE: Can not use es.shape --> it is aligned and one need offset in svg + ExPolygons shape = to_expolygons(image); + if (shape.empty()) + return false; + + BoundingBox bb = get_extents(shape); + Point bb_size = bb.size(); + double bb_width = bb_size.x(); // [in mm] + double bb_height = bb_size.y(); // [in mm] + + bool is_widder = bb_size.x() > bb_size.y(); + float scale = 0.f; + if (is_widder){ + scale = static_cast(max_size_px / bb_width); + texture.width = max_size_px; + texture.height = static_cast(std::ceil(bb_height * scale)); + } else { + scale = static_cast(max_size_px / bb_height); + texture.width = static_cast(std::ceil(bb_width * scale)); + texture.height = max_size_px; + } + const int n_pixels = texture.width * texture.height; + if (n_pixels <= 0) + return false; + + NSVGrasterizer* rast = nsvgCreateRasterizer(); + if (rast == nullptr) + return false; + ScopeGuard sg_rast([rast]() { nsvgDeleteRasterizer(rast); }); + + int channels_count = 4; + std::vector data(n_pixels * channels_count, 0); + float tx = static_cast(-bb.min.x() * scale); + float ty = static_cast(bb.max.y() * scale); // Reverse direction of y + int stride = texture.width * channels_count; + nsvgRasterizeXY(rast, image, tx, ty, scale, scale, data.data(), texture.width, texture.height, stride); + + // sends data to gpu + glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + glsafe(::glGenTextures(1, &texture.id)); + glsafe(::glBindTexture(GL_TEXTURE_2D, texture.id)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) texture.width, (GLsizei) texture.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void *) data.data())); + + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + + GLuint NO_TEXTURE_ID = 0; + glsafe(::glBindTexture(GL_TEXTURE_2D, NO_TEXTURE_ID)); + return true; +} +} + void GLGizmoSVG::set_volume_by_selection() { const Selection &selection = m_parent.get_selection(); @@ -471,7 +554,7 @@ void GLGizmoSVG::set_volume_by_selection() // Calculate current angle of up vector m_angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit); m_distance = calc_distance(*gl_volume, m_raycast_manager, m_parent); - + init_texture(m_texture, *m_volume); // calculate scale for height and depth inside of scaled object instance calculate_scale(); } @@ -484,6 +567,9 @@ void GLGizmoSVG::reset_volume() m_volume = nullptr; m_volume_id.id = 0; m_volume_shape.shapes_with_ids.clear(); + + if (m_texture.id != 0) + glsafe(::glDeleteTextures(1, &m_texture.id)); } void GLGizmoSVG::calculate_scale() { @@ -553,10 +639,17 @@ void GLGizmoSVG::draw_window() if (m_volume->emboss_shape.has_value()) ImGui::Text("SVG file path is %s", m_volume->emboss_shape->svg_file_path.c_str()); + if (m_texture.id != 0) { + ImTextureID id = (void *) static_cast(m_texture.id); + ImVec2 s(m_texture.width, m_texture.height); + ImGui::Image(id, s); + } + ImGui::Indent(m_gui_cfg->icon_width); draw_depth(); draw_size(); draw_use_surface(); + draw_distance(); draw_rotation(); ImGui::Unindent(m_gui_cfg->icon_width); @@ -597,16 +690,6 @@ void GLGizmoSVG::draw_depth() ImGui::SetTooltip("%s", _u8L("Size in emboss direction.").c_str()); } -namespace Slic3r { -BoundingBox get_extents(const ExPolygonsWithIds &expoly_ids) -{ - BoundingBox result; - for (const ExPolygonsWithId &expoly_id : expoly_ids) - result.merge(get_extents(expoly_id.expoly)); - return result; -} -} // namespace Slic3r - void GLGizmoSVG::draw_size() { ImGuiWrapper::text(m_gui_cfg->translations.size); @@ -859,7 +942,7 @@ void GLGizmoSVG::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) { + if (m_volume->emboss_shape->projection.use_surface) { // move inside bool is_volume_move_inside = (type == part); bool is_volume_move_outside = (*new_type == part); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp index 82f01ed693..3b8fc961a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp @@ -6,6 +6,7 @@ #include "GLGizmoBase.hpp" #include "GLGizmoRotate.hpp" #include "slic3r/GUI/SurfaceDrag.hpp" +#include "slic3r/GUI/GLTexture.hpp" #include "slic3r/Utils/RaycastManager.hpp" #include @@ -20,13 +21,20 @@ #include namespace Slic3r{ - //class AppConfig; - class GLVolume; - class ModelVolume; - enum class ModelVolumeType : int; +//class AppConfig; +class GLVolume; +class ModelVolume; +enum class ModelVolumeType : int; } namespace Slic3r::GUI { + +struct Texture{ + unsigned id{0}; + unsigned width{0}; + unsigned height{0}; +}; + class GLGizmoSVG : public GLGizmoBase { public: @@ -151,10 +159,13 @@ private: std::optional m_scale_depth; void calculate_scale(); + // keep SVG data rendered on GPU + + Texture m_texture; + // only temporary solution static const std::string M_ICON_FILENAME; }; - } // namespace Slic3r::GUI #endif // slic3r_GLGizmoSVG_hpp_ diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index 6ec4caaaa1..0fd0d8a945 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -859,9 +859,8 @@ template TriangleMesh create_mesh_per_glyph(DataBase &input, Fnc w Vec2d to_zero_vec = letter_bb.center().cast() * shape.scale; // [in mm] float surface_offset = input.is_outside ? -SAFE_SURFACE_OFFSET : (-shape.projection.depth + SAFE_SURFACE_OFFSET); - // TODO: fix it - //if (prop.distance.has_value()) - // surface_offset += *prop.distance; + if (input.from_surface.has_value()) + surface_offset += *input.from_surface; Eigen::Translation to_zero(-to_zero_vec.x(), 0., static_cast(surface_offset)); @@ -931,6 +930,8 @@ TriangleMesh try_create_mesh(DataBase &input, const Fnc& was_canceled) double depth = input.shape.projection.depth / scale; auto projectZ = std::make_unique(depth); float offset = input.is_outside ? -SAFE_SURFACE_OFFSET : (SAFE_SURFACE_OFFSET - input.shape.projection.depth); + if (input.from_surface.has_value()) + offset += *input.from_surface; Transform3d tr = Eigen::Translation(0., 0.,static_cast(offset)) * Eigen::Scaling(scale); ProjectTransform project(std::move(projectZ), tr); if (was_canceled()) return {}; diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index 1d5b2e65c2..d347d32b68 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -57,13 +57,18 @@ public: virtual void write(ModelVolume &volume) const; // Define projection move - // True (raised) .. move outside from surface - // False (engraved).. move into object - bool is_outside; + // True (raised) .. move outside from surface (MODEL_PART) + // False (engraved).. move into object (NEGATIVE_VOLUME) + bool is_outside = true; // Define per letter projection on one text line // [optional] It is not used when empty - Slic3r::Emboss::TextLines text_lines; + Slic3r::Emboss::TextLines text_lines = {}; + + // [optional] Define distance for surface + // It is used only for flat surface (not cutted) + // Position of Zero(not set value) differ for MODEL_PART and NEGATIVE_VOLUME + std::optional from_surface; // new volume name std::string volume_name;