From b4133fcf96eeb591e92303f6c65e1ec0a1f6ee8e Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 31 Jan 2023 18:24:12 +0100 Subject: [PATCH 01/64] Remove irrelevant asserts --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index cebef8b42b..663aafc773 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -576,9 +576,6 @@ bool GLGizmoEmboss::on_mouse(const wxMouseEvent &mouse_event) if (mouse_event.Moving()) return false; // not selected volume - assert(m_volume != nullptr); - assert(priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) != nullptr); - assert(m_volume->text_configuration.has_value()); if (m_volume == nullptr || priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr || !m_volume->text_configuration.has_value()) return false; From 617c163bd634580e27c85fe49445f408d6105470 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 31 Jan 2023 19:23:09 +0100 Subject: [PATCH 02/64] Write result transformation into all gl_volumes --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 663aafc773..32dc65e9b0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -547,11 +547,25 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // with Mesa driver OR on Linux if (!m_temp_transformation.has_value()) return false; + int instance_idx = m_parent.get_selection().get_instance_idx(); + const auto &instances = m_volume->get_object()->instances; + if (instance_idx < 0 || instance_idx >= instances.size()) + return false; + // Override of common transformation after draggig by set transformation into gl_volume - Transform3d volume_trmat = - gl_volume->get_instance_transformation().get_matrix().inverse() * + Transform3d volume_trmat = + instances[instance_idx]->get_matrix().inverse() * *m_temp_transformation; - gl_volume->set_volume_transformation(Geometry::Transformation(volume_trmat)); + + // ReWrite transformation inside of all instances + Geometry::Transformation transformation(volume_trmat); + for (GLVolume *vol : m_parent.get_volumes().volumes) { + if (vol->object_idx() != gl_volume->object_idx() || + vol->volume_idx() != gl_volume->volume_idx()) + continue; + vol->set_volume_transformation(transformation); + } + m_parent.toggle_model_objects_visibility(true); // Apply temporary position m_temp_transformation = {}; From baa5a011ee6e085a3323c72b4db4652b10a91c15 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Feb 2023 11:21:41 +0100 Subject: [PATCH 03/64] Rework dragging over surface to use GlVolume and function do_move No need for special rendering any more --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 256 ++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 23 ++- 2 files changed, 122 insertions(+), 157 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 32dc65e9b0..4bc5d22edf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -466,70 +466,77 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { - // filter events - if (!(mouse_event.Dragging() && mouse_event.LeftIsDown()) && - !mouse_event.LeftUp() && - !mouse_event.LeftDown()) - return false; - - // must exist hover object - int hovered_id = m_parent.get_first_hover_volume_idx(); - if (hovered_id < 0) return false; - - GLVolume *gl_volume = m_parent.get_volumes().volumes[hovered_id]; - const ModelObjectPtrs &objects = wxGetApp().plater()->model().objects; - ModelVolume *act_model_volume = priv::get_model_volume(gl_volume, objects); - - // hovered object must be actual text volume - if (m_volume != act_model_volume) return false; - - const ModelVolumePtrs &volumes = m_volume->get_object()->volumes; - std::vector allowed_volumes_id; - if (volumes.size() > 1) { - allowed_volumes_id.reserve(volumes.size() - 1); - for (auto &v : volumes) { - if (v->id() == m_volume->id()) continue; - if (!v->is_model_part()) continue; - allowed_volumes_id.emplace_back(v->id().id); - } - } - - // wxCoord == int --> wx/types.h - Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); - Vec2d mouse_pos = mouse_coord.cast(); - RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); - // detect start text dragging if (mouse_event.LeftDown()) { + // must exist hover object + int hovered_id = m_parent.get_first_hover_volume_idx(); + if (hovered_id < 0) + return false; + + GLVolume *gl_volume = m_parent.get_volumes().volumes[hovered_id]; + const ModelObjectPtrs &objects = m_parent.get_model()->objects; + + // hovered object must be actual text volume + if (m_volume != priv::get_model_volume(gl_volume, objects)) + return false; + + const ModelVolumePtrs &volumes = m_volume->get_object()->volumes; + std::vector allowed_volumes_id; + if (volumes.size() > 1) { + allowed_volumes_id.reserve(volumes.size() - 1); + for (auto &v : volumes) { + if (v->id() == m_volume->id()) + continue; + if (!v->is_model_part()) + continue; + allowed_volumes_id.emplace_back(v->id().id); + } + } + RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); + // initialize raycasters - // IMPROVE: move to job, for big scene it slows down - ModelObject *act_model_object = act_model_volume->get_object(); - m_raycast_manager.actualize(act_model_object, &condition); - m_dragging_mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume); + // INFO: It could slows down for big objects + // (may be move to thread and do not show drag until it finish) + m_raycast_manager.actualize(m_volume->get_object(), &condition); + + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + Vec2d mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume); + Transform3d instance_inv = gl_volume->get_instance_transformation().get_matrix().inverse(); + m_surface_drag = SurfaceDrag{mouse_offset, instance_inv, gl_volume, condition}; + // Cancel job to prevent interuption of dragging (duplicit result) if (m_job_cancel != nullptr) m_job_cancel->store(true); - return false; + + m_parent.enable_moving(false); + m_parent.enable_picking(false); + return true; } // Dragging starts out of window - if (!m_dragging_mouse_offset.has_value()) + if (!m_surface_drag.has_value()) return false; if (mouse_event.Dragging()) { + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset; const Camera &camera = wxGetApp().plater()->get_camera(); - Vec2d offseted_mouse = mouse_pos + *m_dragging_mouse_offset; - auto hit = m_raycast_manager.unproject(offseted_mouse, camera, &condition); - if (!hit.has_value()) - return false; - TextConfiguration &tc = *m_volume->text_configuration; - // INFO: GLVolume is transformed by common movement but we need move over surface - // so hide common dragging of object - m_parent.toggle_model_objects_visibility(false, m_volume->get_object(), gl_volume->instance_idx(), m_volume); + auto hit = m_raycast_manager.unproject(offseted_mouse, camera, &m_surface_drag->condition); + if (!hit.has_value()) { + // cross hair need redraw + m_parent.set_as_dirty(); + return true; + } // Calculate temporary position Transform3d object_trmat = m_raycast_manager.get_transformation(hit->tr_key); Transform3d trmat = create_transformation_onto_surface(hit->position, hit->normal); + + TextConfiguration &tc = *m_volume->text_configuration; const FontProp& font_prop = tc.style.prop; apply_transformation(font_prop, trmat); @@ -537,49 +544,43 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (tc.fix_3mf_tr.has_value()) trmat = trmat * (*tc.fix_3mf_tr); - // temp is in world coors - m_temp_transformation = object_trmat * trmat; + // volume transfomration in world coor + Transform3d world = object_trmat * trmat; + Transform3d volume_tr = m_surface_drag->instance_inv * world; + + // Update transformation inside of instances + for (GLVolume *vol : m_parent.get_volumes().volumes) { + if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() || + vol->volume_idx() != m_surface_drag->gl_volume->volume_idx()) + continue; + vol->set_volume_transformation(volume_tr); + } // calculate scale calculate_scale(); + + m_parent.set_as_dirty(); + return true; } else if (mouse_event.LeftUp()) { - // Added because of weird case after double click into scene - // with Mesa driver OR on Linux - if (!m_temp_transformation.has_value()) return false; - - int instance_idx = m_parent.get_selection().get_instance_idx(); - const auto &instances = m_volume->get_object()->instances; - if (instance_idx < 0 || instance_idx >= instances.size()) - return false; - - // Override of common transformation after draggig by set transformation into gl_volume - Transform3d volume_trmat = - instances[instance_idx]->get_matrix().inverse() * - *m_temp_transformation; - - // ReWrite transformation inside of all instances - Geometry::Transformation transformation(volume_trmat); - for (GLVolume *vol : m_parent.get_volumes().volumes) { - if (vol->object_idx() != gl_volume->object_idx() || - vol->volume_idx() != gl_volume->volume_idx()) - continue; - vol->set_volume_transformation(transformation); - } - - m_parent.toggle_model_objects_visibility(true); - // Apply temporary position - m_temp_transformation = {}; - m_dragging_mouse_offset = {}; + // write transformation from UI into model + Selection &s = m_parent.get_selection(); + Selection::EMode mode = s.get_mode(); + s.set_mode(Selection::EMode::Volume); // Want to move with all volumes inside of instances + m_parent.do_move(L("Surface move")); + s.set_mode(mode); // revert setting of mode // Update surface by new position - if (m_volume->text_configuration->style.prop.use_surface) { - // need actual position - m_volume->set_transformation(volume_trmat); + if (m_volume->text_configuration->style.prop.use_surface) process(); - } // calculate scale calculate_scale(); + + // allow moving and picking again + m_parent.enable_moving(true); + m_parent.enable_picking(true); + m_surface_drag.reset(); + return true; } return false; } @@ -619,53 +620,10 @@ void GLGizmoEmboss::on_render() { Selection &selection = m_parent.get_selection(); if (selection.is_empty()) return; - if (m_temp_transformation.has_value()) { - // draw text volume on temporary position - GLVolume& gl_volume = *selection.get_volume(*selection.get_volume_idxs().begin()); - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - shader->start_using(); - - const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d matrix = camera.get_view_matrix() * (*m_temp_transformation); - shader->set_uniform("view_model_matrix", matrix); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - shader->set_uniform("view_normal_matrix", (Matrix3d) (matrix).matrix().block(0, 0, 3, 3).inverse().transpose()); - shader->set_uniform("emission_factor", 0.0f); - - // dragging object must be selected so draw it with correct color - //auto color = gl_volume.color; - //auto color = gl_volume.render_color; - auto color = GLVolume::SELECTED_COLOR; - // Set transparent color for NEGATIVE_VOLUME & PARAMETER_MODIFIER - bool is_transparent = m_volume->type() != ModelVolumeType::MODEL_PART; - if (is_transparent) { - color.a(0.5f); - glsafe(::glEnable(GL_BLEND)); - glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - } - - bool is_left_handed = has_reflection(*m_temp_transformation); - if (is_left_handed) - glsafe(::glFrontFace(GL_CW)); - - glsafe(::glEnable(GL_DEPTH_TEST)); - gl_volume.model.set_color(color); - gl_volume.model.render(); - glsafe(::glDisable(GL_DEPTH_TEST)); - - // set it back to pevious state - if (is_left_handed) - glsafe(::glFrontFace(GL_CCW)); - if (is_transparent) - glsafe(::glDisable(GL_BLEND)); - - shader->stop_using(); - } - // prevent get local coordinate system on multi volumes if (!selection.is_single_volume_or_modifier() && !selection.is_single_volume_instance()) return; - bool is_surface_dragging = m_temp_transformation.has_value(); + bool is_surface_dragging = m_surface_drag.has_value(); bool is_parent_dragging = m_parent.is_mouse_dragging(); // Do NOT render rotation grabbers when dragging object bool is_rotate_by_grabbers = m_dragging; @@ -737,36 +695,27 @@ static void draw_mouse_offset(const std::optional &offset) draw_list->AddLine(p1, p2, color, thickness); } #endif // SHOW_OFFSET_DURING_DRAGGING -namespace priv { -static void draw_origin(const GLCanvas3D& canvas) { - auto draw_list = ImGui::GetOverlayDrawList(); - const Selection &selection = canvas.get_selection(); - Transform3d to_world = priv::world_matrix(selection); - Vec3d volume_zero = to_world * Vec3d::Zero(); - - const Camera &camera = wxGetApp().plater()->get_camera(); - Point screen_coor = CameraUtils::project(camera, volume_zero); - ImVec2 center(screen_coor.x(), screen_coor.y()); - float radius = 16.f; - ImU32 color = ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, .75f)); - int num_segments = 0; - float thickness = 4.f; - draw_list->AddCircle(center, radius, color, num_segments, thickness); +namespace priv { +static void draw_cross_hair(const ImVec2 &position, + float radius = 16.f, + ImU32 color = ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, .75f)), + int num_segments = 0, + float thickness = 4.f); +} // namespace priv + +void priv::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, int num_segments, float thickness) +{ + auto draw_list = ImGui::GetOverlayDrawList(); + draw_list->AddCircle(position, radius, color, num_segments, thickness); auto dirs = {ImVec2{0, 1}, ImVec2{1, 0}, ImVec2{0, -1}, ImVec2{-1, 0}}; for (const ImVec2 &dir : dirs) { - ImVec2 start( - center.x + dir.x * 0.5 * radius, - center.y + dir.y * 0.5 * radius); - ImVec2 end( - center.x + dir.x * 1.5 * radius, - center.y + dir.y * 1.5 * radius); + ImVec2 start(position.x + dir.x * 0.5 * radius, position.y + dir.y * 0.5 * radius); + ImVec2 end(position.x + dir.x * 1.5 * radius, position.y + dir.y * 1.5 * radius); draw_list->AddLine(start, end, color, thickness); } } -} // namespace priv - void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) { if (!m_gui_cfg.has_value()) initialize(); @@ -784,8 +733,13 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, min_window_size); // Draw origin position of text during dragging - if (m_temp_transformation.has_value()) - priv::draw_origin(m_parent); + if (m_surface_drag.has_value()) { + ImVec2 mouse_pos = ImGui::GetMousePos(); + ImVec2 center( + mouse_pos.x + m_surface_drag->mouse_offset.x(), + mouse_pos.y + m_surface_drag->mouse_offset.y()); + priv::draw_cross_hair(center); + } #ifdef SHOW_FINE_POSITION draw_fine_position(m_parent.get_selection(), m_parent.get_canvas_size(), min_window_size); @@ -1288,9 +1242,7 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) } void GLGizmoEmboss::calculate_scale() { - Transform3d to_world = m_temp_transformation.has_value()? - *m_temp_transformation : - priv::world_matrix(m_parent.get_selection()); + Transform3d to_world = m_parent.get_selection().get_first_volume()->world_matrix(); auto to_world_linear = to_world.linear(); auto calc = [&to_world_linear](const Vec3d &axe, std::optional& scale)->bool { Vec3d axe_world = to_world_linear * axe; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 2ed6c20003..443d83d3be 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -299,16 +299,29 @@ private: // Value is set only when dragging rotation to calculate actual angle std::optional m_rotate_start_angle; - // when draging with text object hold screen offset of cursor from object center - std::optional m_dragging_mouse_offset; + // Data for drag&drop over surface with mouse + struct SurfaceDrag + { + // hold screen coor offset of cursor from object center + Vec2d mouse_offset; + + // Invers transformation of text volume instance + // Help convert world transformation to instance space + Transform3d instance_inv; + + // Dragged gl volume + GLVolume *gl_volume; + + // condition for raycaster + RaycastManager::AllowVolumes condition; + }; + // Keep data about dragging only during drag&drop + std::optional m_surface_drag; // TODO: it should be accessible by other gizmo too. // May be move to plater? RaycastManager m_raycast_manager; - // Only when drag text object it stores world position - std::optional m_temp_transformation; - // For text on scaled objects std::optional m_scale_height; std::optional m_scale_depth; From a2fb0c377fd5750cac547bebb5ed690610901040 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Feb 2023 12:38:04 +0100 Subject: [PATCH 04/64] Fix unselecting of volume after drag --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 4bc5d22edf..8f7c3de017 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -510,8 +510,8 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (m_job_cancel != nullptr) m_job_cancel->store(true); + // disable moving with object by mouse m_parent.enable_moving(false); - m_parent.enable_picking(false); return true; } @@ -563,11 +563,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) return true; } else if (mouse_event.LeftUp()) { // write transformation from UI into model - Selection &s = m_parent.get_selection(); - Selection::EMode mode = s.get_mode(); - s.set_mode(Selection::EMode::Volume); // Want to move with all volumes inside of instances m_parent.do_move(L("Surface move")); - s.set_mode(mode); // revert setting of mode // Update surface by new position if (m_volume->text_configuration->style.prop.use_surface) @@ -576,9 +572,8 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // calculate scale calculate_scale(); - // allow moving and picking again + // allow moving with object again m_parent.enable_moving(true); - m_parent.enable_picking(true); m_surface_drag.reset(); return true; } From d7bd20b957b4f62e77735a562e54ae19fbd79110 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Feb 2023 15:59:39 +0100 Subject: [PATCH 05/64] Fix drag&drop outside of screen --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 40 +++++++++++++++---------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 8f7c3de017..5d8cf47bf4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -466,6 +466,29 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { + auto do_move = [&]() { + // write transformation from UI into model + m_parent.do_move(L("Surface move")); + + // Update surface by new position + if (m_volume->text_configuration->style.prop.use_surface) + process(); + + // calculate scale + calculate_scale(); + + // allow moving with object again + m_parent.enable_moving(true); + m_surface_drag.reset(); + }; + + if (mouse_event.Moving()) { + // Fix when leave window during dragging and move cursor back + if (m_surface_drag.has_value()) + do_move(); + return false; + } + // detect start text dragging if (mouse_event.LeftDown()) { // must exist hover object @@ -562,19 +585,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) m_parent.set_as_dirty(); return true; } else if (mouse_event.LeftUp()) { - // write transformation from UI into model - m_parent.do_move(L("Surface move")); - - // Update surface by new position - if (m_volume->text_configuration->style.prop.use_surface) - process(); - - // calculate scale - calculate_scale(); - - // allow moving with object again - m_parent.enable_moving(true); - m_surface_drag.reset(); + do_move(); return true; } return false; @@ -582,9 +593,6 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) bool GLGizmoEmboss::on_mouse(const wxMouseEvent &mouse_event) { - // do not process moving event - if (mouse_event.Moving()) return false; - // not selected volume if (m_volume == nullptr || priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr || From 7ef291d052f8979ab21440c8190af0c1484f3170 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Feb 2023 16:01:20 +0100 Subject: [PATCH 06/64] Fix gui transformation when change DPI - initialize m_gui_cfg only when start draw imgui window --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 100 ++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 10 ++- 2 files changed, 67 insertions(+), 43 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 5d8cf47bf4..1c8e5a28c6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -223,9 +223,8 @@ bool priv::is_valid(ModelVolumeType volume_type){ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos) { if (!priv::is_valid(volume_type)) return; - if (!m_gui_cfg.has_value()) initialize(); - set_default_text(); m_style_manager.discard_style_changes(); + set_default_text(); GLVolume *gl_volume = priv::get_hovered_gl_volume(m_parent); DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); @@ -242,9 +241,8 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous void GLGizmoEmboss::create_volume(ModelVolumeType volume_type) { if (!priv::is_valid(volume_type)) return; - if (!m_gui_cfg.has_value()) initialize(); - set_default_text(); m_style_manager.discard_style_changes(); + set_default_text(); // select position by camera position and view direction const Selection &selection = m_parent.get_selection(); @@ -721,7 +719,14 @@ void priv::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, in void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) { - if (!m_gui_cfg.has_value()) initialize(); + // Check that DPI is same + double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); + if (m_gui_cfg.has_value() && m_gui_cfg->screen_scale != screen_scale) + m_gui_cfg.reset(); + + // Cache for gui offsets + if (!m_gui_cfg.has_value()) + initialize(screen_scale); set_volume_by_selection(); // Do not render window for not selected text volume @@ -840,8 +845,6 @@ void GLGizmoEmboss::on_set_state() m_style_manager.store_styles_to_app_config(false); remove_notification_not_valid_font(); } else if (GLGizmoBase::m_state == GLGizmoBase::On) { - if (!m_gui_cfg.has_value()) initialize(); - // to reload fonts from system, when install new one wxFontEnumerator::InvalidateCache(); @@ -861,10 +864,15 @@ void GLGizmoEmboss::on_set_state() } // change position of just opened emboss window - if (m_allow_open_near_volume) + if (m_allow_open_near_volume) { m_set_window_offset = priv::calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size()); - else - priv::change_window_position(m_set_window_offset, false); + } else { + if (m_gui_cfg.has_value()) + priv::change_window_position(m_set_window_offset, false); + else + m_set_window_offset = ImVec2(-1, -1); + } + // when open by hyperlink it needs to show up // or after key 'T' windows doesn't appear m_parent.set_as_dirty(); @@ -896,11 +904,13 @@ void GLGizmoEmboss::on_stop_dragging() } void GLGizmoEmboss::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); } -void GLGizmoEmboss::initialize() +void GLGizmoEmboss::initialize(double screen_scale) { if (m_gui_cfg.has_value()) return; - GuiCfg cfg; // initialize by default values; + GuiCfg cfg; // initialize by default values; + cfg.screen_scale = screen_scale; + float line_height = ImGui::GetTextLineHeight(); float line_height_with_spacing = ImGui::GetTextLineHeightWithSpacing(); float space = line_height_with_spacing - line_height; @@ -1147,8 +1157,9 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) // search in enumerated fonts // refresh list of installed font in the OS. - init_face_names(); + init_face_names(m_face_names); m_face_names.is_init = false; + auto cmp = [](const FaceName &fn, const wxString& face_name)->bool { return fn.wx_name < face_name; }; const std::vector &faces = m_face_names.faces; auto it = std::lower_bound(faces.begin(), faces.end(), face_name, cmp); @@ -1779,31 +1790,34 @@ bool GLGizmoEmboss::load(Facenames &facenames) { return true; } -void GLGizmoEmboss::init_face_names() { +void GLGizmoEmboss::init_truncated_names(Facenames &face_names, float max_width) +{ + for (FaceName &face : face_names.faces) { + std::string name_str(face.wx_name.ToUTF8().data()); + face.name_truncated = ImGuiWrapper::trunc(name_str, max_width); + } + face_names.has_truncated_names = true; +} + +void GLGizmoEmboss::init_face_names(Facenames &face_names) +{ Timer t("enumerate_fonts"); - if (m_face_names.is_init) return; - m_face_names.is_init = true; + if (face_names.is_init) return; + face_names.is_init = true; // to reload fonts from system, when install new one wxFontEnumerator::InvalidateCache(); - auto create_truncated_names = [&facenames = m_face_names, &width = m_gui_cfg->face_name_max_width]() { - for (FaceName &face : facenames.faces) { - std::string name_str(face.wx_name.ToUTF8().data()); - face.name_truncated = ImGuiWrapper::trunc(name_str, width); - } - }; - // try load cache // Only not OS enumerated face has hash value 0 - if (m_face_names.hash == 0) { - load(m_face_names); - create_truncated_names(); + if (face_names.hash == 0) { + load(face_names); + face_names.has_truncated_names = false; } using namespace std::chrono; steady_clock::time_point enumerate_start = steady_clock::now(); - ScopeGuard sg([&enumerate_start, &face_names = m_face_names]() { + ScopeGuard sg([&enumerate_start, &face_names = face_names]() { steady_clock::time_point enumerate_end = steady_clock::now(); long long enumerate_duration = duration_cast(enumerate_end - enumerate_start).count(); BOOST_LOG_TRIVIAL(info) << "OS enumerate " << face_names.faces.size() << " fonts " @@ -1811,25 +1825,25 @@ void GLGizmoEmboss::init_face_names() { << "= " << face_names.faces.size() + face_names.bad.size() << " fonts) " << "in " << enumerate_duration << " ms\n" << concat(face_names.bad); }); - wxArrayString facenames = wxFontEnumerator::GetFacenames(m_face_names.encoding); + wxArrayString facenames = wxFontEnumerator::GetFacenames(face_names.encoding); size_t hash = boost::hash_range(facenames.begin(), facenames.end()); // Zero value is used as uninitialized hash if (hash == 0) hash = 1; // check if it is same as last time - if (m_face_names.hash == hash) { + if (face_names.hash == hash) { // no new installed font BOOST_LOG_TRIVIAL(info) << "Same FontNames hash, cache is used. " << "For clear cache delete file: " << get_fontlist_cache_path().string(); return; } - BOOST_LOG_TRIVIAL(info) << ((m_face_names.hash == 0) ? + BOOST_LOG_TRIVIAL(info) << ((face_names.hash == 0) ? "FontName list is generate from scratch." : "Hash are different. Only previous bad fonts are used and set again as bad"); - m_face_names.hash = hash; + face_names.hash = hash; // validation lambda - auto is_valid_font = [encoding = m_face_names.encoding, bad = m_face_names.bad /*copy*/](const wxString &name) { + auto is_valid_font = [encoding = face_names.encoding, bad = face_names.bad /*copy*/](const wxString &name) { if (name.empty()) return false; // vertical font start with @, we will filter it out @@ -1856,20 +1870,20 @@ void GLGizmoEmboss::init_face_names() { return true; }; - m_face_names.faces.clear(); - m_face_names.bad.clear(); - m_face_names.faces.reserve(facenames.size()); + face_names.faces.clear(); + face_names.bad.clear(); + face_names.faces.reserve(facenames.size()); std::sort(facenames.begin(), facenames.end()); for (const wxString &name : facenames) { if (is_valid_font(name)) { - m_face_names.faces.push_back({name}); + face_names.faces.push_back({name}); }else{ - m_face_names.bad.push_back(name); + face_names.bad.push_back(name); } } - assert(std::is_sorted(m_face_names.bad.begin(), m_face_names.bad.end())); - create_truncated_names(); - store(m_face_names); + assert(std::is_sorted(face_names.bad.begin(), face_names.bad.end())); + face_names.has_truncated_names = false; + store(face_names); } // create texture for visualization font face @@ -2073,9 +2087,13 @@ void GLGizmoEmboss::draw_font_list() { bool set_selection_focus = false; if (!m_face_names.is_init) { - init_face_names(); + init_face_names(m_face_names); set_selection_focus = true; } + + if (!m_face_names.has_truncated_names) + init_truncated_names(m_face_names, m_gui_cfg->face_name_max_width); + if (m_face_names.texture_id == 0) init_font_name_texture(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 443d83d3be..50bec2a287 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -82,7 +82,7 @@ protected: std::string get_gizmo_leaving_text() const override { return _u8L("Leave emboss gizmo"); } std::string get_action_snapshot_name() override { return _u8L("Embossing actions"); } private: - void initialize(); + void initialize(double screen_scale); static EmbossStyles create_default_styles(); // localized default text void set_default_text(); @@ -118,7 +118,6 @@ private: void draw_advanced(); bool select_facename(const wxString& facename); - void init_face_names(); void do_translate(const Vec3d& relative_move); void do_rotate(float relative_z_angle); @@ -167,6 +166,9 @@ private: // so the change takes effect. (info by GLGizmoFdmSupports.hpp) struct GuiCfg { + // Detect invalid config values when change monitor DPI + double screen_scale; + // Zero means it is calculated in init function ImVec2 minimal_window_size = ImVec2(0, 0); ImVec2 minimal_window_size_with_advance = ImVec2(0, 0); @@ -244,6 +246,8 @@ private: // true .. already enumerated(During opened combo box) bool is_init = false; + bool has_truncated_names = false; + // data of can_load() faces std::vector faces = {}; // Sorter set of Non valid face names in OS @@ -277,6 +281,8 @@ private: static bool store(const Facenames &facenames); static bool load(Facenames &facenames); + static void init_face_names(Facenames &facenames); + static void init_truncated_names(Facenames &face_names, float max_width); // Text to emboss std::string m_text; From f9b39c0f6f76e57e2e2f88ace3058c244581cc47 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Feb 2023 17:56:56 +0100 Subject: [PATCH 07/64] Re-Initialize Emboss GUI configuration on change monitor scale --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 46 ++++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 5 ++- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 1c8e5a28c6..41ee661099 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -608,6 +608,12 @@ bool GLGizmoEmboss::on_init() ColorRGBA gray_color(.6f, .6f, .6f, .3f); m_rotate_gizmo.set_highlight_color(gray_color); m_shortcut_key = WXK_CONTROL_T; + + // initialize text styles + m_style_manager.init(wxGetApp().app_config); + + // Set rotation gizmo upwardrotate + m_rotate_gizmo.set_angle(PI / 2); return true; } @@ -720,13 +726,20 @@ void priv::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, in void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) { // Check that DPI is same - double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); - if (m_gui_cfg.has_value() && m_gui_cfg->screen_scale != screen_scale) - m_gui_cfg.reset(); + GUI_App& app = wxGetApp(); + double screen_scale = wxDisplay(app.plater()).GetScaleFactor(); + if (!m_gui_cfg.has_value() || + m_gui_cfg->screen_scale != screen_scale) { + // Create cache for gui offsets + GuiCfg cfg = create_gui_configuration(); + cfg.screen_scale = screen_scale; + m_gui_cfg.emplace(std::move(cfg)); + // set position near toolbar + m_set_window_offset = ImVec2(-1.f, -1.f); - // Cache for gui offsets - if (!m_gui_cfg.has_value()) - initialize(screen_scale); + // change resolution regenerate icons + init_icons(); + } set_volume_by_selection(); // Do not render window for not selected text volume @@ -904,12 +917,9 @@ void GLGizmoEmboss::on_stop_dragging() } void GLGizmoEmboss::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); } -void GLGizmoEmboss::initialize(double screen_scale) +GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() { - if (m_gui_cfg.has_value()) return; - - GuiCfg cfg; // initialize by default values; - cfg.screen_scale = screen_scale; + GuiCfg cfg; // initialize by default values; float line_height = ImGui::GetTextLineHeight(); float line_height_with_spacing = ImGui::GetTextLineHeightWithSpacing(); @@ -996,17 +1006,7 @@ void GLGizmoEmboss::initialize(double screen_scale) int max_style_image_height = 1.5 * input_height; cfg.max_style_image_size = Vec2i(max_style_image_width, max_style_image_height); cfg.face_name_size.y() = line_height_with_spacing; - - m_gui_cfg.emplace(std::move(cfg)); - - init_icons(); - - // initialize text styles - m_style_manager.init(wxGetApp().app_config); - set_default_text(); - - // Set rotation gizmo upwardrotate - m_rotate_gizmo.set_angle(PI/2); + return cfg; } EmbossStyles GLGizmoEmboss::create_default_styles() @@ -1014,7 +1014,7 @@ EmbossStyles GLGizmoEmboss::create_default_styles() wxFont wx_font_normal = *wxNORMAL_FONT; wxFont wx_font_small = *wxSMALL_FONT; -#ifdef __APPLE__ +#ifdef __APPLE__= wx_font_normal.SetFaceName("Helvetica"); wx_font_small.SetFaceName("Helvetica"); #endif // __APPLE__ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 50bec2a287..77b86b8b2f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -82,7 +82,6 @@ protected: std::string get_gizmo_leaving_text() const override { return _u8L("Leave emboss gizmo"); } std::string get_action_snapshot_name() override { return _u8L("Embossing actions"); } private: - void initialize(double screen_scale); static EmbossStyles create_default_styles(); // localized default text void set_default_text(); @@ -213,10 +212,10 @@ private: std::string collection; }; Translations translations; - - GuiCfg() = default; }; std::optional m_gui_cfg; + static GuiCfg create_gui_configuration(); + bool m_is_advanced_edit_style = false; // when true window will appear near to text volume when open From 5cc1e4dac0c798ab87f4d93f004c07f3d602dc03 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Feb 2023 07:56:38 +0100 Subject: [PATCH 08/64] add missing include --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 41ee661099..2876d438c5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -34,6 +34,7 @@ #include #include #include +#include // detection of change DPI #include From 21ff6bc048454c1075f3aa4eb74688e99d6e1c3a Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Feb 2023 09:42:30 +0100 Subject: [PATCH 09/64] Fix emboss window position when change slicer size --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 65 ++++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 2 + 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 2876d438c5..95736e271a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -726,23 +726,7 @@ void priv::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, in void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) { - // Check that DPI is same - GUI_App& app = wxGetApp(); - double screen_scale = wxDisplay(app.plater()).GetScaleFactor(); - if (!m_gui_cfg.has_value() || - m_gui_cfg->screen_scale != screen_scale) { - // Create cache for gui offsets - GuiCfg cfg = create_gui_configuration(); - cfg.screen_scale = screen_scale; - m_gui_cfg.emplace(std::move(cfg)); - // set position near toolbar - m_set_window_offset = ImVec2(-1.f, -1.f); - - // change resolution regenerate icons - init_icons(); - } set_volume_by_selection(); - // Do not render window for not selected text volume if (m_volume == nullptr || priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr || @@ -751,6 +735,26 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) return; } + // Configuration creation + double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); + float main_toolbar_height = m_parent.get_main_toolbar_height(); + if (!m_gui_cfg.has_value() || // Exist configuration - first run + m_gui_cfg->screen_scale != screen_scale || // change of DPI + m_gui_cfg->main_toolbar_height != main_toolbar_height // change size of view port + ) { + // Create cache for gui offsets + GuiCfg cfg = create_gui_configuration(); + cfg.screen_scale = screen_scale; + cfg.main_toolbar_height = main_toolbar_height; + m_gui_cfg.emplace(std::move(cfg)); + // set position near toolbar + m_set_window_offset = ImVec2(-1.f, -1.f); + + // change resolution regenerate icons + init_icons(); + m_style_manager.clear_imgui_font(); + } + const ImVec2 &min_window_size = get_minimal_window_size(); ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, min_window_size); @@ -925,10 +929,11 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() float line_height = ImGui::GetTextLineHeight(); float line_height_with_spacing = ImGui::GetTextLineHeightWithSpacing(); float space = line_height_with_spacing - line_height; + const ImGuiStyle &style = ImGui::GetStyle(); cfg.max_style_name_width = ImGui::CalcTextSize("Maximal font name, extended").x; - cfg.icon_width = std::ceil(line_height); + cfg.icon_width = static_cast(std::ceil(line_height)); // make size pair number if (cfg.icon_width % 2 != 0) ++cfg.icon_width; @@ -946,8 +951,8 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() ImGui::CalcTextSize(tr.font.c_str()).x, ImGui::CalcTextSize(tr.size.c_str()).x, ImGui::CalcTextSize(tr.depth.c_str()).x}); - cfg.input_offset = max_text_width - + 3 * space + ImGui::GetTreeNodeToLabelSpacing(); + cfg.indent = static_cast(cfg.icon_width); + cfg.input_offset = style.WindowPadding.x + cfg.indent + max_text_width + space; tr.use_surface = _u8L("Use surface"); tr.char_gap = _u8L("Char gap"); @@ -967,10 +972,9 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() ImGui::CalcTextSize(tr.angle.c_str()).x, ImGui::CalcTextSize(tr.collection.c_str()).x }); cfg.advanced_input_offset = max_advanced_text_width - + 3 * space + ImGui::GetTreeNodeToLabelSpacing(); + + 3 * space + cfg.indent; // calculate window size - const ImGuiStyle &style = ImGui::GetStyle(); float window_title = line_height + 2*style.FramePadding.y + 2 * style.WindowTitleAlign.y; float input_height = line_height_with_spacing + 2*style.FramePadding.y; float tree_header = line_height_with_spacing; @@ -1495,12 +1499,13 @@ void GLGizmoEmboss::draw_window() ScopeGuard unknown_font_sc([&]() { m_imgui->disabled_end(); }); - draw_text_input(); m_imgui->disabled_begin(!is_active_font); - ImGui::TreePush(); + + ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, m_gui_cfg->indent); + ImGui::Indent(); draw_style_edit(); - ImGui::TreePop(); + ImGui::Unindent(); // close advanced style property when unknown font is selected if (m_is_unknown_font && m_is_advanced_edit_style) @@ -1516,6 +1521,8 @@ void GLGizmoEmboss::draw_window() } else if (m_is_advanced_edit_style) set_minimal_window_size(false); + ImGui::PopStyleVar(); // ImGuiStyleVar_IndentSpacing + ImGui::Separator(); draw_style_list(); @@ -1590,7 +1597,9 @@ void GLGizmoEmboss::draw_text_input() ImFont *imgui_font = m_style_manager.get_imgui_font(); if (imgui_font == nullptr) { // try create new imgui font - m_style_manager.create_imgui_font(create_range_text_prep(), scale); + double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); + double imgui_scale = scale * screen_scale; + m_style_manager.create_imgui_font(create_range_text_prep(), imgui_scale); imgui_font = m_style_manager.get_imgui_font(); } bool exist_font = @@ -1680,10 +1689,8 @@ void GLGizmoEmboss::draw_text_input() // IMPROVE: only extend not clear // Extend font ranges if (!range_text.empty() && - !m_imgui->contain_all_glyphs(imgui_font, range_text) ) { - m_style_manager.clear_imgui_font(); - m_style_manager.create_imgui_font(range_text, scale); - } + !m_imgui->contain_all_glyphs(imgui_font, range_text) ) + m_style_manager.clear_imgui_font(); } #include diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 77b86b8b2f..0cfe34508e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -167,6 +167,7 @@ private: { // Detect invalid config values when change monitor DPI double screen_scale; + float main_toolbar_height; // Zero means it is calculated in init function ImVec2 minimal_window_size = ImVec2(0, 0); @@ -181,6 +182,7 @@ private: // maximal width and height of style image Vec2i max_style_image_size = Vec2i(0, 0); + float indent = 0.f; float input_offset = 0.f; float advanced_input_offset = 0.f; From d5686a0ecc18cf3acf495dff8649bda38042afb7 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Feb 2023 09:50:30 +0100 Subject: [PATCH 10/64] Fix typo --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 95736e271a..d72fb5dc25 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -1019,7 +1019,7 @@ EmbossStyles GLGizmoEmboss::create_default_styles() wxFont wx_font_normal = *wxNORMAL_FONT; wxFont wx_font_small = *wxSMALL_FONT; -#ifdef __APPLE__= +#ifdef __APPLE__ wx_font_normal.SetFaceName("Helvetica"); wx_font_small.SetFaceName("Helvetica"); #endif // __APPLE__ From 4b31db5e66af4fc525869b80ad6641a5820b6511 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Feb 2023 12:36:01 +0100 Subject: [PATCH 11/64] Fix search in font list --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 77 +++++++++++++------------ src/slic3r/GUI/ImGuiWrapper.cpp | 4 ++ src/slic3r/GUI/ImGuiWrapper.hpp | 6 ++ 3 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index d72fb5dc25..3a7841f96d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -1080,7 +1080,6 @@ EmbossStyles GLGizmoEmboss::create_default_styles() void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); } -#include "imgui/imgui_internal.h" // to unfocus input --> ClearActiveID void GLGizmoEmboss::set_volume_by_selection() { ModelVolume *vol = priv::get_selected_volume(m_parent.get_selection()); @@ -1094,47 +1093,12 @@ void GLGizmoEmboss::set_volume_by_selection() // Do not use focused input value when switch volume(it must swith value) if (m_volume != nullptr && m_volume != vol) // when update volume it changed id BUT not pointer - ImGui::ClearActiveID(); + ImGuiWrapper::left_inputs(); // is select embossed volume? set_volume(vol); } -// Need internals to get window -void priv::change_window_position(std::optional& output_window_offset, bool try_to_fix) { - const char* name = "Emboss"; - ImGuiWindow *window = ImGui::FindWindowByName(name); - // is window just created - if (window == NULL) - return; - - // position of window on screen - ImVec2 position = window->Pos; - ImVec2 size = window->SizeFull; - - // screen size - ImVec2 screen = ImGui::GetMainViewport()->Size; - - if (position.x < 0) { - if (position.y < 0) - output_window_offset = ImVec2(0, 0); - else - output_window_offset = ImVec2(0, position.y); - } else if (position.y < 0) { - output_window_offset = ImVec2(position.x, 0); - } else if (screen.x < (position.x + size.x)) { - if (screen.y < (position.y + size.y)) - output_window_offset = ImVec2(screen.x - size.x, screen.y - size.y); - else - output_window_offset = ImVec2(screen.x - size.x, position.y); - } else if (screen.y < (position.y + size.y)) { - output_window_offset = ImVec2(position.x, screen.y - size.y); - } - - if (!try_to_fix && output_window_offset.has_value()) - output_window_offset = ImVec2(-1, -1); // Cannot -} - bool GLGizmoEmboss::set_volume(ModelVolume *volume) { if (volume == nullptr) { @@ -2152,6 +2116,9 @@ void GLGizmoEmboss::draw_font_list() face.cancel->store(true); glsafe(::glDeleteTextures(1, &m_face_names.texture_id)); m_face_names.texture_id = 0; + + // Remove value from search input + ImGuiWrapper::left_inputs(); } // delete unloadable face name when try to use @@ -3923,5 +3890,41 @@ void priv::find_closest_volume(const Selection &selection, } } +// Need internals to get window +#include "imgui/imgui_internal.h" +void priv::change_window_position(std::optional& output_window_offset, bool try_to_fix) { + const char* name = "Emboss"; + ImGuiWindow *window = ImGui::FindWindowByName(name); + // is window just created + if (window == NULL) + return; + + // position of window on screen + ImVec2 position = window->Pos; + ImVec2 size = window->SizeFull; + + // screen size + ImVec2 screen = ImGui::GetMainViewport()->Size; + + if (position.x < 0) { + if (position.y < 0) + output_window_offset = ImVec2(0, 0); + else + output_window_offset = ImVec2(0, position.y); + } else if (position.y < 0) { + output_window_offset = ImVec2(position.x, 0); + } else if (screen.x < (position.x + size.x)) { + if (screen.y < (position.y + size.y)) + output_window_offset = ImVec2(screen.x - size.x, screen.y - size.y); + else + output_window_offset = ImVec2(screen.x - size.x, position.y); + } else if (screen.y < (position.y + size.y)) { + output_window_offset = ImVec2(position.x, screen.y - size.y); + } + + if (!try_to_fix && output_window_offset.has_value()) + output_window_offset = ImVec2(-1, -1); // Cannot +} + // any existing icon filename to not influence GUI const std::string GLGizmoEmboss::M_ICON_FILENAME = "cut.svg"; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 20913f66db..8bdecc5ef3 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1360,6 +1360,10 @@ bool ImGuiWrapper::slider_optional_int(const char *label, } else return false; } +void ImGuiWrapper::left_inputs() { + ImGui::ClearActiveID(); +} + std::string ImGuiWrapper::trunc(const std::string &text, float width, const char * tail) diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 3365954bab..d088dad2f0 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -146,6 +146,12 @@ public: // Extended function ImGuiWrapper::slider_float to work with std::optional, when value == def_val than optional release its value bool slider_optional_int(const char* label, std::optional &v, int v_min, int v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true, int def_val = 0); + /// + /// Use ImGui internals to unactivate (lose focus) in input. + /// When input is activ it can't change value by application. + /// + static void left_inputs(); + /// /// Truncate text by ImGui draw function to specific width /// NOTE 1: ImGui must be initialized From 66be5faedd8d609a285924be2d533c22c8374746 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Feb 2023 14:42:29 +0100 Subject: [PATCH 12/64] Move text only above selected instance. --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 12 +++- src/slic3r/Utils/RaycastManager.cpp | 93 +++++++++++++++++++------ src/slic3r/Utils/RaycastManager.hpp | 1 + 3 files changed, 83 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 3a7841f96d..acf29d6530 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -490,6 +490,10 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // detect start text dragging if (mouse_event.LeftDown()) { + // exist selected volume? + if (m_volume == nullptr) + return false; + // must exist hover object int hovered_id = m_parent.get_first_hover_volume_idx(); if (hovered_id < 0) @@ -502,6 +506,12 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (m_volume != priv::get_model_volume(gl_volume, objects)) return false; + const ModelInstancePtrs instances = m_volume->get_object()->instances; + int instance_id = gl_volume->instance_idx(); + if (instance_id < 0 || static_cast(instance_id) >= instances.size()) + return false; // should not happen + const ModelInstance *instance = instances[instance_id]; + const ModelVolumePtrs &volumes = m_volume->get_object()->volumes; std::vector allowed_volumes_id; if (volumes.size() > 1) { @@ -519,7 +529,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // initialize raycasters // INFO: It could slows down for big objects // (may be move to thread and do not show drag until it finish) - m_raycast_manager.actualize(m_volume->get_object(), &condition); + m_raycast_manager.actualize(instance, &condition); // wxCoord == int --> wx/types.h Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 4132fd647f..12fc3dfeda 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -6,34 +6,53 @@ #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/Camera.hpp" -using namespace Slic3r::GUI; +using namespace Slic3r::GUI; -void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) +namespace priv { + +using namespace Slic3r; +// copied from private part of RaycastManager.hpp +using Raycaster = std::pair >; +// ModelVolume.id + +using Raycasters = std::vector; + +static void actualize(Raycasters &casters, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip) { // check if volume was removed - std::vector removed_casters(m_raycasters.size(), {true}); - // check if inscance was removed - std::vector removed_transf(m_transformations.size(), {true}); - + std::vector removed_casters(casters.size(), {true}); // actualize MeshRaycaster - for (const ModelVolume *volume : object->volumes) { + for (const ModelVolume *volume : volumes) { size_t oid = volume->id().id; - if (skip != nullptr && skip->skip(oid)) + if (skip != nullptr && skip->skip(oid)) continue; - auto item = std::find_if(m_raycasters.begin(), m_raycasters.end(), - [oid](const RaycastManager::Raycaster &it)->bool { - return oid == it.first; - }); - if (item == m_raycasters.end()) { + auto item = std::find_if(casters.begin(), casters.end(), + [oid](const Raycaster &it) -> bool { return oid == it.first; }); + if (item == casters.end()) { // add new raycaster auto raycaster = std::make_unique(volume->get_mesh_shared_ptr()); - m_raycasters.emplace_back(std::make_pair(oid, std::move(raycaster))); + casters.emplace_back(std::make_pair(oid, std::move(raycaster))); } else { - size_t index = item - m_raycasters.begin(); + size_t index = item - casters.begin(); removed_casters[index] = false; } } + + // clean other raycasters + for (int i = removed_casters.size() - 1; i >= 0; --i) + if (removed_casters[i]) + casters.erase(casters.begin() + i); +} +} +void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) +{ + // actualize MeshRaycaster + priv::actualize(m_raycasters, object->volumes, skip); + + // check if inscance was removed + std::vector removed_transf(m_transformations.size(), {true}); + // actualize transformation matrices for (const ModelVolume *volume : object->volumes) { if (skip != nullptr && skip->skip(volume->id().id)) continue; @@ -41,8 +60,6 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) for (const ModelInstance *instance : object->instances) { const Transform3d &instrance_tr = instance->get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - // TODO: add SLA shift Z - // transformation.translation()(2) += m_sla_shift_z; TrKey tr_key = std::make_pair(instance->id().id, volume->id().id); auto item = std::find_if(m_transformations.begin(), m_transformations.end(), @@ -62,17 +79,49 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) } } - // clean other raycasters - for (int i = removed_casters.size() - 1; i >= 0; --i) - if (removed_casters[i]) - m_raycasters.erase(m_raycasters.begin() + i); - // clean other transformation for (int i = removed_transf.size() - 1; i >= 0; --i) if (removed_transf[i]) m_transformations.erase(m_transformations.begin() + i); } +void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) { + const ModelVolumePtrs &volumes = instance->get_object()->volumes; + + // actualize MeshRaycaster + priv::actualize(m_raycasters, volumes, skip); + + // check if inscance was removed + std::vector removed_transf(m_transformations.size(), {true}); + + // actualize transformation matrices + for (const ModelVolume *volume : volumes) { + if (skip != nullptr && skip->skip(volume->id().id)) + continue; + const Transform3d &volume_tr = volume->get_matrix(); + const Transform3d &instrance_tr = instance->get_matrix(); + Transform3d transformation = instrance_tr * volume_tr; + TrKey tr_key = std::make_pair(instance->id().id, volume->id().id); + auto item = std::find_if(m_transformations.begin(), m_transformations.end(), + [&tr_key](const TrItem &it) -> bool { return it.first == tr_key; }); + if (item != m_transformations.end()) { + // actualize transformation all the time + item->second = transformation; + size_t index = item - m_transformations.begin(); + removed_transf[index] = false; + } else { + // add new transformation + m_transformations.emplace_back(std::make_pair(tr_key, transformation)); + } + } + + // clean other transformation + for (int i = removed_transf.size() - 1; i >= 0; --i) + if (removed_transf[i]) + m_transformations.erase(m_transformations.begin() + i); +} + + std::optional RaycastManager::unproject( const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const { diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index a185f63077..94da384d2d 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -51,6 +51,7 @@ public: /// Model representation /// Condifiton for skip actualization void actualize(const ModelObject *object, const ISkip *skip = nullptr); + void actualize(const ModelInstance *instance, const ISkip *skip = nullptr); // TODO: it is more general object move outside of this class struct SurfacePoint From d63c954247d3552425fbe17ad13c53983d467532 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 3 Feb 2023 07:51:12 +0100 Subject: [PATCH 13/64] Add another one clear of search string for font name --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index acf29d6530..f1bffa8c3a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2037,8 +2037,14 @@ void GLGizmoEmboss::draw_font_list() // change color of hint to normal text bool is_popup_open = ImGui::IsPopupOpen(popup_id); - if (!is_popup_open) + if (!is_popup_open) { ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + + // Fix clearance of search input, + // Sometime happens that search text not disapear after font select + m_face_names.search.clear(); + } + if (ImGui::InputTextWithHint(input_id, selected, &m_face_names.search, input_flags)) { // update filtration result m_face_names.hide = std::vector(m_face_names.faces.size(), {false}); @@ -2052,7 +2058,7 @@ void GLGizmoEmboss::draw_font_list() m_face_names.hide[index] = !start_with; } } - if (!is_popup_open) + if (!is_popup_open) ImGui::PopStyleColor(); // revert changes for hint color const bool is_input_text_active = ImGui::IsItemActive(); @@ -2118,7 +2124,6 @@ void GLGizmoEmboss::draw_font_list() // Just one after close combo box // free texture and set id to zero m_face_names.is_init = false; - m_face_names.search.clear(); m_face_names.hide.clear(); // cancel all process for generation of texture for (FaceName &face : m_face_names.faces) @@ -2129,6 +2134,7 @@ void GLGizmoEmboss::draw_font_list() // Remove value from search input ImGuiWrapper::left_inputs(); + m_face_names.search.clear(); } // delete unloadable face name when try to use From d6eea1de429d59b77553d0e5a30ec5e9ad708ad4 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 6 Feb 2023 10:26:00 +0100 Subject: [PATCH 14/64] Relative move over surface - Not Work --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 123 ++++++++++++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 13 +++ 2 files changed, 116 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index f1bffa8c3a..b88705ad36 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -465,7 +465,9 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { - auto do_move = [&]() { + // Fix when leave window during dragging + // Fix when click right button + if (m_surface_drag.has_value() && !mouse_event.Dragging()) { // write transformation from UI into model m_parent.do_move(L("Surface move")); @@ -479,15 +481,15 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // allow moving with object again m_parent.enable_moving(true); m_surface_drag.reset(); - }; - if (mouse_event.Moving()) { - // Fix when leave window during dragging and move cursor back - if (m_surface_drag.has_value()) - do_move(); - return false; + // only left up is correct + // otherwise it is fix state and return false + return mouse_event.LeftUp(); } + if (mouse_event.Moving()) + return false; + // detect start text dragging if (mouse_event.LeftDown()) { // exist selected volume? @@ -536,7 +538,8 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2d mouse_pos = mouse_coord.cast(); Vec2d mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume); Transform3d instance_inv = gl_volume->get_instance_transformation().get_matrix().inverse(); - m_surface_drag = SurfaceDrag{mouse_offset, instance_inv, gl_volume, condition}; + Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix(); + m_surface_drag = SurfaceDrag{mouse_offset, instance_inv, volume_tr, gl_volume, condition}; // Cancel job to prevent interuption of dragging (duplicit result) if (m_job_cancel != nullptr) @@ -568,19 +571,85 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Transform3d object_trmat = m_raycast_manager.get_transformation(hit->tr_key); Transform3d trmat = create_transformation_onto_surface(hit->position, hit->normal); - TextConfiguration &tc = *m_volume->text_configuration; - const FontProp& font_prop = tc.style.prop; - apply_transformation(font_prop, trmat); + // !!!!!!!!!!!!!!!! USE DIRECT hit position and normal - // fix baked transformation from .3mf store process - if (tc.fix_3mf_tr.has_value()) - trmat = trmat * (*tc.fix_3mf_tr); + //TextConfiguration &tc = *m_volume->text_configuration; + //const FontProp& font_prop = tc.style.prop; + //apply_transformation(font_prop, trmat); + + //// fix baked transformation from .3mf store process + //if (tc.fix_3mf_tr.has_value()) + // trmat = trmat * (*tc.fix_3mf_tr); // volume transfomration in world coor - Transform3d world = object_trmat * trmat; - Transform3d volume_tr = m_surface_drag->instance_inv * world; + Transform3d wanted_world = object_trmat * trmat; + Transform3d wanted_volume = m_surface_drag->instance_inv * wanted_world; - // Update transformation inside of instances + // Calculate offset inside instance: + // transformation from curret to wanted position + Vec3d from_position = m_surface_drag->volume_tr * Vec3d::Zero(); + Vec3d to_position = wanted_volume * Vec3d::Zero(); + + Transform3d hit_to_instance = object_trmat * m_surface_drag->instance_inv; + Vec3d to_position2 = hit_to_instance * hit->position.cast(); + Vec3d z_t2 = hit_to_instance.linear() * hit->normal.cast(); + + Vec3d offset_instance = to_position - from_position; + Transform3d trnsl{Eigen::Translation(offset_instance)}; + + // current transformation from volume to world + Transform3d current_world = m_surface_drag->instance_inv.inverse() * m_surface_drag->volume_tr; + + auto current_world_linear = current_world.linear(); + auto wanted_world_linear = wanted_world.linear(); + + // Calculate rotation of Z-vectors from current to wanted position + Transform3d rot = Transform3d::Identity(); + // Transformed unit vector Z direction (f)rom, (t)o + Vec3d z_f = m_surface_drag->volume_tr.linear() * Vec3d::UnitZ(); + Vec3d z_t = wanted_volume.linear() * Vec3d::UnitZ(); + z_f.normalize(); + z_t.normalize(); + double cos_angle = z_t.dot(z_f); + + // Calculate only when angle is not zero + if (cos_angle < 1. && cos_angle > -1.) { + m_surface_drag->from = from_position; + m_surface_drag->to = to_position; + m_surface_drag->from_dir = z_f; + m_surface_drag->to_dir = z_t; + + m_surface_drag->f_tr = current_world; + m_surface_drag->t_tr = wanted_world; + + // TODO: solve opposit direction of z_t and z_f (a.k.a. angle 180 DEG) + // if (cos_angle == 0.) {} + + // Calculate rotation axe from current to wanted inside instance + Vec3d axe = z_t.cross(z_f); + axe.normalize(); + double angle = acos(cos_angle); + rot = Eigen::AngleAxis(-angle, axe); + } + + // Calculate scale in world + auto calc_scale = [¤t_world_linear, wanted_world_linear](const Vec3d &dir) -> double { + Vec3d current = current_world_linear * dir; + Vec3d wanted = wanted_world_linear * dir; + double current_sq = current.squaredNorm(); + double wanted_sq = wanted.squaredNorm(); + return sqrt(wanted_sq / current_sq); + }; + double y_scale = calc_scale(Vec3d::UnitY()); + double z_scale = calc_scale(Vec3d::UnitZ()); + Transform3d scale(Eigen::Scaling(1., y_scale, z_scale)); + + Transform3d volume_tr = trnsl * m_surface_drag->volume_tr * rot; + + assert(volume_tr.matrix()(0,0) == volume_tr.matrix()(0,0)); // Check valid transformation not a NAN + Transform3d volume_tr2 = m_surface_drag->instance_inv * wanted_world; + + // Update transformation inside of instances for (GLVolume *vol : m_parent.get_volumes().volumes) { if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() || vol->volume_idx() != m_surface_drag->gl_volume->volume_idx()) @@ -593,9 +662,6 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) m_parent.set_as_dirty(); return true; - } else if (mouse_event.LeftUp()) { - do_move(); - return true; } return false; } @@ -631,6 +697,23 @@ bool GLGizmoEmboss::on_init() std::string GLGizmoEmboss::on_get_name() const { return _u8L("Emboss"); } void GLGizmoEmboss::on_render() { + // Render debug view to surface move + if (m_surface_drag.has_value()) { + auto glvol = priv::get_gl_volume(m_parent.get_selection()); + auto tr = glvol->get_instance_transformation().get_matrix(); + CoordAxes from; + from.set_origin(m_surface_drag->from); + //from.render(tr, 2.); + + CoordAxes to; + to.set_origin(m_surface_drag->to); + //to.render(tr, 2.); + + CoordAxes axe; + axe.render(m_surface_drag->f_tr); + axe.render(m_surface_drag->t_tr); + } + // no volume selected if (m_volume == nullptr || priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 0cfe34508e..610d10e720 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -316,11 +316,24 @@ private: // Help convert world transformation to instance space Transform3d instance_inv; + // Start dragging volume transformation + Transform3d volume_tr; + // Dragged gl volume GLVolume *gl_volume; // condition for raycaster RaycastManager::AllowVolumes condition; + + // Visuzalization + Vec3d from = Vec3d::Zero(); + Vec3d to = Vec3d::Zero(); + + Vec3d from_dir = Vec3d::UnitZ(); + Vec3d to_dir = Vec3d::UnitZ(); + + Transform3d f_tr = Transform3d::Identity(); + Transform3d t_tr = Transform3d::Identity(); }; // Keep data about dragging only during drag&drop std::optional m_surface_drag; From c1b480e57f8aafbcb7a68d6e71064c01265b94d0 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 7 Feb 2023 12:10:40 +0100 Subject: [PATCH 15/64] On rayCast miss add part simillar way as right panel do. Fix: https://github.com/prusa3d/PrusaSlicer/issues/9611 --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 28 ++++++++++++++++--------- src/slic3r/Utils/RaycastManager.hpp | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index b88705ad36..b7081b2104 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -229,13 +229,17 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous GLVolume *gl_volume = priv::get_hovered_gl_volume(m_parent); DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); - // 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)) - // object found - return; - - // object is not under mouse position soo create object on plater - priv::start_create_object_job(emboss_data, mouse_pos); + if (gl_volume != nullptr) { + // 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)) { + // 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); + } + } else { + // object is not under mouse position soo create object on plater + priv::start_create_object_job(emboss_data, mouse_pos); + } } // Designed for create volume without information of mouse in scene @@ -266,8 +270,9 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type) const GLVolume *vol = nullptr; const Camera &camera = wxGetApp().plater()->get_camera(); priv::find_closest_volume(selection, screen_center, camera, objects, &coor, &vol); - if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager)) { - assert(vol != nullptr); + 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)) { // in centroid of convex hull is not hit with object // soo create transfomation on border of object @@ -3924,7 +3929,9 @@ GLVolume * priv::get_hovered_gl_volume(const 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) { + assert(gl_volume != nullptr); if (gl_volume == nullptr) return false; + Plater *plater = wxGetApp().plater(); const ModelObjectPtrs &objects = plater->model().objects; @@ -3941,7 +3948,8 @@ bool priv::start_create_volume_on_surface_job( // context menu for add text could be open only by right click on an // object. After right click, object is selected and object_idx is set // also hit must exist. But there is options to add text by object list - if (!hit.has_value()) return false; + if (!hit.has_value()) + return false; Transform3d hit_object_trmat = raycaster.get_transformation(hit->tr_key); Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix(); diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 94da384d2d..5451c4e92e 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -124,7 +124,7 @@ public: std::optional closest(const Vec3d &point, const ISkip *skip = nullptr) const; /// - /// Getter on transformation + /// Getter on transformation from hitted volume to world /// /// Define transformation /// Transformation for key From fb488e745b66e1805f937a9b8aff9faccfd7dc4f Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 7 Feb 2023 14:58:04 +0100 Subject: [PATCH 16/64] Allowe cast on surface of corrupted meshes. Skip filtration --> allowe move over surface under the bed --- src/slic3r/Utils/RaycastManager.cpp | 38 ++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 12fc3dfeda..be11c7f894 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -121,6 +121,33 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) m_transformations.erase(m_transformations.begin() + i); } +#include "slic3r/GUI/CameraUtils.hpp" +namespace priv { + +// Copy functionality from MeshRaycaster::unproject_on_mesh without filtering +static std::optional unproject_on_mesh(const MeshRaycaster &raycaster, + const Vec2d &mouse_pos, const Transform3d &transformation, const Camera &camera) { + + Vec3d point; + Vec3d direction; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + Transform3d inv = transformation.inverse(); + point = inv*point; + direction = inv.linear()*direction; + + const AABBMesh &aabb_mesh = raycaster.get_aabb_mesh(); + std::vector hits = aabb_mesh.query_ray_hits(point, direction); + + if (hits.empty()) + return {}; // no intersection found + + const AABBMesh::hit_result &hit = hits.front(); + return RaycastManager::SurfacePoint( + hit.position().cast(), + hit.normal().cast() + ); +} +} // namespace priv std::optional RaycastManager::unproject( const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const @@ -137,12 +164,11 @@ std::optional RaycastManager::unproject( -> bool { return volume_id == it.first; }); if (raycaster_it == m_raycasters.end()) continue; const MeshRaycaster &raycaster = *(raycaster_it->second); - SurfacePoint surface_point; - bool success = raycaster.unproject_on_mesh( - mouse_pos, transformation, camera, - surface_point.position, surface_point.normal); - if (!success) continue; - + std::optional surface_point_opt = priv::unproject_on_mesh( + raycaster, mouse_pos, transformation, camera); + if (!surface_point_opt.has_value()) + continue; + const SurfacePoint &surface_point = *surface_point_opt; Vec3d act_hit_tr = transformation * surface_point.position.cast(); double squared_distance = (camera.get_position() - act_hit_tr).squaredNorm(); if (closest.has_value() && From 040f721873aefaa2707f8f61f6fbf83e625dd97d Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 7 Feb 2023 15:23:08 +0100 Subject: [PATCH 17/64] Fix translation mentioned by issue: https://github.com/prusa3d/PrusaSlicer/issues/9583 --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index b7081b2104..abdeec66e4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -1703,9 +1703,9 @@ void GLGizmoEmboss::draw_text_input() auto &ff = m_style_manager.get_font_file_with_cache(); float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file, scale); if (imgui_size > StyleManager::max_imgui_font_size) - append_warning(_u8L("To tall"), _u8L("Diminished font height inside text input.")); + append_warning(_u8L("Too tall"), _u8L("Diminished font height inside text input.")); if (imgui_size < StyleManager::min_imgui_font_size) - append_warning(_u8L("To small"), _u8L("Enlarged font height inside text input.")); + append_warning(_u8L("Too small"), _u8L("Enlarged font height inside text input.")); if (!who.empty()) warning = GUI::format(_L("%1% is NOT shown."), who); } From fc9bae667da0a32c6b0f6f72452eeeffd1bd300f Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 9 Feb 2023 09:12:09 +0100 Subject: [PATCH 18/64] separate calculation of rotation --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 162 ++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 4 +- 2 files changed, 97 insertions(+), 69 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index abdeec66e4..84ae365082 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -467,6 +467,41 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu } return nearest_offset; } +namespace priv { + +static bool allign_z(const Vec3d &z_t, Transform3d &rotate) +{ + // Transformed unit vector Z direction (f)rom, (t)o + const Vec3d& z_f = Vec3d::UnitZ(); + Vec3d z_t_norm = z_t.normalized(); + double cos_angle = z_t_norm.dot(z_f); + + // Calculate rotation of Z-vectors from current to wanted position + rotate = Transform3d::Identity(); + + if (cos_angle == 0.) { + // check that direction is not same + if (z_t_norm.z() > 0.) + return false; + + // opposit direction of z_t and z_f (a.k.a. angle 180 DEG) + rotate = Eigen::AngleAxis(M_PI, Vec3d::UnitX()); + return true; + } else if (cos_angle >= 1. || cos_angle <= -1.) { + // bad cas angle value almost zero angle so no rotation + return false; + } + + // Calculate only when angle is not zero + // Calculate rotation axe from current to wanted inside instance + Vec3d axe = z_t_norm.cross(z_f); + axe.normalize(); + double angle = acos(cos_angle); + rotate = Eigen::AngleAxis(-angle, axe); + return true; +} + +} bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { @@ -544,6 +579,11 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2d mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume); Transform3d instance_inv = gl_volume->get_instance_transformation().get_matrix().inverse(); Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix(); + TextConfiguration &tc = *m_volume->text_configuration; + // fix baked transformation from .3mf store process + if (tc.fix_3mf_tr.has_value()) + volume_tr = volume_tr * tc.fix_3mf_tr->inverse(); + m_surface_drag = SurfaceDrag{mouse_offset, instance_inv, volume_tr, gl_volume, condition}; // Cancel job to prevent interuption of dragging (duplicit result) @@ -566,76 +606,38 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset; const Camera &camera = wxGetApp().plater()->get_camera(); auto hit = m_raycast_manager.unproject(offseted_mouse, camera, &m_surface_drag->condition); + m_surface_drag->exist_hit = hit.has_value(); if (!hit.has_value()) { // cross hair need redraw m_parent.set_as_dirty(); return true; } - // Calculate temporary position + // Calculate offset: transformation to wanted position Transform3d object_trmat = m_raycast_manager.get_transformation(hit->tr_key); - Transform3d trmat = create_transformation_onto_surface(hit->position, hit->normal); - - // !!!!!!!!!!!!!!!! USE DIRECT hit position and normal - - //TextConfiguration &tc = *m_volume->text_configuration; - //const FontProp& font_prop = tc.style.prop; - //apply_transformation(font_prop, trmat); - - //// fix baked transformation from .3mf store process - //if (tc.fix_3mf_tr.has_value()) - // trmat = trmat * (*tc.fix_3mf_tr); - - // volume transfomration in world coor - Transform3d wanted_world = object_trmat * trmat; - Transform3d wanted_volume = m_surface_drag->instance_inv * wanted_world; - - // Calculate offset inside instance: - // transformation from curret to wanted position - Vec3d from_position = m_surface_drag->volume_tr * Vec3d::Zero(); - Vec3d to_position = wanted_volume * Vec3d::Zero(); - Transform3d hit_to_instance = object_trmat * m_surface_drag->instance_inv; - Vec3d to_position2 = hit_to_instance * hit->position.cast(); - Vec3d z_t2 = hit_to_instance.linear() * hit->normal.cast(); + Transform3d hit_to_volume = hit_to_instance * m_surface_drag->volume_tr.inverse(); + Vec3d offset_volume = hit_to_volume * hit->position.cast(); + Transform3d translate{Eigen::Translation(offset_volume)}; - Vec3d offset_instance = to_position - from_position; - Transform3d trnsl{Eigen::Translation(offset_instance)}; + Transform3d rotate; + // normal transformed to volume + Vec3d z_t = hit_to_volume.linear() * hit->normal.cast(); + bool exist_rotate = priv::allign_z(z_t, rotate); + Transform3d volume_tr = m_surface_drag->volume_tr * translate * rotate; + assert(volume_tr.matrix()(0, 0) == volume_tr.matrix()(0, 0)); // Check valid transformation not a NAN + if (volume_tr.matrix()(0, 0) != volume_tr.matrix()(0, 0)) + return true; + + // Check scale in world + // current transformation from volume to world Transform3d current_world = m_surface_drag->instance_inv.inverse() * m_surface_drag->volume_tr; - auto current_world_linear = current_world.linear(); - auto wanted_world_linear = wanted_world.linear(); - // Calculate rotation of Z-vectors from current to wanted position - Transform3d rot = Transform3d::Identity(); - // Transformed unit vector Z direction (f)rom, (t)o - Vec3d z_f = m_surface_drag->volume_tr.linear() * Vec3d::UnitZ(); - Vec3d z_t = wanted_volume.linear() * Vec3d::UnitZ(); - z_f.normalize(); - z_t.normalize(); - double cos_angle = z_t.dot(z_f); - - // Calculate only when angle is not zero - if (cos_angle < 1. && cos_angle > -1.) { - m_surface_drag->from = from_position; - m_surface_drag->to = to_position; - m_surface_drag->from_dir = z_f; - m_surface_drag->to_dir = z_t; - - m_surface_drag->f_tr = current_world; - m_surface_drag->t_tr = wanted_world; - - // TODO: solve opposit direction of z_t and z_f (a.k.a. angle 180 DEG) - // if (cos_angle == 0.) {} - - // Calculate rotation axe from current to wanted inside instance - Vec3d axe = z_t.cross(z_f); - axe.normalize(); - double angle = acos(cos_angle); - rot = Eigen::AngleAxis(-angle, axe); - } + Transform3d wanted_world = m_surface_drag->instance_inv.inverse() * volume_tr; + auto wanted_world_linear = wanted_world.linear(); // Calculate scale in world auto calc_scale = [¤t_world_linear, wanted_world_linear](const Vec3d &dir) -> double { @@ -643,18 +645,32 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec3d wanted = wanted_world_linear * dir; double current_sq = current.squaredNorm(); double wanted_sq = wanted.squaredNorm(); - return sqrt(wanted_sq / current_sq); + return sqrt(current_sq / wanted_sq); }; double y_scale = calc_scale(Vec3d::UnitY()); double z_scale = calc_scale(Vec3d::UnitZ()); Transform3d scale(Eigen::Scaling(1., y_scale, z_scale)); + volume_tr = volume_tr * scale; - Transform3d volume_tr = trnsl * m_surface_drag->volume_tr * rot; + // recalculate rotation for scaled volume + //Transform3d hit_to_volume2 = hit_to_instance * (m_surface_drag->volume_tr*scale).inverse(); + //z_t = hit_to_volume2.linear() * hit->normal.cast(); + //bool exist_rotate2 = priv::allign_z(z_t, rotate); + //volume_tr = m_surface_drag->volume_tr * translate * rotate * scale; - assert(volume_tr.matrix()(0,0) == volume_tr.matrix()(0,0)); // Check valid transformation not a NAN - Transform3d volume_tr2 = m_surface_drag->instance_inv * wanted_world; + const TextConfiguration &tc = *m_volume->text_configuration; + // fix baked transformation from .3mf store process + if (tc.fix_3mf_tr.has_value()) + volume_tr = volume_tr * (*tc.fix_3mf_tr); - // Update transformation inside of instances + // apply move in Z direction for move with flat surface above texture + const FontProp &prop = tc.style.prop; + if (!prop.use_surface && prop.distance.has_value()) { + Vec3d translate = Vec3d::UnitZ() * (*prop.distance); + volume_tr.translate(translate); + } + + // Update transformation forf all instances for (GLVolume *vol : m_parent.get_volumes().volumes) { if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() || vol->volume_idx() != m_surface_drag->gl_volume->volume_idx()) @@ -662,7 +678,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) vol->set_volume_transformation(volume_tr); } - // calculate scale + // update scale of selected volume --> should be approx the same calculate_scale(); m_parent.set_as_dirty(); @@ -862,7 +878,13 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) ImVec2 center( mouse_pos.x + m_surface_drag->mouse_offset.x(), mouse_pos.y + m_surface_drag->mouse_offset.y()); - priv::draw_cross_hair(center); + ImU32 color = ImGui::GetColorU32( + m_surface_drag->exist_hit ? + ImVec4(1.f, 1.f, 1.f, .75f) : // transparent white + ImVec4(1.f, .3f, .3f, .75f) + ); // Warning color + const float radius = 16.f; + priv::draw_cross_hair(center, radius, color); } #ifdef SHOW_FINE_POSITION @@ -1115,18 +1137,22 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() EmbossStyles GLGizmoEmboss::create_default_styles() { wxFont wx_font_normal = *wxNORMAL_FONT; - wxFont wx_font_small = *wxSMALL_FONT; - #ifdef __APPLE__ - wx_font_normal.SetFaceName("Helvetica"); - wx_font_small.SetFaceName("Helvetica"); + // Set normal font to helvetica when possible + wxArrayString facenames = wxFontEnumerator::GetFacenames(Facenames::encoding); + for (const wxString &facename : facenames) { + if (facename.IsSameAs("Helvetica")) { + wx_font_normal = wxFont(wxFontInfo().FaceName(facename).Encoding(Facenames::encoding)); + break; + } + } #endif // __APPLE__ // https://docs.wxwidgets.org/3.0/classwx_font.html // Predefined objects/pointers: wxNullFont, wxNORMAL_FONT, wxSMALL_FONT, wxITALIC_FONT, wxSWISS_FONT EmbossStyles styles = { WxFontUtils::create_emboss_style(wx_font_normal, _u8L("NORMAL")), // wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) - WxFontUtils::create_emboss_style(wx_font_normal, _u8L("SMALL")), // A font using the wxFONTFAMILY_SWISS family and 2 points smaller than wxNORMAL_FONT. + WxFontUtils::create_emboss_style(*wxSMALL_FONT, _u8L("SMALL")), // A font using the wxFONTFAMILY_SWISS family and 2 points smaller than wxNORMAL_FONT. WxFontUtils::create_emboss_style(*wxITALIC_FONT, _u8L("ITALIC")), // A font using the wxFONTFAMILY_ROMAN family and wxFONTSTYLE_ITALIC style and of the same size of wxNORMAL_FONT. WxFontUtils::create_emboss_style(*wxSWISS_FONT, _u8L("SWISS")), // A font identic to wxNORMAL_FONT except for the family used which is wxFONTFAMILY_SWISS. WxFontUtils::create_emboss_style(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), _u8L("MODERN")), diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 610d10e720..0c9b3b98ed 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -255,7 +255,7 @@ private: std::vector bad = {}; // Configuration of font encoding - const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM; + static const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM; // Identify if preview texture exists GLuint texture_id = 0; @@ -325,6 +325,8 @@ private: // condition for raycaster RaycastManager::AllowVolumes condition; + bool exist_hit = true; + // Visuzalization Vec3d from = Vec3d::Zero(); Vec3d to = Vec3d::Zero(); From 88af7762fcd766343425672c08e4f90db0c76632 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 13 Feb 2023 12:07:31 +0100 Subject: [PATCH 19/64] Remove dependency on main app in job --- .../GUI/Jobs/CreateFontStyleImagesJob.cpp | 57 ++++++++----------- .../GUI/Jobs/CreateFontStyleImagesJob.hpp | 6 +- src/slic3r/Utils/EmbossStyleManager.cpp | 16 +++++- src/slic3r/Utils/EmbossStyleManager.hpp | 3 + 4 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp index d1a671330a..8aa9e23cb9 100644 --- a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp +++ b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp @@ -2,11 +2,6 @@ // rasterization of ExPoly #include "libslic3r/SLA/AGGRaster.hpp" - -// for get DPI -#include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/MainFrame.hpp" - #include "slic3r/GUI/3DScene.hpp" // ::glsafe // ability to request new frame after finish rendering @@ -20,15 +15,15 @@ using namespace Slic3r::GUI; using namespace Slic3r::GUI::Emboss; -CreateFontStyleImagesJob::CreateFontStyleImagesJob( - StyleManager::StyleImagesData &&input) - : m_input(std::move(input)) +CreateFontStyleImagesJob::CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input) + : m_input(std::move(input)), m_width(0), m_height(0) { assert(m_input.result != nullptr); assert(!m_input.styles.empty()); assert(!m_input.text.empty()); assert(m_input.max_size.x() > 1); assert(m_input.max_size.y() > 1); + assert(m_input.ppm > 1e-5); } void CreateFontStyleImagesJob::process(Ctl &ctl) @@ -36,7 +31,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl) // create shapes and calc size (bounding boxes) std::vector name_shapes(m_input.styles.size()); std::vector scales(m_input.styles.size()); - images = std::vector(m_input.styles.size()); + m_images = std::vector(m_input.styles.size()); for (auto &item : m_input.styles) { size_t index = &item - &m_input.styles.front(); @@ -44,21 +39,17 @@ void CreateFontStyleImagesJob::process(Ctl &ctl) shapes = text2shapes(item.font, m_input.text.c_str(), item.prop); // create image description - StyleManager::StyleImage &image = images[index]; + StyleManager::StyleImage &image = m_images[index]; BoundingBox &bounding_box = image.bounding_box; for (ExPolygon &shape : shapes) bounding_box.merge(BoundingBox(shape.contour.points)); for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min); // calculate conversion from FontPoint to screen pixels by size of font - auto mf = wxGetApp().mainframe; - // dot per inch for monitor - int dpi = get_dpi_for_window(mf); - double ppm = dpi / 25.4; // pixel per milimeter const auto &cn = item.prop.collection_number; unsigned int font_index = (cn.has_value()) ? *cn : 0; double unit_per_em = item.font.font_file->infos[font_index].unit_per_em; - double scale = item.prop.size_in_mm / unit_per_em * SHAPE_SCALE * ppm; + double scale = item.prop.size_in_mm / unit_per_em * SHAPE_SCALE * m_input.ppm; scales[index] = scale; //double scale = font_prop.size_in_mm * SCALING_FACTOR; @@ -78,30 +69,30 @@ void CreateFontStyleImagesJob::process(Ctl &ctl) // arrange bounding boxes int offset_y = 0; - width = 0; - for (StyleManager::StyleImage &image : images) { + m_width = 0; + for (StyleManager::StyleImage &image : m_images) { image.offset.y() = offset_y; offset_y += image.tex_size.y+1; - if (width < image.tex_size.x) - width = image.tex_size.x; + if (m_width < image.tex_size.x) + m_width = image.tex_size.x; } - height = offset_y; - for (StyleManager::StyleImage &image : images) { + m_height = offset_y; + for (StyleManager::StyleImage &image : m_images) { const Point &o = image.offset; const ImVec2 &s = image.tex_size; - image.uv0 = ImVec2(o.x() / (double) width, - o.y() / (double) height); - image.uv1 = ImVec2((o.x() + s.x) / (double) width, - (o.y() + s.y) / (double) height); + image.uv0 = ImVec2(o.x() / (double) m_width, + o.y() / (double) m_height); + image.uv1 = ImVec2((o.x() + s.x) / (double) m_width, + (o.y() + s.y) / (double) m_height); } // Set up result - pixels = std::vector(4*width * height, {255}); + m_pixels = std::vector(4 * m_width * m_height, {255}); // upload sub textures - for (StyleManager::StyleImage &image : images) { + for (StyleManager::StyleImage &image : m_images) { sla::Resolution resolution(image.tex_size.x, image.tex_size.y); - size_t index = &image - &images.front(); + size_t index = &image - &m_images.front(); double pixel_dim = SCALING_FACTOR / scales[index]; sla::PixelDim dim(pixel_dim, pixel_dim); double gamma = 1.; @@ -110,7 +101,7 @@ void CreateFontStyleImagesJob::process(Ctl &ctl) for (const ExPolygon &shape : name_shapes[index]) r->draw(shape); // copy rastered data to pixels - sla::RasterEncoder encoder = [&offset = image.offset, &pix = pixels, w=width,h=height] + sla::RasterEncoder encoder = [&offset = image.offset, &pix = m_pixels, w=m_width,h=m_height] (const void *ptr, size_t width, size_t height, size_t num_components) { // bigger value create darker image unsigned char gray_level = 5; @@ -142,18 +133,18 @@ void CreateFontStyleImagesJob::finalize(bool canceled, std::exception_ptr &) glsafe(::glBindTexture(target, tex_id)); glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); glsafe(::glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); - GLint w = width, h=height; + GLint w = m_width, h = m_height; glsafe(::glTexImage2D(target, level, GL_RGBA, w, h, border, format, type, - (const void *) pixels.data())); + (const void *) m_pixels.data())); // set up texture id void *texture_id = (void *) (intptr_t) tex_id; - for (StyleManager::StyleImage &image : images) + for (StyleManager::StyleImage &image : m_images) image.texture_id = texture_id; // move to result m_input.result->styles = std::move(m_input.styles); - m_input.result->images = std::move(images); + m_input.result->images = std::move(m_images); // bind default texture GLuint no_texture_id = 0; diff --git a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp index c220f2ee01..b8c2757a62 100644 --- a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp +++ b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp @@ -19,11 +19,11 @@ class CreateFontStyleImagesJob : public Job // Output data // texture size - int width, height; + int m_width, m_height; // texture data - std::vector pixels; + std::vector m_pixels; // descriptors of sub textures - std::vector images; + std::vector m_images; public: CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input); diff --git a/src/slic3r/Utils/EmbossStyleManager.cpp b/src/slic3r/Utils/EmbossStyleManager.cpp index 9c6738ece3..af1aae669f 100644 --- a/src/slic3r/Utils/EmbossStyleManager.cpp +++ b/src/slic3r/Utils/EmbossStyleManager.cpp @@ -304,12 +304,15 @@ void StyleManager::init_trunc_names(float max_width) { } } -#include "slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp" - // for access to worker #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" +// for get DPI +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/MainFrame.hpp" +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" + void StyleManager::init_style_images(const Vec2i &max_size, const std::string &text) { @@ -361,8 +364,15 @@ void StyleManager::init_style_images(const Vec2i &max_size, style.prop }); } + + auto mf = wxGetApp().mainframe; + // dot per inch for monitor + int dpi = get_dpi_for_window(mf); + // pixel per milimeter + double ppm = dpi / ObjectManipulation::in_to_mm; + auto &worker = wxGetApp().plater()->get_ui_job_worker(); - StyleImagesData data{std::move(styles), max_size, text, m_temp_style_images}; + StyleImagesData data{std::move(styles), max_size, text, m_temp_style_images, ppm}; queue_job(worker, std::make_unique(std::move(data))); } diff --git a/src/slic3r/Utils/EmbossStyleManager.hpp b/src/slic3r/Utils/EmbossStyleManager.hpp index 8183214e95..08fa72642b 100644 --- a/src/slic3r/Utils/EmbossStyleManager.hpp +++ b/src/slic3r/Utils/EmbossStyleManager.hpp @@ -277,6 +277,9 @@ private: // place to store result in main thread in Finalize std::shared_ptr result; + + // pixel per milimeter (scaled DPI) + double ppm; }; std::shared_ptr m_temp_style_images; bool m_exist_style_images; From f42ae64277069c5fd3cd641cd845a4d59aae6097 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 14 Feb 2023 16:06:59 +0100 Subject: [PATCH 20/64] Change Text property after move over surface when world size is changed(re-create text volume). NOTE: Not intuitive during dragging. --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 348 +++++++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 8 + 2 files changed, 290 insertions(+), 66 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 84ae365082..e602bbd76c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -467,21 +467,20 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu } return nearest_offset; } -namespace priv { -static bool allign_z(const Vec3d &z_t, Transform3d &rotate) +namespace priv { +static bool allign_vec(const Vec3d &z_f, const Vec3d &z_t, Transform3d &rotate) { - // Transformed unit vector Z direction (f)rom, (t)o - const Vec3d& z_f = Vec3d::UnitZ(); - Vec3d z_t_norm = z_t.normalized(); - double cos_angle = z_t_norm.dot(z_f); + Vec3d z_f_norm = z_f.normalized(); + Vec3d z_t_norm = z_t.normalized(); + double cos_angle = z_t_norm.dot(z_f_norm); // Calculate rotation of Z-vectors from current to wanted position rotate = Transform3d::Identity(); if (cos_angle == 0.) { // check that direction is not same - if (z_t_norm.z() > 0.) + if (z_t_norm.z() > 0.) return false; // opposit direction of z_t and z_f (a.k.a. angle 180 DEG) @@ -494,13 +493,187 @@ static bool allign_z(const Vec3d &z_t, Transform3d &rotate) // Calculate only when angle is not zero // Calculate rotation axe from current to wanted inside instance - Vec3d axe = z_t_norm.cross(z_f); + Vec3d axe = z_t_norm.cross(z_f_norm); axe.normalize(); double angle = acos(cos_angle); - rotate = Eigen::AngleAxis(-angle, axe); + rotate = Eigen::AngleAxis(-angle, axe); return true; } +static bool allign_z(const Vec3d &z_t, Transform3d &rotate) +{ + // Transformed unit vector Z direction (f)rom, (t)o + const Vec3d& z_f = Vec3d::UnitZ(); + return allign_vec(Vec3d::UnitZ(), z_t, rotate); +} + + // Calculate scale in world +static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) +{ + Vec3d from_dir = from * dir; + Vec3d to_dir = to * dir; + double from_scale_sq = from_dir.squaredNorm(); + double to_scale_sq = to_dir.squaredNorm(); + if (is_approx(from_scale_sq, to_scale_sq, 1e-3)) + return {}; // no scale + return sqrt(from_scale_sq / to_scale_sq); +}; + +// Copy from branch et_transformation --> Geometry +// suggested by @bubnikv +void reset_skew(Transform3d& m) +{ + auto new_scale_factor = [](const Matrix3d& s) { + return pow(s(0, 0) * s(1, 1) * s(2, 2), 1. / 3.); // scale average + }; + + const Eigen::JacobiSVD svd(m.linear(), Eigen::ComputeFullU | Eigen::ComputeFullV); + Matrix3d u = svd.matrixU(); + Matrix3d v = svd.matrixV(); + Matrix3d s = svd.singularValues().asDiagonal(); + + //Matrix3d mirror; + m = Eigen::Translation3d(m.translation()) * Transform3d(u * Eigen::Scaling(new_scale_factor(s)) * v.transpose());// * mirror; +} + +// Multiply from right +static Transform3d surface_transformR(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { + Transform3d to_volume = (i * v).inverse(); + Vec3d offset_volume = to_volume * p; + Transform3d translate{Eigen::Translation(offset_volume)}; + + Transform3d rotate; + // normal transformed to volume + Vec3d z_t = to_volume.linear() * n; + bool exist_rotate = priv::allign_z(z_t, rotate); + return v * translate * rotate; +} + +/// +/// Create transformation for volume to move over surface +/// Multiply from Left side - NOT WORK - with scaled instances +/// +/// Point in world coordinate +/// Normal in world coordinate - orientation +/// Original volume transformation +/// Instance transformation +/// Transformation of volume to lay on surface +static Transform3d surface_transformL(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { + // w .. original world + Transform3d w = i * v; + + // remove already existing of skew before calc rotation + // priv::reset_skew(w); + + // z .. unit z vector in world coordinate + Vec3d z = w.linear() * Vec3d::UnitZ(); + Transform3d rot = Transform3d::Identity(); + bool exist_rot = priv::allign_vec(z, n, rot); + + // rot_w .. new rotation applied on world + Transform3d rot_w = rot * w; + + // p0 .. Zero of volume in world + Vec3d p0 = rot_w * Vec3d::Zero(); + Vec3d offset = p - p0; // in world + Transform3d tr{Eigen::Translation(offset)}; + + // w2 .. wanted world transformation + Transform3d w2 = tr * rot_w; + + //priv::reset_skew(w2); + + // _ .. inverse + // i_ .. instance inverse + Transform3d i_ = i.inverse(); + + // w = i * v \\ left multiply by i_ + // i_ * w = i_ * i * v + // v = i_ * w + return i_ * w2; + // NOTE: Do not keep scale of text when move over scaled instance +} + +// transformation inside of instance +static Transform3d surface_transform2(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) +{ + // _ .. inverse + // i_ .. instance inverse + Transform3d i_ = i.inverse(); + Vec3d pp = i_ * p; + Vec3d nn = i_.linear() * n.normalized(); + nn.normalize(); + + // z .. unit z vector in world coordinate + Vec3d z = v * Vec3d::UnitZ(); + z.normalize(); + + Transform3d rot = Transform3d::Identity(); + bool exist_rot = priv::allign_vec(z, nn, rot); + + // rot_w .. new rotation applied on world + Transform3d rotated = rot * v; + + // p0 .. Zero of volume in world + Vec3d p0 = rotated * Vec3d::Zero(); + Vec3d offset = pp - p0; // in world + Transform3d tr{Eigen::Translation(offset)}; + Transform3d volume_new = tr * rotated; + //return volume_new; + + // Remove skew in world + Transform3d world_new = i * volume_new; + reset_skew(world_new); + volume_new = i_ * world_new; + + return volume_new; +} + +// work in space defined by SVD +static Transform3d surface_transform3(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { + // w .. original world + Transform3d w = i * v; + + const Eigen::JacobiSVD svd1(w.linear(), Eigen::ComputeFullU | Eigen::ComputeFullV); + Matrix3d u1 = svd1.matrixU(); + Matrix3d v1 = svd1.matrixV(); + Matrix3d s1 = svd1.singularValues().asDiagonal(); + Transform3d tr1(Eigen::Translation3d(w.translation())); + //Transform3d test = tr1 * Transform3d(u1 * s1 * v1.transpose()); + + // modification of world + Transform3d mod(s1 * v1.transpose()); + Transform3d mod_ = mod.inverse(); + Transform3d w_mod = w * mod_; + + Vec3d nn = mod_.linear() * n; + // z .. unit z vector in world coordinate + Vec3d z = w_mod.linear() * Vec3d::UnitZ(); + Transform3d rot = Transform3d::Identity(); + bool exist_rot = priv::allign_vec(z, nn, rot); + + // rot_w .. new rotation applied on world + Transform3d rot_w = rot * w; + + // p0 .. Zero of volume in world + Vec3d p0 = rot_w * Vec3d::Zero(); + Vec3d offset = p - p0; // in world + Transform3d tr{Eigen::Translation(offset)}; + + // w2 .. wanted world transformation + Transform3d w2 = tr * rot_w; + + // _ .. inverse + // i_ .. instance inverse + Transform3d i_ = i.inverse(); + + // w = i * v \\ left multiply by i_ + // i_ * w = i_ * i * v + // v = i_ * w + return i_ * w2; + // NOTE: Do not keep scale of text when move over scaled instance +} + } bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) @@ -512,7 +685,19 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) m_parent.do_move(L("Surface move")); // Update surface by new position - if (m_volume->text_configuration->style.prop.use_surface) + bool need_process = m_volume->text_configuration->style.prop.use_surface; + + if (m_surface_drag->y_scale.has_value()) { + m_style_manager.get_style().prop.size_in_mm *= (*m_surface_drag->y_scale); + need_process |= set_height(); + } + + if (m_surface_drag->z_scale.has_value()) { + m_style_manager.get_style().prop.emboss *= (*m_surface_drag->z_scale); + need_process |= set_depth(); + } + + if (need_process) process(); // calculate scale @@ -612,45 +797,52 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) m_parent.set_as_dirty(); return true; } - // Calculate offset: transformation to wanted position - Transform3d object_trmat = m_raycast_manager.get_transformation(hit->tr_key); - Transform3d hit_to_instance = object_trmat * m_surface_drag->instance_inv; + Transform3d hit_to_world = m_raycast_manager.get_transformation(hit->tr_key); + //priv::reset_skew(hit_to_world); + + Transform3d hit_to_instance = hit_to_world * m_surface_drag->instance_inv; Transform3d hit_to_volume = hit_to_instance * m_surface_drag->volume_tr.inverse(); - Vec3d offset_volume = hit_to_volume * hit->position.cast(); + + Vec3d hit_position = hit->position.cast(); + Vec3d offset_volume = hit_to_volume * hit_position; Transform3d translate{Eigen::Translation(offset_volume)}; Transform3d rotate; // normal transformed to volume - Vec3d z_t = hit_to_volume.linear() * hit->normal.cast(); + Vec3d hit_normal = hit->normal.cast(); + Vec3d z_t = hit_to_volume.linear() * hit_normal; bool exist_rotate = priv::allign_z(z_t, rotate); + // Edit position from right + Transform3d volume_new = m_surface_drag->volume_tr * translate * rotate; - Transform3d volume_tr = m_surface_drag->volume_tr * translate * rotate; - assert(volume_tr.matrix()(0, 0) == volume_tr.matrix()(0, 0)); // Check valid transformation not a NAN - if (volume_tr.matrix()(0, 0) != volume_tr.matrix()(0, 0)) + const Transform3d &instance = m_surface_drag->gl_volume->get_instance_transformation().get_matrix(); + const Transform3d &volume = m_surface_drag->volume_tr; + + Vec3d hit_position_world = hit_to_world * hit_position; + Vec3d hit_normal_world = hit_to_world.linear() * hit_normal; + + // REWRITE transformation + Transform3d volume_R = priv::surface_transformR(hit_position_world, hit_normal_world, volume, instance); + Transform3d volume_L = priv::surface_transformL(hit_position_world, hit_normal_world, volume, instance); + Transform3d volume_2 = priv::surface_transform2(hit_position_world, hit_normal_world, volume, instance); + Transform3d volume_3 = priv::surface_transform3(hit_position_world, hit_normal_world, volume, instance); + //volume_new = volume_L; + + assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN + if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) return true; // Check scale in world - - // current transformation from volume to world - Transform3d current_world = m_surface_drag->instance_inv.inverse() * m_surface_drag->volume_tr; + // Calculate Scale to keep size after move over scaled surface + Transform3d current_world = instance * volume; auto current_world_linear = current_world.linear(); - Transform3d wanted_world = m_surface_drag->instance_inv.inverse() * volume_tr; + Transform3d wanted_world = instance * volume_new; auto wanted_world_linear = wanted_world.linear(); - // Calculate scale in world - auto calc_scale = [¤t_world_linear, wanted_world_linear](const Vec3d &dir) -> double { - Vec3d current = current_world_linear * dir; - Vec3d wanted = wanted_world_linear * dir; - double current_sq = current.squaredNorm(); - double wanted_sq = wanted.squaredNorm(); - return sqrt(current_sq / wanted_sq); - }; - double y_scale = calc_scale(Vec3d::UnitY()); - double z_scale = calc_scale(Vec3d::UnitZ()); - Transform3d scale(Eigen::Scaling(1., y_scale, z_scale)); - volume_tr = volume_tr * scale; + m_surface_drag->y_scale = priv::calc_scale(current_world_linear, wanted_world_linear, Vec3d::UnitY()); + m_surface_drag->z_scale = priv::calc_scale(current_world_linear, wanted_world_linear, Vec3d::UnitZ()); // recalculate rotation for scaled volume //Transform3d hit_to_volume2 = hit_to_instance * (m_surface_drag->volume_tr*scale).inverse(); @@ -661,13 +853,13 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) const TextConfiguration &tc = *m_volume->text_configuration; // fix baked transformation from .3mf store process if (tc.fix_3mf_tr.has_value()) - volume_tr = volume_tr * (*tc.fix_3mf_tr); + volume_new = volume_new * (*tc.fix_3mf_tr); // apply move in Z direction for move with flat surface above texture const FontProp &prop = tc.style.prop; if (!prop.use_surface && prop.distance.has_value()) { Vec3d translate = Vec3d::UnitZ() * (*prop.distance); - volume_tr.translate(translate); + volume_new.translate(translate); } // Update transformation forf all instances @@ -675,7 +867,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() || vol->volume_idx() != m_surface_drag->gl_volume->volume_idx()) continue; - vol->set_volume_transformation(volume_tr); + vol->set_volume_transformation(volume_new); } // update scale of selected volume --> should be approx the same @@ -1136,10 +1328,12 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() EmbossStyles GLGizmoEmboss::create_default_styles() { + wxFontEnumerator::InvalidateCache(); + wxArrayString facenames = wxFontEnumerator::GetFacenames(Facenames::encoding); + wxFont wx_font_normal = *wxNORMAL_FONT; #ifdef __APPLE__ // Set normal font to helvetica when possible - wxArrayString facenames = wxFontEnumerator::GetFacenames(Facenames::encoding); for (const wxString &facename : facenames) { if (facename.IsSameAs("Helvetica")) { wx_font_normal = wxFont(wxFontInfo().FaceName(facename).Encoding(Facenames::encoding)); @@ -1181,7 +1375,6 @@ EmbossStyles GLGizmoEmboss::create_default_styles() // No valid style in defult list // at least one style must contain loadable font - wxArrayString facenames = wxFontEnumerator::GetFacenames(wxFontEncoding::wxFONTENCODING_SYSTEM); wxFont wx_font; for (const wxString &face : facenames) { wx_font = wxFont(face); @@ -3082,6 +3275,31 @@ void GLGizmoEmboss::draw_style_edit() { #endif // SHOW_WX_WEIGHT_INPUT } +bool GLGizmoEmboss::set_height() { + float &value = m_style_manager.get_style().prop.size_in_mm; + + // size can't be zero or negative + priv::Limits::apply(value, priv::limits.size_in_mm); + + if (m_volume == nullptr || !m_volume->text_configuration.has_value()) { + assert(false); + return false; + } + + // only different value need process + if (is_approx(value, m_volume->text_configuration->style.prop.size_in_mm)) + return false; + + // store font size into path serialization + const std::optional &wx_font_opt = m_style_manager.get_wx_font(); + if (wx_font_opt.has_value()) { + wxFont wx_font = *wx_font_opt; + wx_font.SetPointSize(static_cast(value)); + m_style_manager.set_wx_font(wx_font); + } + return true; +} + void GLGizmoEmboss::draw_height(bool use_inch) { float &value = m_style_manager.get_style().prop.size_in_mm; @@ -3090,24 +3308,21 @@ void GLGizmoEmboss::draw_height(bool use_inch) const char *size_format = ((use_inch) ? "%.2f in" : "%.1f mm"); const std::string revert_text_size = _u8L("Revert text size."); const std::string& name = m_gui_cfg->translations.size; - if (rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, m_scale_height)) { - // size can't be zero or negative - priv::Limits::apply(value, priv::limits.size_in_mm); - // only different value need process - if (!is_approx(value, m_volume->text_configuration->style.prop.size_in_mm)) { - // store font size into path - EmbossStyle &style = m_style_manager.get_style(); - if (style.type == WxFontUtils::get_actual_type()) { - const std::optional &wx_font_opt = m_style_manager.get_wx_font(); - if (wx_font_opt.has_value()) { - wxFont wx_font = *wx_font_opt; - wx_font.SetPointSize(static_cast(value)); - m_style_manager.set_wx_font(wx_font); - } - } + if (rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, m_scale_height)) + if (set_height()) process(); - } - } +} + + +bool GLGizmoEmboss::set_depth() +{ + float &value = m_style_manager.get_style().prop.emboss; + + // size can't be zero or negative + priv::Limits::apply(value, priv::limits.emboss); + + // only different value need process + return !is_approx(value, m_volume->text_configuration->style.prop.emboss); } void GLGizmoEmboss::draw_depth(bool use_inch) @@ -3118,11 +3333,9 @@ void GLGizmoEmboss::draw_depth(bool use_inch) const std::string revert_emboss_depth = _u8L("Revert embossed depth."); const char *size_format = ((use_inch) ? "%.3f in" : "%.2f mm"); const std::string name = m_gui_cfg->translations.depth; - if (rev_input_mm(name, value, stored, revert_emboss_depth, 0.1f, 1.f, size_format, use_inch, m_scale_depth)) { - // size can't be zero or negative - priv::Limits::apply(value, priv::limits.emboss); - process(); - } + if (rev_input_mm(name, value, stored, revert_emboss_depth, 0.1f, 1.f, size_format, use_inch, m_scale_depth)) + if (set_depth()) + process(); } @@ -3977,14 +4190,17 @@ bool priv::start_create_volume_on_surface_job( if (!hit.has_value()) return false; - Transform3d hit_object_trmat = raycaster.get_transformation(hit->tr_key); - Transform3d hit_instance_trmat = gl_volume->get_instance_transformation().get_matrix(); + Transform3d hit_to_world = raycaster.get_transformation(hit->tr_key); + // priv::reset_skew(hit_to_world); + Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); // Create result volume transformation - Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); - const FontProp &font_prop = emboss_data.text_configuration.style.prop; + Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); + const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); - Transform3d volume_trmat = hit_instance_trmat.inverse() * hit_object_trmat * surface_trmat; + Transform3d world_new = hit_to_world * surface_trmat; + // priv::reset_skew(world_new); + Transform3d volume_trmat = instance.inverse() * world_new; start_create_volume_job(obj, 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 0c9b3b98ed..e1fded204d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -112,6 +112,11 @@ private: void draw_height(bool use_inch); void draw_depth(bool use_inch); + // call after set m_style_manager.get_style().prop.size_in_mm + bool set_height(); + // call after set m_style_manager.get_style().prop.emboss + bool set_depth(); + bool draw_italic_button(); bool draw_bold_button(); void draw_advanced(); @@ -336,6 +341,9 @@ private: Transform3d f_tr = Transform3d::Identity(); Transform3d t_tr = Transform3d::Identity(); + + std::optional y_scale; + std::optional z_scale; }; // Keep data about dragging only during drag&drop std::optional m_surface_drag; From 89ff154f9b8f91d0929cc726575927c97f49b2b0 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 14 Feb 2023 18:16:05 +0100 Subject: [PATCH 21/64] temp --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index e602bbd76c..35aec716e8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -799,8 +799,6 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) } // Calculate offset: transformation to wanted position Transform3d hit_to_world = m_raycast_manager.get_transformation(hit->tr_key); - //priv::reset_skew(hit_to_world); - Transform3d hit_to_instance = hit_to_world * m_surface_drag->instance_inv; Transform3d hit_to_volume = hit_to_instance * m_surface_drag->volume_tr.inverse(); From 6d0d24eecf0f172a2cdfb272fe6196b08fcc9169 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 15 Feb 2023 11:05:20 +0100 Subject: [PATCH 22/64] Get GL volume by volume by selection (not hovered volume) --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 76 +++++++++++++++++++++---- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 056719c198..44e664f8f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -375,6 +375,7 @@ static Vec2d calc_mouse_to_center_text_offset(const Vec2d &mouse, const ModelVol /// Containe what is selected /// Slected when only one volume otherwise nullptr static const GLVolume *get_gl_volume(const Selection &selection); +static GLVolume *get_gl_volume(const GLCanvas3D &canvas); /// /// Get transformation to world @@ -395,6 +396,7 @@ static void change_window_position(std::optional &output_window_offset, } // namespace priv const GLVolume *priv::get_gl_volume(const Selection &selection) { + // return selection.get_first_volume(); const auto &list = selection.get_volume_idxs(); if (list.size() != 1) return nullptr; @@ -402,6 +404,19 @@ const GLVolume *priv::get_gl_volume(const Selection &selection) { return selection.get_volume(volume_idx); } +GLVolume *priv::get_gl_volume(const GLCanvas3D &canvas) { + const GLVolume *gl_volume = get_gl_volume(canvas.get_selection()); + if (gl_volume == nullptr) + return nullptr; + + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + for (GLVolume *v : gl_volumes) + if (v->composite_id == gl_volume->composite_id) + return v; + + return nullptr; +} + Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model) { if (!gl_volume) @@ -536,17 +551,40 @@ void reset_skew(Transform3d& m) m = Eigen::Translation3d(m.translation()) * Transform3d(u * Eigen::Scaling(new_scale_factor(s)) * v.transpose());// * mirror; } +void reset_skew_respect_z(Transform3d &m) +{ + Vec3d z_before = m * Vec3d::UnitZ(); + priv::reset_skew(m); + Vec3d z_after = m * Vec3d::UnitZ(); + + Transform3d rot; // = Transform3d::Identity(); + if (priv::allign_vec(z_after, z_before, rot)) + m = rot * m; +} + // Multiply from right static Transform3d surface_transformR(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { Transform3d to_volume = (i * v).inverse(); Vec3d offset_volume = to_volume * p; Transform3d translate{Eigen::Translation(offset_volume)}; + + // new transformation for volume + Transform3d v_new = v * translate; + // rotation when exists Transform3d rotate; + // normal transformed to volume Vec3d z_t = to_volume.linear() * n; - bool exist_rotate = priv::allign_z(z_t, rotate); - return v * translate * rotate; + if (priv::allign_z(z_t, rotate)) + v_new = v_new * rotate; + + // Reset skew in world + Transform3d w_new = i * v_new; + priv::reset_skew_respect_z(w_new); + v_new = i.inverse() * w_new; + + return v_new; } /// @@ -581,7 +619,7 @@ static Transform3d surface_transformL(const Vec3d &p, const Vec3d &n, const Tran // w2 .. wanted world transformation Transform3d w2 = tr * rot_w; - //priv::reset_skew(w2); + //priv::reset_skew_respect_z(w2); // _ .. inverse // i_ .. instance inverse @@ -721,15 +759,15 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (m_volume == nullptr) return false; - // must exist hover object - int hovered_id = m_parent.get_first_hover_volume_idx(); - if (hovered_id < 0) + if (m_parent.get_first_hover_volume_idx() < 0) return false; - GLVolume *gl_volume = m_parent.get_volumes().volumes[hovered_id]; - const ModelObjectPtrs &objects = m_parent.get_model()->objects; + GLVolume *gl_volume = priv::get_gl_volume(m_parent); + if (gl_volume == nullptr) + return false; // hovered object must be actual text volume + const ModelObjectPtrs &objects = m_parent.get_model()->objects; if (m_volume != priv::get_model_volume(gl_volume, objects)) return false; @@ -825,7 +863,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Transform3d volume_L = priv::surface_transformL(hit_position_world, hit_normal_world, volume, instance); Transform3d volume_2 = priv::surface_transform2(hit_position_world, hit_normal_world, volume, instance); Transform3d volume_3 = priv::surface_transform3(hit_position_world, hit_normal_world, volume, instance); - //volume_new = volume_L; + volume_new = volume_R; assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) @@ -3701,6 +3739,21 @@ void GLGizmoEmboss::draw_advanced() ImGui::SetTooltip("%s", _u8L("Use camera direction for text orientation").c_str()); } + ImGui::SameLine(); + if (ImGui::Button(_u8L("Reset scale").c_str())) { + GLVolume *gl_volume = priv::get_gl_volume(m_parent); + if (gl_volume != nullptr) { + Transform3d w = gl_volume->world_matrix(); + priv::reset_skew_respect_z(w); + Transform3d i = gl_volume->get_instance_transformation().get_matrix(); + Transform3d v_new = i.inverse() * w; + gl_volume->set_volume_transformation(v_new); + m_parent.do_move(L("Reset scale")); + } + } else if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("%s", _u8L("Reset skew of text to be normal in world").c_str()); + } + #ifdef ALLOW_DEBUG_MODE ImGui::Text("family = %s", (font_prop.family.has_value() ? font_prop.family->c_str() : @@ -4197,7 +4250,10 @@ 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 world_new = hit_to_world * surface_trmat; - // priv::reset_skew(world_new); + + // Reset skew + priv::reset_skew_respect_z(world_new); + Transform3d volume_trmat = instance.inverse() * world_new; start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); return true; From 67155e8da01acc7b78391a6144ec94c2e52fe25b Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 16 Feb 2023 13:09:29 +0100 Subject: [PATCH 23/64] RaycastManager use directly AABBMesh instead of MeshRayCaster --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 38 ++-- src/slic3r/Utils/RaycastManager.cpp | 261 +++++++++++++----------- src/slic3r/Utils/RaycastManager.hpp | 82 +++++--- 3 files changed, 215 insertions(+), 166 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 44e664f8f8..c6ce9da8fe 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -559,7 +559,7 @@ void reset_skew_respect_z(Transform3d &m) Transform3d rot; // = Transform3d::Identity(); if (priv::allign_vec(z_after, z_before, rot)) - m = rot * m; + m = m * rot; } // Multiply from right @@ -3489,21 +3489,33 @@ std::optional priv::calc_surface_offset(const ModelVolume &volume, Raycas std::optional hit_opt = raycast_manager.unproject(point, direction, &cond); // Try to find closest point when no hit object in emboss direction - if (!hit_opt.has_value()) - hit_opt = raycast_manager.closest(point); + if (!hit_opt.has_value()) { + std::optional close_point_opt = raycast_manager.closest(point); - // It should NOT appear. Closest point always exists. - if (!hit_opt.has_value()) - return {}; + // It should NOT appear. Closest point always exists. + assert(close_point_opt.has_value()); + if (!close_point_opt.has_value()) + return {}; + + // It is no neccesary to move with origin by very small value + if (close_point_opt->squared_distance < EPSILON) + return {}; + + const RaycastManager::ClosePoint &close_point = *close_point_opt; + Transform3d hit_tr = raycast_manager.get_transformation(close_point.tr_key); + Vec3d hit_world = hit_tr * close_point.point; + Vec3d offset_world = hit_world - point; // vector in world + Vec3d offset_volume = to_world.inverse().linear() * offset_world; + return offset_volume; + } // It is no neccesary to move with origin by very small value - if (hit_opt->squared_distance < EPSILON) - return {}; - const RaycastManager::Hit &hit = *hit_opt; - Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key); - Vec3d hit_world = hit_tr * hit.position.cast(); - Vec3d offset_world = hit_world - point; // vector in world + if (hit.squared_distance < EPSILON) + return {}; + Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key); + Vec3d hit_world = hit_tr * hit.position; + Vec3d offset_world = hit_world - point; // vector in world // TIP: It should be close to only z move Vec3d offset_volume = to_world.inverse().linear() * offset_world; return offset_volume; @@ -4246,7 +4258,7 @@ bool priv::start_create_volume_on_surface_job( Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); // Create result volume transformation - Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); + Transform3d surface_trmat = create_transformation_onto_surface(hit->position.cast(), hit->normal.cast()); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); Transform3d world_new = hit_to_world * surface_trmat; diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index be11c7f894..fad41424f9 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -3,69 +3,50 @@ // include for earn camera #include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/Plater.hpp" -#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/CameraUtils.hpp" using namespace Slic3r::GUI; namespace priv { - using namespace Slic3r; -// copied from private part of RaycastManager.hpp -using Raycaster = std::pair >; -// ModelVolume.id +static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip); +static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); +static RaycastManager::TrKey create_key(const ModelVolume* volume, const ModelInstance* instance){ + return std::make_pair(instance->id().id, volume->id().id); } +static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); +static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { + return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; } +static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { + return is_lower_key(i1.first, i2.first); }; -using Raycasters = std::vector; + // Copy functionality from MeshRaycaster::unproject_on_mesh without filtering +using SurfacePoint = RaycastManager::SurfacePoint; +static std::optional unproject_on_mesh(const AABBMesh &aabb_mesh, + const Vec2d &mouse_pos, + const Transform3d &transformation, + const Camera &camera); -static void actualize(Raycasters &casters, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip) -{ - // check if volume was removed - std::vector removed_casters(casters.size(), {true}); - // actualize MeshRaycaster - for (const ModelVolume *volume : volumes) { - size_t oid = volume->id().id; - if (skip != nullptr && skip->skip(oid)) - continue; - auto item = std::find_if(casters.begin(), casters.end(), - [oid](const Raycaster &it) -> bool { return oid == it.first; }); - if (item == casters.end()) { - // add new raycaster - auto raycaster = std::make_unique(volume->get_mesh_shared_ptr()); - casters.emplace_back(std::make_pair(oid, std::move(raycaster))); - } else { - size_t index = item - casters.begin(); - removed_casters[index] = false; - } - } - - // clean other raycasters - for (int i = removed_casters.size() - 1; i >= 0; --i) - if (removed_casters[i]) - casters.erase(casters.begin() + i); -} } void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) { // actualize MeshRaycaster - priv::actualize(m_raycasters, object->volumes, skip); + priv::actualize(m_meshes, object->volumes, skip); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); - + + bool need_sort = false; // actualize transformation matrices for (const ModelVolume *volume : object->volumes) { if (skip != nullptr && skip->skip(volume->id().id)) continue; const Transform3d &volume_tr = volume->get_matrix(); for (const ModelInstance *instance : object->instances) { - const Transform3d &instrance_tr = instance->get_matrix(); - Transform3d transformation = instrance_tr * volume_tr; - TrKey tr_key = std::make_pair(instance->id().id, volume->id().id); - auto item = std::find_if(m_transformations.begin(), - m_transformations.end(), - [&tr_key](const TrItem &it) -> bool { - return it.first == tr_key; - }); + const Transform3d &instrance_tr = instance->get_matrix(); + Transform3d transformation = instrance_tr * volume_tr; + TrKey key = priv::create_key(volume, instance); + auto item = priv::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time item->second = transformation; @@ -73,8 +54,8 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) removed_transf[index] = false; } else { // add new transformation - m_transformations.emplace_back( - std::make_pair(tr_key, transformation)); + m_transformations.emplace_back(std::make_pair(key, transformation)); + need_sort = true; } } } @@ -83,17 +64,21 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) for (int i = removed_transf.size() - 1; i >= 0; --i) if (removed_transf[i]) m_transformations.erase(m_transformations.begin() + i); + + if (need_sort) + std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) { const ModelVolumePtrs &volumes = instance->get_object()->volumes; // actualize MeshRaycaster - priv::actualize(m_raycasters, volumes, skip); + priv::actualize(m_meshes, volumes, skip); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); + bool need_sort = false; // actualize transformation matrices for (const ModelVolume *volume : volumes) { if (skip != nullptr && skip->skip(volume->id().id)) @@ -101,9 +86,8 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) const Transform3d &volume_tr = volume->get_matrix(); const Transform3d &instrance_tr = instance->get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey tr_key = std::make_pair(instance->id().id, volume->id().id); - auto item = std::find_if(m_transformations.begin(), m_transformations.end(), - [&tr_key](const TrItem &it) -> bool { return it.first == tr_key; }); + TrKey key = priv::create_key(volume, instance); + auto item = priv::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time item->second = transformation; @@ -111,7 +95,8 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) removed_transf[index] = false; } else { // add new transformation - m_transformations.emplace_back(std::make_pair(tr_key, transformation)); + m_transformations.emplace_back(std::make_pair(key, transformation)); + need_sort = true; } } @@ -119,65 +104,33 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) for (int i = removed_transf.size() - 1; i >= 0; --i) if (removed_transf[i]) m_transformations.erase(m_transformations.begin() + i); + + if (need_sort) + std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } -#include "slic3r/GUI/CameraUtils.hpp" -namespace priv { - -// Copy functionality from MeshRaycaster::unproject_on_mesh without filtering -static std::optional unproject_on_mesh(const MeshRaycaster &raycaster, - const Vec2d &mouse_pos, const Transform3d &transformation, const Camera &camera) { - - Vec3d point; - Vec3d direction; - CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); - Transform3d inv = transformation.inverse(); - point = inv*point; - direction = inv.linear()*direction; - - const AABBMesh &aabb_mesh = raycaster.get_aabb_mesh(); - std::vector hits = aabb_mesh.query_ray_hits(point, direction); - - if (hits.empty()) - return {}; // no intersection found - - const AABBMesh::hit_result &hit = hits.front(); - return RaycastManager::SurfacePoint( - hit.position().cast(), - hit.normal().cast() - ); -} -} // namespace priv - std::optional RaycastManager::unproject( const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const { std::optional closest; for (const auto &item : m_transformations) { const TrKey &key = item.first; - size_t volume_id = key.second; + size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; + const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + if (mesh == nullptr) continue; const Transform3d &transformation = item.second; - auto raycaster_it = - std::find_if(m_raycasters.begin(), m_raycasters.end(), - [volume_id](const RaycastManager::Raycaster &it) - -> bool { return volume_id == it.first; }); - if (raycaster_it == m_raycasters.end()) continue; - const MeshRaycaster &raycaster = *(raycaster_it->second); - std::optional surface_point_opt = priv::unproject_on_mesh( - raycaster, mouse_pos, transformation, camera); + auto surface_point_opt = + priv::unproject_on_mesh(*mesh, mouse_pos, transformation, camera); if (!surface_point_opt.has_value()) continue; - const SurfacePoint &surface_point = *surface_point_opt; - Vec3d act_hit_tr = transformation * surface_point.position.cast(); + Vec3d act_hit_tr = transformation * surface_point_opt->position.cast(); double squared_distance = (camera.get_position() - act_hit_tr).squaredNorm(); if (closest.has_value() && closest->squared_distance < squared_distance) continue; - closest = Hit(key, surface_point, squared_distance); + closest = Hit{*surface_point_opt, key, squared_distance}; } - - //if (!closest.has_value()) return {}; return closest; } @@ -186,16 +139,11 @@ std::optional RaycastManager::unproject(const Vec3d &point, std::optional closest; for (const auto &item : m_transformations) { const TrKey &key = item.first; - size_t volume_id = key.second; + size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; const Transform3d &transformation = item.second; - auto raycaster_it = - std::find_if(m_raycasters.begin(), m_raycasters.end(), - [volume_id](const RaycastManager::Raycaster &it) - -> bool { return volume_id == it.first; }); - if (raycaster_it == m_raycasters.end()) continue; - const MeshRaycaster &raycaster = *(raycaster_it->second); - const AABBMesh& mesh = raycaster.get_aabb_mesh(); + const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + if (mesh == nullptr) continue; Transform3d tr_inv = transformation.inverse(); Vec3d mesh_point = tr_inv * point; Vec3d mesh_direction = tr_inv.linear() * direction; @@ -205,49 +153,50 @@ std::optional RaycastManager::unproject(const Vec3d &point, Vec3d point_negative = mesh_point + mesh_direction; // Throw ray to both directions of ray - std::vector hits = mesh.query_ray_hits(point_positive, mesh_direction); - std::vector hits_neg = mesh.query_ray_hits(point_negative, -mesh_direction); + std::vector hits = mesh->query_ray_hits(point_positive, mesh_direction); + std::vector hits_neg = mesh->query_ray_hits(point_negative, -mesh_direction); hits.insert(hits.end(), std::make_move_iterator(hits_neg.begin()), std::make_move_iterator(hits_neg.end())); for (const AABBMesh::hit_result &hit : hits) { double squared_distance = (mesh_point - hit.position()).squaredNorm(); if (closest.has_value() && closest->squared_distance < squared_distance) continue; - SurfacePoint surface_point(hit.position().cast(), hit.normal().cast()); - closest = Hit(key, surface_point, squared_distance); + closest = Hit{{hit.position(), hit.normal()}, key, squared_distance}; } } return closest; } -std::optional RaycastManager::closest(const Vec3d &point, const ISkip *skip) const { - std::optional closest; +std::optional RaycastManager::closest(const Vec3d &point, const ISkip *skip) const +{ + std::optional closest; for (const auto &item : m_transformations) { const TrKey &key = item.first; size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; - auto raycaster_it = std::find_if(m_raycasters.begin(), m_raycasters.end(), - [volume_id](const RaycastManager::Raycaster &it) -> bool { return volume_id == it.first; }); - if (raycaster_it == m_raycasters.end()) - continue; - const MeshRaycaster &raycaster = *(raycaster_it->second); + const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + if (mesh == nullptr) continue; const Transform3d &transformation = item.second; Transform3d tr_inv = transformation.inverse(); - Vec3d mesh_point_d = tr_inv * point; - Vec3f mesh_point_f = mesh_point_d.cast(); - Vec3f n; - Vec3f p = raycaster.get_closest_point(mesh_point_f, &n); - double squared_distance = (mesh_point_f - p).squaredNorm(); + Vec3d mesh_point = tr_inv * point; + + int face_idx = 0; + Vec3d closest_point; + Vec3d pointd = point.cast(); + mesh->squared_distance(pointd, face_idx, closest_point); + + double squared_distance = (mesh_point - closest_point).squaredNorm(); if (closest.has_value() && closest->squared_distance < squared_distance) continue; - SurfacePoint surface_point(p,n); - closest = Hit(key, surface_point, squared_distance); + + closest = ClosePoint{key, closest_point, squared_distance}; } return closest; } Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const { + // TODO: transformations are sorted use lower bound auto item = std::find_if(m_transformations.begin(), m_transformations.end(), [&tr_key](const TrItem &it) -> bool { @@ -255,4 +204,78 @@ Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) cons }); if (item == m_transformations.end()) return Transform3d::Identity(); return item->second; -} \ No newline at end of file +} + +void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip) +{ + // check if volume was removed + std::vector removed_meshes(meshes.size(), {true}); + bool need_sort = false; + // actualize MeshRaycaster + for (const ModelVolume *volume : volumes) { + size_t oid = volume->id().id; + if (skip != nullptr && skip->skip(oid)) + continue; + auto item = std::find_if(meshes.begin(), meshes.end(), [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); + if (item == meshes.end()) { + // add new raycaster + bool calculate_epsilon = true; + auto mesh = std::make_unique(volume->mesh(), calculate_epsilon); + meshes.emplace_back(std::make_pair(oid, std::move(mesh))); + need_sort = true; + } else { + size_t index = item - meshes.begin(); + removed_meshes[index] = false; + } + } + + // clean other raycasters + for (int i = removed_meshes.size() - 1; i >= 0; --i) + if (removed_meshes[i]) + meshes.erase(meshes.begin() + i); + + // All the time meshes must be sorted by volume id - for faster search + if (need_sort) { + auto is_lower = [](const RaycastManager::Mesh &m1, const RaycastManager::Mesh &m2) { return m1.first < m2.first; }; + std::sort(meshes.begin(), meshes.end(), is_lower); + } +} + +std::optional priv::unproject_on_mesh(const AABBMesh &aabb_mesh, + const Vec2d &mouse_pos, + const Transform3d &transformation, + const Camera &camera) +{ + Vec3d point; + Vec3d direction; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + Transform3d inv = transformation.inverse(); + point = inv * point; + direction = inv.linear() * direction; + std::vector hits = aabb_mesh.query_ray_hits(point, direction); + + if (hits.empty()) + return {}; // no intersection found + + const AABBMesh::hit_result &hit = hits.front(); + return priv::SurfacePoint{hit.position(), hit.normal()}; +} + +const Slic3r::AABBMesh *priv::get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id) +{ + auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) -> bool { return m.first < i; }; + auto it = std::lower_bound(meshes.begin(), meshes.end(), volume_id, is_lower_index); + if (it == meshes.end() || it->first != volume_id) + return nullptr; + return &(*(it->second)); +} + +RaycastManager::TrItems::iterator priv::find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) { + auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &key)->bool { + return priv::is_lower_key(it.first, key); + }; + auto it = std::lower_bound(items.begin(), items.end(), key, fnc); + if (it == items.end() || it->first != key) + return items.end(); + return it; +} diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 5451c4e92e..406e51c86c 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -2,12 +2,12 @@ #define slic3r_RaycastManager_hpp_ #include // unique_ptr -#include // unique_ptr -#include -#include "slic3r/GUI/MeshUtils.hpp" // MeshRaycaster +#include +#include "libslic3r/AABBMesh.hpp" // Structure to cast rays #include "libslic3r/Point.hpp" // Transform3d #include "libslic3r/ObjectID.hpp" #include "libslic3r/Model.hpp" // ModelObjectPtrs, ModelObject, ModelInstance, ModelVolume +#include "slic3r/GUI/Camera.hpp" namespace Slic3r::GUI{ @@ -17,19 +17,22 @@ namespace Slic3r::GUI{ /// class RaycastManager { - // ModelVolume.id - using Raycaster = std::pair >; - std::vector m_raycasters; +// Public structures used by RaycastManager +public: - // Key for transformation consist of unique volume and instance + // ModelVolume.id + using Mesh = std::pair >; + using Meshes = std::vector; + + // Key for transformation consist of unique volume and instance id ... ObjectId() // ModelInstance, ModelVolume using TrKey = std::pair; using TrItem = std::pair; - std::vector m_transformations; + using TrItems = std::vector; - // should contain shared pointer to camera but it is not shared pointer so it need it every time when casts rays - -public: + /// + /// Interface for identify allowed volumes to cast rays. + /// class ISkip{ public: virtual ~ISkip() = default; @@ -42,6 +45,39 @@ public: virtual bool skip(const size_t &model_volume_id) const { return false; } }; + // TODO: it is more general object move outside of this class + template + struct SurfacePoint { + using Vec3 = Eigen::Matrix; + Vec3 position = Vec3::Zero(); + Vec3 normal = Vec3::UnitZ(); + }; + + struct Hit : public SurfacePoint + { + TrKey tr_key; + double squared_distance; + }; + + struct ClosePoint + { + TrKey tr_key; + Vec3d point; + double squared_distance; + }; + +// Members +private: + // Keep structure to fast cast rays + // meshes are sorted by volume_id for faster search + Meshes m_meshes; + + // Keep transformation of meshes + TrItems m_transformations; + // Note: one mesh could have more transformations ... instances + +public: + /// /// Actualize raycasters + transformation /// Detection of removed object @@ -53,27 +89,6 @@ public: void actualize(const ModelObject *object, const ISkip *skip = nullptr); void actualize(const ModelInstance *instance, const ISkip *skip = nullptr); - // TODO: it is more general object move outside of this class - struct SurfacePoint - { - Vec3f position = Vec3f::Zero(); - Vec3f normal = Vec3f::UnitZ(); - SurfacePoint() = default; - SurfacePoint(Vec3f position, Vec3f normal) - : position(position), normal(normal) - {} - }; - - struct Hit: public SurfacePoint - { - using Key = TrKey; - Key tr_key; - double squared_distance; - Hit(const Key& tr_key, const SurfacePoint& surface_point, double squared_distance) - : SurfacePoint(surface_point), tr_key(tr_key), squared_distance(squared_distance) - {} - }; - class SkipVolume: public ISkip { size_t volume_id; @@ -95,7 +110,6 @@ public: /// /// Unproject on mesh by Mesh raycasters - /// Note: Function use current camera position from wxGetApp() /// /// Position of mouse on screen /// Projection params @@ -121,7 +135,7 @@ public: /// Point /// Define which caster will be skipped, null mean no skip /// - std::optional closest(const Vec3d &point, const ISkip *skip = nullptr) const; + std::optional closest(const Vec3d &point, const ISkip *skip = nullptr) const; /// /// Getter on transformation from hitted volume to world From b82f1fe8187a7873c795b08b114869e9b22e17e2 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 17 Feb 2023 08:16:54 +0100 Subject: [PATCH 24/64] Move over surface with relative transformation --- src/libslic3r/Emboss.cpp | 39 ++++++++--- src/libslic3r/Emboss.hpp | 12 +++- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 63 +++++++---------- src/slic3r/Utils/RaycastManager.cpp | 92 ++++++++++++++----------- src/slic3r/Utils/RaycastManager.hpp | 7 +- 5 files changed, 119 insertions(+), 94 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index e3f7454a76..44fa402025 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -1540,8 +1540,28 @@ std::optional Emboss::ProjectZ::unproject(const Vec3d &p, double *depth) return Vec2d(p.x() / SHAPE_SCALE, p.y() / SHAPE_SCALE); } -Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position, - const Vec3f &normal, + +Vec3d Emboss::suggest_up(const Vec3d normal, double up_limit) +{ + // Normal must be 1 + assert(is_approx(normal.norm(), 1.)); + + // wanted up direction of result + Vec3d wanted_up_side = + (std::fabs(normal.z()) > up_limit)? + Vec3d::UnitY() : Vec3d::UnitZ(); + + // create perpendicular unit vector to surface triangle normal vector + // lay on surface of triangle and define up vector for text + Vec3d wanted_up_dir = normal.cross(wanted_up_side).cross(normal); + // normal3d is NOT perpendicular to normal_up_dir + wanted_up_dir.normalize(); + + return wanted_up_dir; +} + +Transform3d Emboss::create_transformation_onto_surface(const Vec3d &position, + const Vec3d &normal, float up_limit) { // up and emboss direction for generated model @@ -1552,28 +1572,27 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position, Vec3d wanted_up_side = Vec3d::UnitZ(); if (std::fabs(normal.z()) > up_limit) wanted_up_side = Vec3d::UnitY(); - Vec3d wanted_emboss_dir = normal.cast(); // after cast from float it needs to be normalized again - wanted_emboss_dir.normalize(); + assert(is_approx(normal.norm(), 1.)); // create perpendicular unit vector to surface triangle normal vector // lay on surface of triangle and define up vector for text - Vec3d wanted_up_dir = wanted_emboss_dir + Vec3d wanted_up_dir = normal .cross(wanted_up_side) - .cross(wanted_emboss_dir); + .cross(normal); // normal3d is NOT perpendicular to normal_up_dir wanted_up_dir.normalize(); // perpendicular to emboss vector of text and normal Vec3d axis_view; double angle_view; - if (wanted_emboss_dir == -Vec3d::UnitZ()) { + if (normal == -Vec3d::UnitZ()) { // text_emboss_dir has opposit direction to wanted_emboss_dir axis_view = Vec3d::UnitY(); angle_view = M_PI; } else { - axis_view = text_emboss_dir.cross(wanted_emboss_dir); - angle_view = std::acos(text_emboss_dir.dot(wanted_emboss_dir)); // in rad + axis_view = text_emboss_dir.cross(normal); + angle_view = std::acos(text_emboss_dir.dot(normal)); // in rad axis_view.normalize(); } @@ -1593,7 +1612,7 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position, Eigen::AngleAxis up_rot(angle_up, text_emboss_dir); Transform3d transform = Transform3d::Identity(); - transform.translate(position.cast()); + transform.translate(position); transform.rotate(view_rot); transform.rotate(up_rot); return transform; diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index ca27afe45c..cf15aa2cb9 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -273,7 +273,15 @@ namespace Emboss /// Define transformation from 2d to 3d(orientation, position, scale, ...) /// Projected shape into space indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection); - + + /// + /// Suggest wanted up vector of embossed text by emboss direction + /// + /// Normalized vector of emboss direction in world + /// Is compared with normal.z to suggest up direction + /// Wanted up vector + Vec3d suggest_up(const Vec3d normal, double up_limit = 0.9); + /// /// Create transformation for emboss text object to lay on surface point /// @@ -282,7 +290,7 @@ namespace Emboss /// Is compared with normal.z to suggest up direction /// Transformation onto surface point Transform3d create_transformation_onto_surface( - const Vec3f &position, const Vec3f &normal, float up_limit = 0.9f); + const Vec3d &position, const Vec3d &normal, float up_limit = 0.9f); class ProjectZ : public IProjection { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index c6ce9da8fe..4493fba38e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -828,42 +828,38 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2d mouse_pos = mouse_coord.cast(); Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset; const Camera &camera = wxGetApp().plater()->get_camera(); - auto hit = m_raycast_manager.unproject(offseted_mouse, camera, &m_surface_drag->condition); + auto hit = m_raycast_manager.ray_from_camera(offseted_mouse, camera, &m_surface_drag->condition); m_surface_drag->exist_hit = hit.has_value(); if (!hit.has_value()) { // cross hair need redraw m_parent.set_as_dirty(); return true; } - // Calculate offset: transformation to wanted position - Transform3d hit_to_world = m_raycast_manager.get_transformation(hit->tr_key); - Transform3d hit_to_instance = hit_to_world * m_surface_drag->instance_inv; - Transform3d hit_to_volume = hit_to_instance * m_surface_drag->volume_tr.inverse(); - - Vec3d hit_position = hit->position.cast(); - Vec3d offset_volume = hit_to_volume * hit_position; - Transform3d translate{Eigen::Translation(offset_volume)}; - - Transform3d rotate; - // normal transformed to volume - Vec3d hit_normal = hit->normal.cast(); - Vec3d z_t = hit_to_volume.linear() * hit_normal; - bool exist_rotate = priv::allign_z(z_t, rotate); - // Edit position from right - Transform3d volume_new = m_surface_drag->volume_tr * translate * rotate; const Transform3d &instance = m_surface_drag->gl_volume->get_instance_transformation().get_matrix(); const Transform3d &volume = m_surface_drag->volume_tr; - Vec3d hit_position_world = hit_to_world * hit_position; - Vec3d hit_normal_world = hit_to_world.linear() * hit_normal; + // Calculate offset: transformation to wanted position + Transform3d text_to_world_old = instance * volume; + { + // Reset skew of the text Z axis: + // Project the old Z axis into a new Z axis, which is perpendicular to the old XY plane. + Vec3d old_z = text_to_world_old.linear().col(2); + Vec3d new_z = text_to_world_old.linear().col(0).cross(text_to_world_old.linear().col(1)); + text_to_world_old.linear().col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); + } - // REWRITE transformation - Transform3d volume_R = priv::surface_transformR(hit_position_world, hit_normal_world, volume, instance); - Transform3d volume_L = priv::surface_transformL(hit_position_world, hit_normal_world, volume, instance); - Transform3d volume_2 = priv::surface_transform2(hit_position_world, hit_normal_world, volume, instance); - Transform3d volume_3 = priv::surface_transform3(hit_position_world, hit_normal_world, volume, instance); - volume_new = volume_R; + // normal transformed to volume + Vec3d text_z_world = text_to_world_old.linear() * Vec3d::UnitZ(); + auto z_rotation = Eigen::Quaternion::FromTwoVectors(text_z_world, hit->normal); + Transform3d text_to_world_new = z_rotation * text_to_world_old; + + // Fix up vector ?? + //auto y_rotation = Eigen::Quaternion::FromTwoVectors(text_y_world, hit->normal); + + // Edit position from right + Transform3d volume_new{Eigen::Translation(m_surface_drag->instance_inv * hit->position)}; + volume_new.linear() = instance.linear().inverse() * text_to_world_new.linear(); assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) @@ -871,14 +867,8 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // Check scale in world // Calculate Scale to keep size after move over scaled surface - Transform3d current_world = instance * volume; - auto current_world_linear = current_world.linear(); - - Transform3d wanted_world = instance * volume_new; - auto wanted_world_linear = wanted_world.linear(); - - m_surface_drag->y_scale = priv::calc_scale(current_world_linear, wanted_world_linear, Vec3d::UnitY()); - m_surface_drag->z_scale = priv::calc_scale(current_world_linear, wanted_world_linear, Vec3d::UnitZ()); + m_surface_drag->y_scale = priv::calc_scale(text_to_world_old.linear(), text_to_world_new.linear(), Vec3d::UnitY()); + m_surface_drag->z_scale = priv::calc_scale(text_to_world_old.linear(), text_to_world_new.linear(), Vec3d::UnitZ()); // recalculate rotation for scaled volume //Transform3d hit_to_volume2 = hit_to_instance * (m_surface_drag->volume_tr*scale).inverse(); @@ -4245,7 +4235,7 @@ bool priv::start_create_volume_on_surface_job( raycaster.actualize(obj, &cond); const Camera &camera = plater->get_camera(); - std::optional hit = raycaster.unproject(screen_coor, camera); + std::optional hit = raycaster.ray_from_camera(screen_coor, camera, &cond); // context menu for add text could be open only by right click on an // object. After right click, object is selected and object_idx is set @@ -4253,15 +4243,14 @@ bool priv::start_create_volume_on_surface_job( if (!hit.has_value()) return false; - Transform3d hit_to_world = raycaster.get_transformation(hit->tr_key); // priv::reset_skew(hit_to_world); Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); // Create result volume transformation - Transform3d surface_trmat = create_transformation_onto_surface(hit->position.cast(), hit->normal.cast()); + Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); - Transform3d world_new = hit_to_world * surface_trmat; + Transform3d world_new = surface_trmat; // Reset skew priv::reset_skew_respect_z(world_new); diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index fad41424f9..3f34c37fcd 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -19,14 +19,6 @@ static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager:: return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; } static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { return is_lower_key(i1.first, i2.first); }; - - // Copy functionality from MeshRaycaster::unproject_on_mesh without filtering -using SurfacePoint = RaycastManager::SurfacePoint; -static std::optional unproject_on_mesh(const AABBMesh &aabb_mesh, - const Vec2d &mouse_pos, - const Transform3d &transformation, - const Camera &camera); - } void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) @@ -108,11 +100,21 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) if (need_sort) std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } - -std::optional RaycastManager::unproject( + +std::optional RaycastManager::ray_from_camera( const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const { - std::optional closest; + // Improve it is not neccessaru to use AABBMesh and calc normal in + + struct Result + { + const AABBMesh *mesh = nullptr; + double squared_distance; + int face; + Vec3d hit_world; + const Transform3d *tramsformation; + const TrKey *key; + }result; for (const auto &item : m_transformations) { const TrKey &key = item.first; size_t volume_id = key.second; @@ -120,18 +122,46 @@ std::optional RaycastManager::unproject( const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; const Transform3d &transformation = item.second; - auto surface_point_opt = - priv::unproject_on_mesh(*mesh, mouse_pos, transformation, camera); - if (!surface_point_opt.has_value()) + + Vec3d point; + Vec3d direction; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + Transform3d inv = transformation.inverse(); + point = inv * point; + direction = inv.linear() * direction; + std::vector hits = mesh->query_ray_hits(point, direction); + if (hits.empty()) continue; // no intersection found + + const AABBMesh::hit_result &hit = hits.front(); + + // convert to world + Vec3d hit_world = transformation * hit.position(); + double squared_distance = (camera.get_position() - hit_world).squaredNorm(); + if (result.mesh != nullptr && + result.squared_distance < squared_distance) continue; - Vec3d act_hit_tr = transformation * surface_point_opt->position.cast(); - double squared_distance = (camera.get_position() - act_hit_tr).squaredNorm(); - if (closest.has_value() && - closest->squared_distance < squared_distance) - continue; - closest = Hit{*surface_point_opt, key, squared_distance}; + + result.mesh = mesh; + result.squared_distance = squared_distance; + result.face = hit.face(); + result.hit_world = hit_world; + result.tramsformation = &transformation; + result.key = &key; } - return closest; + + if (result.mesh == nullptr) + return {}; + + const Vec3i tri = result.mesh->indices(result.face); + Vec3d pts[3]; + auto tr = result.tramsformation->linear(); + for (int i = 0; i < 3; ++i) + pts[i] = tr * result.mesh->vertices(tri[i]).cast(); + Vec3d normal_world = (pts[1] - pts[0]).cross(pts[2] - pts[1]); + normal_world.normalize(); + + SurfacePoint point_world{result.hit_world, normal_world}; + return RaycastManager::Hit{point_world, *result.key, result.squared_distance}; } std::optional RaycastManager::unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const @@ -241,26 +271,6 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu } } -std::optional priv::unproject_on_mesh(const AABBMesh &aabb_mesh, - const Vec2d &mouse_pos, - const Transform3d &transformation, - const Camera &camera) -{ - Vec3d point; - Vec3d direction; - CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); - Transform3d inv = transformation.inverse(); - point = inv * point; - direction = inv.linear() * direction; - std::vector hits = aabb_mesh.query_ray_hits(point, direction); - - if (hits.empty()) - return {}; // no intersection found - - const AABBMesh::hit_result &hit = hits.front(); - return priv::SurfacePoint{hit.position(), hit.normal()}; -} - const Slic3r::AABBMesh *priv::get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id) { auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) -> bool { return m.first < i; }; diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 406e51c86c..5bd28d00a6 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -114,10 +114,9 @@ public: /// Position of mouse on screen /// Projection params /// Define which caster will be skipped, null mean no skip - /// Position on surface, normal direction and transformation key, which define hitted object instance - std::optional unproject(const Vec2d &mouse_pos, - const Camera &camera, - const ISkip *skip = nullptr) const; + /// Position on surface, normal direction in world coorinate + /// + key, to know hitted instance and volume + std::optional ray_from_camera(const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip = nullptr) const; /// /// Unproject Ray(point direction) on mesh by MeshRaycasters From 5be8e41545f8202b1b973ba1d82368f20ef8ee1e Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 17 Feb 2023 10:13:54 +0100 Subject: [PATCH 25/64] Clean up calculation of transformation --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 251 ++++-------------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 19 +- 2 files changed, 36 insertions(+), 234 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 4493fba38e..5aba107287 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -551,167 +551,6 @@ void reset_skew(Transform3d& m) m = Eigen::Translation3d(m.translation()) * Transform3d(u * Eigen::Scaling(new_scale_factor(s)) * v.transpose());// * mirror; } -void reset_skew_respect_z(Transform3d &m) -{ - Vec3d z_before = m * Vec3d::UnitZ(); - priv::reset_skew(m); - Vec3d z_after = m * Vec3d::UnitZ(); - - Transform3d rot; // = Transform3d::Identity(); - if (priv::allign_vec(z_after, z_before, rot)) - m = m * rot; -} - -// Multiply from right -static Transform3d surface_transformR(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { - Transform3d to_volume = (i * v).inverse(); - Vec3d offset_volume = to_volume * p; - Transform3d translate{Eigen::Translation(offset_volume)}; - - // new transformation for volume - Transform3d v_new = v * translate; - - // rotation when exists - Transform3d rotate; - - // normal transformed to volume - Vec3d z_t = to_volume.linear() * n; - if (priv::allign_z(z_t, rotate)) - v_new = v_new * rotate; - - // Reset skew in world - Transform3d w_new = i * v_new; - priv::reset_skew_respect_z(w_new); - v_new = i.inverse() * w_new; - - return v_new; -} - -/// -/// Create transformation for volume to move over surface -/// Multiply from Left side - NOT WORK - with scaled instances -/// -/// Point in world coordinate -/// Normal in world coordinate - orientation -/// Original volume transformation -/// Instance transformation -/// Transformation of volume to lay on surface -static Transform3d surface_transformL(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { - // w .. original world - Transform3d w = i * v; - - // remove already existing of skew before calc rotation - // priv::reset_skew(w); - - // z .. unit z vector in world coordinate - Vec3d z = w.linear() * Vec3d::UnitZ(); - Transform3d rot = Transform3d::Identity(); - bool exist_rot = priv::allign_vec(z, n, rot); - - // rot_w .. new rotation applied on world - Transform3d rot_w = rot * w; - - // p0 .. Zero of volume in world - Vec3d p0 = rot_w * Vec3d::Zero(); - Vec3d offset = p - p0; // in world - Transform3d tr{Eigen::Translation(offset)}; - - // w2 .. wanted world transformation - Transform3d w2 = tr * rot_w; - - //priv::reset_skew_respect_z(w2); - - // _ .. inverse - // i_ .. instance inverse - Transform3d i_ = i.inverse(); - - // w = i * v \\ left multiply by i_ - // i_ * w = i_ * i * v - // v = i_ * w - return i_ * w2; - // NOTE: Do not keep scale of text when move over scaled instance -} - -// transformation inside of instance -static Transform3d surface_transform2(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) -{ - // _ .. inverse - // i_ .. instance inverse - Transform3d i_ = i.inverse(); - Vec3d pp = i_ * p; - Vec3d nn = i_.linear() * n.normalized(); - nn.normalize(); - - // z .. unit z vector in world coordinate - Vec3d z = v * Vec3d::UnitZ(); - z.normalize(); - - Transform3d rot = Transform3d::Identity(); - bool exist_rot = priv::allign_vec(z, nn, rot); - - // rot_w .. new rotation applied on world - Transform3d rotated = rot * v; - - // p0 .. Zero of volume in world - Vec3d p0 = rotated * Vec3d::Zero(); - Vec3d offset = pp - p0; // in world - Transform3d tr{Eigen::Translation(offset)}; - Transform3d volume_new = tr * rotated; - //return volume_new; - - // Remove skew in world - Transform3d world_new = i * volume_new; - reset_skew(world_new); - volume_new = i_ * world_new; - - return volume_new; -} - -// work in space defined by SVD -static Transform3d surface_transform3(const Vec3d &p, const Vec3d &n, const Transform3d &v, const Transform3d &i) { - // w .. original world - Transform3d w = i * v; - - const Eigen::JacobiSVD svd1(w.linear(), Eigen::ComputeFullU | Eigen::ComputeFullV); - Matrix3d u1 = svd1.matrixU(); - Matrix3d v1 = svd1.matrixV(); - Matrix3d s1 = svd1.singularValues().asDiagonal(); - Transform3d tr1(Eigen::Translation3d(w.translation())); - //Transform3d test = tr1 * Transform3d(u1 * s1 * v1.transpose()); - - // modification of world - Transform3d mod(s1 * v1.transpose()); - Transform3d mod_ = mod.inverse(); - Transform3d w_mod = w * mod_; - - Vec3d nn = mod_.linear() * n; - // z .. unit z vector in world coordinate - Vec3d z = w_mod.linear() * Vec3d::UnitZ(); - Transform3d rot = Transform3d::Identity(); - bool exist_rot = priv::allign_vec(z, nn, rot); - - // rot_w .. new rotation applied on world - Transform3d rot_w = rot * w; - - // p0 .. Zero of volume in world - Vec3d p0 = rot_w * Vec3d::Zero(); - Vec3d offset = p - p0; // in world - Transform3d tr{Eigen::Translation(offset)}; - - // w2 .. wanted world transformation - Transform3d w2 = tr * rot_w; - - // _ .. inverse - // i_ .. instance inverse - Transform3d i_ = i.inverse(); - - // w = i * v \\ left multiply by i_ - // i_ * w = i_ * i * v - // v = i_ * w - return i_ * w2; - // NOTE: Do not keep scale of text when move over scaled instance -} - } bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) @@ -725,15 +564,15 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // Update surface by new position bool need_process = m_volume->text_configuration->style.prop.use_surface; - if (m_surface_drag->y_scale.has_value()) { - m_style_manager.get_style().prop.size_in_mm *= (*m_surface_drag->y_scale); - need_process |= set_height(); - } + //if (m_surface_drag->y_scale.has_value()) { + // m_style_manager.get_style().prop.size_in_mm *= (*m_surface_drag->y_scale); + // need_process |= set_height(); + //} - if (m_surface_drag->z_scale.has_value()) { - m_style_manager.get_style().prop.emboss *= (*m_surface_drag->z_scale); - need_process |= set_depth(); - } + //if (m_surface_drag->z_scale.has_value()) { + // m_style_manager.get_style().prop.emboss *= (*m_surface_drag->z_scale); + // need_process |= set_depth(); + //} if (need_process) process(); @@ -800,14 +639,16 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); Vec2d mouse_pos = mouse_coord.cast(); Vec2d mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume); - Transform3d instance_inv = gl_volume->get_instance_transformation().get_matrix().inverse(); Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix(); TextConfiguration &tc = *m_volume->text_configuration; // fix baked transformation from .3mf store process if (tc.fix_3mf_tr.has_value()) volume_tr = volume_tr * tc.fix_3mf_tr->inverse(); - m_surface_drag = SurfaceDrag{mouse_offset, instance_inv, volume_tr, gl_volume, condition}; + Transform3d instance_tr = gl_volume->get_instance_transformation().get_matrix(); + Transform3d instance_tr_inv = instance_tr.inverse(); + Transform3d world_tr = instance_tr * volume_tr; + m_surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition}; // Cancel job to prevent interuption of dragging (duplicit result) if (m_job_cancel != nullptr) @@ -836,45 +677,36 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) return true; } - const Transform3d &instance = m_surface_drag->gl_volume->get_instance_transformation().get_matrix(); - const Transform3d &volume = m_surface_drag->volume_tr; - + auto world_linear = m_surface_drag->world.linear(); // Calculate offset: transformation to wanted position - Transform3d text_to_world_old = instance * volume; { // Reset skew of the text Z axis: // Project the old Z axis into a new Z axis, which is perpendicular to the old XY plane. - Vec3d old_z = text_to_world_old.linear().col(2); - Vec3d new_z = text_to_world_old.linear().col(0).cross(text_to_world_old.linear().col(1)); - text_to_world_old.linear().col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); + Vec3d old_z = world_linear.col(2); + Vec3d new_z = world_linear.col(0).cross(world_linear.col(1)); + world_linear.col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); } - // normal transformed to volume - Vec3d text_z_world = text_to_world_old.linear() * Vec3d::UnitZ(); + Vec3d text_z_world = world_linear.col(2); // world_linear * Vec3d::UnitZ() auto z_rotation = Eigen::Quaternion::FromTwoVectors(text_z_world, hit->normal); - Transform3d text_to_world_new = z_rotation * text_to_world_old; + Transform3d world_new = z_rotation * m_surface_drag->world; + auto world_new_linear = world_new.linear(); // Fix up vector ?? - //auto y_rotation = Eigen::Quaternion::FromTwoVectors(text_y_world, hit->normal); + //auto y_rotation = Eigen::Quaternion::FromTwoVectors(text_y_world, hit->normal); // Edit position from right Transform3d volume_new{Eigen::Translation(m_surface_drag->instance_inv * hit->position)}; - volume_new.linear() = instance.linear().inverse() * text_to_world_new.linear(); + volume_new.linear() = m_surface_drag->instance_inv.linear() * world_new_linear; + // Check that transformation matrix is valid transformation assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) return true; - // Check scale in world - // Calculate Scale to keep size after move over scaled surface - m_surface_drag->y_scale = priv::calc_scale(text_to_world_old.linear(), text_to_world_new.linear(), Vec3d::UnitY()); - m_surface_drag->z_scale = priv::calc_scale(text_to_world_old.linear(), text_to_world_new.linear(), Vec3d::UnitZ()); - - // recalculate rotation for scaled volume - //Transform3d hit_to_volume2 = hit_to_instance * (m_surface_drag->volume_tr*scale).inverse(); - //z_t = hit_to_volume2.linear() * hit->normal.cast(); - //bool exist_rotate2 = priv::allign_z(z_t, rotate); - //volume_tr = m_surface_drag->volume_tr * translate * rotate * scale; + // Check that scale in world did not changed + assert(!priv::calc_scale(world_linear, world_new_linear, Vec3d::UnitY()).has_value()); + assert(!priv::calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value()); const TextConfiguration &tc = *m_volume->text_configuration; // fix baked transformation from .3mf store process @@ -888,7 +720,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) volume_new.translate(translate); } - // Update transformation forf all instances + // Update transformation for all instances for (GLVolume *vol : m_parent.get_volumes().volumes) { if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() || vol->volume_idx() != m_surface_drag->gl_volume->volume_idx()) @@ -936,23 +768,6 @@ bool GLGizmoEmboss::on_init() std::string GLGizmoEmboss::on_get_name() const { return _u8L("Emboss"); } void GLGizmoEmboss::on_render() { - // Render debug view to surface move - if (m_surface_drag.has_value()) { - auto glvol = priv::get_gl_volume(m_parent.get_selection()); - auto tr = glvol->get_instance_transformation().get_matrix(); - CoordAxes from; - from.set_origin(m_surface_drag->from); - //from.render(tr, 2.); - - CoordAxes to; - to.set_origin(m_surface_drag->to); - //to.render(tr, 2.); - - CoordAxes axe; - axe.render(m_surface_drag->f_tr); - axe.render(m_surface_drag->t_tr); - } - // no volume selected if (m_volume == nullptr || priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr) @@ -3745,12 +3560,12 @@ void GLGizmoEmboss::draw_advanced() if (ImGui::Button(_u8L("Reset scale").c_str())) { GLVolume *gl_volume = priv::get_gl_volume(m_parent); if (gl_volume != nullptr) { - Transform3d w = gl_volume->world_matrix(); - priv::reset_skew_respect_z(w); - Transform3d i = gl_volume->get_instance_transformation().get_matrix(); - Transform3d v_new = i.inverse() * w; - gl_volume->set_volume_transformation(v_new); - m_parent.do_move(L("Reset scale")); + //Transform3d w = gl_volume->world_matrix(); + //priv::reset_skew_respect_z(w); + //Transform3d i = gl_volume->get_instance_transformation().get_matrix(); + //Transform3d v_new = i.inverse() * w; + //gl_volume->set_volume_transformation(v_new); + //m_parent.do_move(L("Reset scale")); } } else if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", _u8L("Reset skew of text to be normal in world").c_str()); @@ -4253,7 +4068,7 @@ bool priv::start_create_volume_on_surface_job( Transform3d world_new = surface_trmat; // Reset skew - priv::reset_skew_respect_z(world_new); + //priv::reset_skew_respect_z(world_new); Transform3d volume_trmat = instance.inverse() * world_new; start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index e1fded204d..209d025fbb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -317,13 +317,13 @@ private: // hold screen coor offset of cursor from object center Vec2d mouse_offset; + // Start dragging text transformations to world + Transform3d world; + // Invers transformation of text volume instance // Help convert world transformation to instance space Transform3d instance_inv; - // Start dragging volume transformation - Transform3d volume_tr; - // Dragged gl volume GLVolume *gl_volume; @@ -331,19 +331,6 @@ private: RaycastManager::AllowVolumes condition; bool exist_hit = true; - - // Visuzalization - Vec3d from = Vec3d::Zero(); - Vec3d to = Vec3d::Zero(); - - Vec3d from_dir = Vec3d::UnitZ(); - Vec3d to_dir = Vec3d::UnitZ(); - - Transform3d f_tr = Transform3d::Identity(); - Transform3d t_tr = Transform3d::Identity(); - - std::optional y_scale; - std::optional z_scale; }; // Keep data about dragging only during drag&drop std::optional m_surface_drag; From e23c89315b50224fecbbb64364bb6062da8b567a Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 17 Feb 2023 14:07:49 +0100 Subject: [PATCH 26/64] Reset for up vector --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 41 ++++++++++++------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 5aba107287..ce1c98a356 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -561,23 +561,11 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) // write transformation from UI into model m_parent.do_move(L("Surface move")); - // Update surface by new position - bool need_process = m_volume->text_configuration->style.prop.use_surface; - - //if (m_surface_drag->y_scale.has_value()) { - // m_style_manager.get_style().prop.size_in_mm *= (*m_surface_drag->y_scale); - // need_process |= set_height(); - //} - - //if (m_surface_drag->z_scale.has_value()) { - // m_style_manager.get_style().prop.emboss *= (*m_surface_drag->z_scale); - // need_process |= set_depth(); - //} - - if (need_process) + // Update surface by new position + if (m_volume->text_configuration->style.prop.use_surface) process(); - // calculate scale + // Show correct value of height & depth inside of inputs calculate_scale(); // allow moving with object again @@ -3557,15 +3545,24 @@ void GLGizmoEmboss::draw_advanced() } ImGui::SameLine(); - if (ImGui::Button(_u8L("Reset scale").c_str())) { + if (ImGui::Button(_u8L("Reset Up").c_str())) { GLVolume *gl_volume = priv::get_gl_volume(m_parent); if (gl_volume != nullptr) { - //Transform3d w = gl_volume->world_matrix(); - //priv::reset_skew_respect_z(w); - //Transform3d i = gl_volume->get_instance_transformation().get_matrix(); - //Transform3d v_new = i.inverse() * w; - //gl_volume->set_volume_transformation(v_new); - //m_parent.do_move(L("Reset scale")); + Transform3d world = gl_volume->world_matrix(); + auto world_linear = world.linear(); + Vec3d z_world = world_linear.col(2); + z_world.normalize(); + Vec3d wanted_up = suggest_up(z_world); + + Vec3d y_world = world_linear.col(1); + auto z_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); + Transform3d world_new = z_rotation * world; + auto world_new_linear = world_new.linear(); + Transform3d volume_new = gl_volume->get_volume_transformation().get_matrix(); + Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); + volume_new.linear() = instance.linear().inverse() * world_new.linear(); + gl_volume->set_volume_transformation(volume_new); + m_parent.do_move(L("Reset up vector")); } } else if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", _u8L("Reset skew of text to be normal in world").c_str()); From 4c321cf554661d2532412db23983d00b8d1fbc3b Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 17 Feb 2023 16:49:55 +0100 Subject: [PATCH 27/64] Keep up rotation --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index ce1c98a356..45e2623e04 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -680,8 +680,19 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Transform3d world_new = z_rotation * m_surface_drag->world; auto world_new_linear = world_new.linear(); - // Fix up vector ?? - //auto y_rotation = Eigen::Quaternion::FromTwoVectors(text_y_world, hit->normal); + if (true) + { + // Fix direction of up vector + Vec3d z_world = world_new_linear.col(2); + z_world.normalize(); + Vec3d wanted_up = suggest_up(z_world); + + Vec3d y_world = world_new_linear.col(1); + auto y_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); + + world_new = y_rotation * world_new; + world_new_linear = world_new.linear(); + } // Edit position from right Transform3d volume_new{Eigen::Translation(m_surface_drag->instance_inv * hit->position)}; @@ -701,12 +712,8 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (tc.fix_3mf_tr.has_value()) volume_new = volume_new * (*tc.fix_3mf_tr); - // apply move in Z direction for move with flat surface above texture - const FontProp &prop = tc.style.prop; - if (!prop.use_surface && prop.distance.has_value()) { - Vec3d translate = Vec3d::UnitZ() * (*prop.distance); - volume_new.translate(translate); - } + // apply move in Z direction and rotation by up vector + apply_transformation(tc.style.prop, volume_new); // Update transformation for all instances for (GLVolume *vol : m_parent.get_volumes().volumes) { From d6b81639754c23890e3e43d2ef26efd9fc634083 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Feb 2023 12:52:08 +0100 Subject: [PATCH 28/64] Fix: drag only by text not by object Divide set and reset of text volume --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 53 +++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 1 + 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 45e2623e04..7c0186e110 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -586,13 +586,17 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (m_volume == nullptr) return false; - if (m_parent.get_first_hover_volume_idx() < 0) - return false; - GLVolume *gl_volume = priv::get_gl_volume(m_parent); if (gl_volume == nullptr) return false; + // is text hovered? + const GLVolumePtrs& gl_volumes = m_parent.get_volumes().volumes; + int hovered_idx = m_parent.get_first_hover_volume_idx(); + if (hovered_idx < 0 || hovered_idx >= gl_volumes.size() || + gl_volumes[hovered_idx] != gl_volume) + return false; + // hovered object must be actual text volume const ModelObjectPtrs &objects = m_parent.get_model()->objects; if (m_volume != priv::get_model_volume(gl_volume, objects)) @@ -1005,7 +1009,7 @@ void GLGizmoEmboss::on_set_state() _u8L("ERROR: Wait until ends or Cancel process.")); return; } - set_volume(nullptr); + reset_volume(); // Store order and last activ index into app.ini // TODO: what to do when can't store into file? m_style_manager.store_styles_to_app_config(false); @@ -1015,7 +1019,7 @@ void GLGizmoEmboss::on_set_state() wxFontEnumerator::InvalidateCache(); // Try(when exist) set text configuration by volume - set_volume(priv::get_selected_volume(m_parent.get_selection())); + set_volume_by_selection(); // when open window by "T" and no valid volume is selected, so Create new one if (m_volume == nullptr || @@ -1248,25 +1252,22 @@ void GLGizmoEmboss::set_volume_by_selection() m_volume != vol) // when update volume it changed id BUT not pointer ImGuiWrapper::left_inputs(); + if (vol == nullptr) { + reset_volume(); + return; + } + // is select embossed volume? set_volume(vol); } bool GLGizmoEmboss::set_volume(ModelVolume *volume) { - if (volume == nullptr) { - if (m_volume == nullptr) - return false; - m_volume = nullptr; - // TODO: check if it is neccessary to set default text - // Idea is to set default text when create object - set_default_text(); - return false; - } + assert(volume != nullptr); const std::optional tc_opt = volume->text_configuration; if (!tc_opt.has_value()) return false; - const TextConfiguration &tc = *tc_opt; - const EmbossStyle &style = tc.style; + const TextConfiguration &tc = *tc_opt; + const EmbossStyle &style = tc.style; // Could exist OS without getter on face_name, // but it is able to restore font from descriptor @@ -1377,6 +1378,18 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) return true; } +void GLGizmoEmboss::reset_volume() +{ + if (m_volume == nullptr) + return; // already reseted + + m_volume = nullptr; + m_volume_id.id = 0; + // TODO: check if it is neccessary to set default text + // Idea is to set default text when create object + set_default_text(); +} + void GLGizmoEmboss::calculate_scale() { Transform3d to_world = m_parent.get_selection().get_first_volume()->world_matrix(); auto to_world_linear = to_world.linear(); @@ -4069,12 +4082,8 @@ bool priv::start_create_volume_on_surface_job( Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); - Transform3d world_new = surface_trmat; - - // Reset skew - //priv::reset_skew_respect_z(world_new); - - Transform3d volume_trmat = instance.inverse() * world_new; + // new transformation in world coor is surface_trmat + Transform3d volume_trmat = instance.inverse() * surface_trmat; start_create_volume_job(obj, 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 209d025fbb..f17293074b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -89,6 +89,7 @@ private: void set_volume_by_selection(); // load text configuration from volume into gizmo bool set_volume(ModelVolume *volume); + void reset_volume(); // create volume from text - main functionality bool process(); From ead192b43bc594ce9c74b564a7486e753c341b5e Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Feb 2023 14:50:17 +0100 Subject: [PATCH 29/64] calculate angle when set volume --- src/libslic3r/Emboss.cpp | 62 ++++++++++++++++--------- src/libslic3r/Emboss.hpp | 8 ++++ src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 41 ++++++---------- 3 files changed, 61 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 44fa402025..221bfbcb63 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -1544,7 +1544,7 @@ std::optional Emboss::ProjectZ::unproject(const Vec3d &p, double *depth) Vec3d Emboss::suggest_up(const Vec3d normal, double up_limit) { // Normal must be 1 - assert(is_approx(normal.norm(), 1.)); + assert(is_approx(normal.squaredNorm(), 1.)); // wanted up direction of result Vec3d wanted_up_side = @@ -1560,28 +1560,45 @@ Vec3d Emboss::suggest_up(const Vec3d normal, double up_limit) return wanted_up_dir; } +std::optional Emboss::calc_up(const Transform3d &tr, double up_limit) +{ + auto tr_linear = tr.linear(); + // z base of transformation ( tr * UnitZ ) + Vec3d normal = tr_linear.col(2); + // scaled matrix has base with different size + normal.normalize(); + Vec3d suggested = suggest_up(normal); + assert(is_approx(suggested.squaredNorm(), 1.)); + + Vec3d up = tr_linear.col(1); // tr * UnitY() + up.normalize(); + + double dot = suggested.dot(up); + if (dot >= 1. || dot <= -1.) + return {}; // zero angle + + Matrix3d m; + m.row(0) = up; + m.row(1) = suggested; + m.row(2) = normal; + double det = m.determinant(); + + return atan2(det, dot); +} + Transform3d Emboss::create_transformation_onto_surface(const Vec3d &position, const Vec3d &normal, float up_limit) { - // up and emboss direction for generated model - Vec3d text_up_dir = Vec3d::UnitY(); - Vec3d text_emboss_dir = Vec3d::UnitZ(); + // is normalized ? + assert(is_approx(normal.squaredNorm(), 1.)); - // wanted up direction of result - Vec3d wanted_up_side = Vec3d::UnitZ(); - if (std::fabs(normal.z()) > up_limit) wanted_up_side = Vec3d::UnitY(); + // up and emboss direction for generated model + Vec3d up_dir = Vec3d::UnitY(); + Vec3d emboss_dir = Vec3d::UnitZ(); // after cast from float it needs to be normalized again - assert(is_approx(normal.norm(), 1.)); - - // create perpendicular unit vector to surface triangle normal vector - // lay on surface of triangle and define up vector for text - Vec3d wanted_up_dir = normal - .cross(wanted_up_side) - .cross(normal); - // normal3d is NOT perpendicular to normal_up_dir - wanted_up_dir.normalize(); + Vec3d wanted_up_dir = suggest_up(normal, up_limit); // perpendicular to emboss vector of text and normal Vec3d axis_view; @@ -1591,25 +1608,24 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3d &position, axis_view = Vec3d::UnitY(); angle_view = M_PI; } else { - axis_view = text_emboss_dir.cross(normal); - angle_view = std::acos(text_emboss_dir.dot(normal)); // in rad + axis_view = emboss_dir.cross(normal); + angle_view = std::acos(emboss_dir.dot(normal)); // in rad axis_view.normalize(); } Eigen::AngleAxis view_rot(angle_view, axis_view); Vec3d wanterd_up_rotated = view_rot.matrix().inverse() * wanted_up_dir; wanterd_up_rotated.normalize(); - double angle_up = std::acos(text_up_dir.dot(wanterd_up_rotated)); + double angle_up = std::acos(up_dir.dot(wanterd_up_rotated)); - // text_view and text_view2 should have same direction - Vec3d text_view2 = text_up_dir.cross(wanterd_up_rotated); - Vec3d diff_view = text_emboss_dir - text_view2; + Vec3d text_view = up_dir.cross(wanterd_up_rotated); + Vec3d diff_view = emboss_dir - text_view; if (std::fabs(diff_view.x()) > 1. || std::fabs(diff_view.y()) > 1. || std::fabs(diff_view.z()) > 1.) // oposit direction angle_up *= -1.; - Eigen::AngleAxis up_rot(angle_up, text_emboss_dir); + Eigen::AngleAxis up_rot(angle_up, emboss_dir); Transform3d transform = Transform3d::Identity(); transform.translate(position); diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index cf15aa2cb9..82ef4d1ace 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -281,6 +281,14 @@ namespace Emboss /// Is compared with normal.z to suggest up direction /// Wanted up vector Vec3d suggest_up(const Vec3d normal, double up_limit = 0.9); + + /// + /// By transformation calculate angle between suggested and actual up vector + /// + /// Transformation of embossed volume in world + /// Is compared with normal.z to suggest up direction + /// Rotation of suggested up-vector[in rad] in the range [-Pi, Pi], When rotation is not zero + std::optional calc_up(const Transform3d &tr, double up_limit = 0.9); /// /// Create transformation for emboss text object to lay on surface point diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 7c0186e110..b03fb6cc9b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -103,8 +103,12 @@ static const struct Limits } return false; } + } limits; +// Define where is up vector on model +constexpr double up_limit = 0.9; + static bool is_text_empty(const std::string &text){ return text.empty() || text.find_first_not_of(" \n\t\r") == std::string::npos; @@ -1239,7 +1243,8 @@ void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); } void GLGizmoEmboss::set_volume_by_selection() { - ModelVolume *vol = priv::get_selected_volume(m_parent.get_selection()); + const Selection &selection = m_parent.get_selection(); + ModelVolume *vol = priv::get_selected_volume(selection); // is same volume selected? if (vol != nullptr && vol->id() == m_volume_id) return; @@ -1259,6 +1264,13 @@ void GLGizmoEmboss::set_volume_by_selection() // is select embossed volume? set_volume(vol); + + // Check if user changed up vector by rotation or scale out of emboss gizmo + if (m_volume != nullptr) { + Transform3d world = selection.get_first_volume()->world_matrix(); + std::optional angle = calc_up(world, priv::up_limit); + m_volume->text_configuration->style.prop.angle = angle; + } } bool GLGizmoEmboss::set_volume(ModelVolume *volume) @@ -3563,31 +3575,6 @@ void GLGizmoEmboss::draw_advanced() } else if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", _u8L("Use camera direction for text orientation").c_str()); } - - ImGui::SameLine(); - if (ImGui::Button(_u8L("Reset Up").c_str())) { - GLVolume *gl_volume = priv::get_gl_volume(m_parent); - if (gl_volume != nullptr) { - Transform3d world = gl_volume->world_matrix(); - auto world_linear = world.linear(); - Vec3d z_world = world_linear.col(2); - z_world.normalize(); - Vec3d wanted_up = suggest_up(z_world); - - Vec3d y_world = world_linear.col(1); - auto z_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); - Transform3d world_new = z_rotation * world; - auto world_new_linear = world_new.linear(); - Transform3d volume_new = gl_volume->get_volume_transformation().get_matrix(); - Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); - volume_new.linear() = instance.linear().inverse() * world_new.linear(); - gl_volume->set_volume_transformation(volume_new); - m_parent.do_move(L("Reset up vector")); - } - } else if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("%s", _u8L("Reset skew of text to be normal in world").c_str()); - } - #ifdef ALLOW_DEBUG_MODE ImGui::Text("family = %s", (font_prop.family.has_value() ? font_prop.family->c_str() : @@ -4079,7 +4066,7 @@ bool priv::start_create_volume_on_surface_job( Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); // Create result volume transformation - Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal); + Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, priv::up_limit); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); // new transformation in world coor is surface_trmat From ae75599af0d1c9031c1d39436d628694beaf3aa1 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 21 Feb 2023 12:32:53 +0100 Subject: [PATCH 30/64] clean up --- src/libslic3r/Emboss.cpp | 2 +- src/libslic3r/Emboss.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 58 +------------------------ 3 files changed, 3 insertions(+), 59 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 221bfbcb63..0005077795 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -1588,7 +1588,7 @@ std::optional Emboss::calc_up(const Transform3d &tr, double up_limit) Transform3d Emboss::create_transformation_onto_surface(const Vec3d &position, const Vec3d &normal, - float up_limit) + double up_limit) { // is normalized ? assert(is_approx(normal.squaredNorm(), 1.)); diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index 82ef4d1ace..d1ddbb1c37 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -298,7 +298,7 @@ namespace Emboss /// Is compared with normal.z to suggest up direction /// Transformation onto surface point Transform3d create_transformation_onto_surface( - const Vec3d &position, const Vec3d &normal, float up_limit = 0.9f); + const Vec3d &position, const Vec3d &normal, double up_limit = 0.9); class ProjectZ : public IProjection { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index b03fb6cc9b..cbbe279346 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -488,44 +488,6 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu } namespace priv { -static bool allign_vec(const Vec3d &z_f, const Vec3d &z_t, Transform3d &rotate) -{ - Vec3d z_f_norm = z_f.normalized(); - Vec3d z_t_norm = z_t.normalized(); - double cos_angle = z_t_norm.dot(z_f_norm); - - // Calculate rotation of Z-vectors from current to wanted position - rotate = Transform3d::Identity(); - - if (cos_angle == 0.) { - // check that direction is not same - if (z_t_norm.z() > 0.) - return false; - - // opposit direction of z_t and z_f (a.k.a. angle 180 DEG) - rotate = Eigen::AngleAxis(M_PI, Vec3d::UnitX()); - return true; - } else if (cos_angle >= 1. || cos_angle <= -1.) { - // bad cas angle value almost zero angle so no rotation - return false; - } - - // Calculate only when angle is not zero - // Calculate rotation axe from current to wanted inside instance - Vec3d axe = z_t_norm.cross(z_f_norm); - axe.normalize(); - double angle = acos(cos_angle); - rotate = Eigen::AngleAxis(-angle, axe); - return true; -} - -static bool allign_z(const Vec3d &z_t, Transform3d &rotate) -{ - // Transformed unit vector Z direction (f)rom, (t)o - const Vec3d& z_f = Vec3d::UnitZ(); - return allign_vec(Vec3d::UnitZ(), z_t, rotate); -} - // Calculate scale in world static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) { @@ -537,24 +499,6 @@ static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to return {}; // no scale return sqrt(from_scale_sq / to_scale_sq); }; - -// Copy from branch et_transformation --> Geometry -// suggested by @bubnikv -void reset_skew(Transform3d& m) -{ - auto new_scale_factor = [](const Matrix3d& s) { - return pow(s(0, 0) * s(1, 1) * s(2, 2), 1. / 3.); // scale average - }; - - const Eigen::JacobiSVD svd(m.linear(), Eigen::ComputeFullU | Eigen::ComputeFullV); - Matrix3d u = svd.matrixU(); - Matrix3d v = svd.matrixV(); - Matrix3d s = svd.singularValues().asDiagonal(); - - //Matrix3d mirror; - m = Eigen::Translation3d(m.translation()) * Transform3d(u * Eigen::Scaling(new_scale_factor(s)) * v.transpose());// * mirror; -} - } bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) @@ -1529,7 +1473,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, is_outside, std::move(sources)}}; job = std::make_unique(std::move(surface_data)); } else { job = std::make_unique(std::move(data)); From 42857d8ecbcc12bf478c86473763855ed47c2d93 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 21 Feb 2023 16:38:39 +0100 Subject: [PATCH 31/64] Use already existing AABB trees for cast into scene. Remove dependency on camera for RayCastManager. --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 73 +++++++++++++++++++++---- src/slic3r/Utils/RaycastManager.cpp | 56 ++++++++++--------- src/slic3r/Utils/RaycastManager.hpp | 20 +++---- 3 files changed, 103 insertions(+), 46 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index cbbe279346..6a4aa40955 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -167,6 +167,19 @@ static void start_create_volume_job(const ModelObject *object, static GLVolume *get_hovered_gl_volume(const GLCanvas3D &canvas); +/// +/// Unproject on mesh by Mesh raycasters +/// +/// Position of mouse on screen +/// Projection params +/// Define which caster will be skipped, null mean no skip +/// Position on surface, normal direction in world coorinate +/// + key, to know hitted instance and volume +static std::optional ray_from_camera(const RaycastManager &raycaster, + const Vec2d &mouse_pos, + const Camera &camera, + const RaycastManager::ISkip *skip); + /// /// Start job for add new volume on surface of object defined by screen coor /// @@ -175,12 +188,14 @@ static GLVolume *get_hovered_gl_volume(const GLCanvas3D &canvas); /// Mouse position which define position /// Volume to find surface for create /// Ability to ray cast to model +/// 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, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, - RaycastManager &raycaster); + RaycastManager &raycaster, + GLCanvas3D &canvas); /// /// Find volume in selected object with closest convex hull to screen center. @@ -235,7 +250,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); if (gl_volume != nullptr) { // 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)) { + if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, mouse_pos, gl_volume, m_raycast_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); @@ -276,7 +291,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)) { + } else if (!priv::start_create_volume_on_surface_job(emboss_data, volume_type, coor, vol, m_raycast_manager, m_parent)) { // in centroid of convex hull is not hit with object // soo create transfomation on border of object @@ -499,6 +514,29 @@ static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to return {}; // no scale return sqrt(from_scale_sq / to_scale_sq); }; + +RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes& condition) +{ + SceneRaycaster::EType type = SceneRaycaster::EType::Volume; + auto scene_casters = canvas.get_raycasters_for_picking(type); + const std::vector> &casters = *scene_casters; + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + const ModelObjectPtrs &objects = canvas.get_model()->objects; + + RaycastManager::Meshes meshes; + for (const std::shared_ptr &caster : casters) { + int index = SceneRaycaster::decode_id(type, caster->get_id()); + if (index < 0 || index >= gl_volumes.size()) continue; + const GLVolume *gl_volume = gl_volumes[index]; + const ModelVolume *volume = priv::get_model_volume(gl_volume, objects); + size_t id = volume->id().id; + if (condition.skip(id)) + continue; + auto mesh = std::make_unique(caster->get_raycaster()->get_aabb_mesh()); + meshes.emplace_back(std::make_pair(id, std::move(mesh))); + } + return meshes; +} } bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) @@ -568,12 +606,12 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) allowed_volumes_id.emplace_back(v->id().id); } } - RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); - + RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); + RaycastManager::Meshes meshes = priv::create_meshes(m_parent, condition); // initialize raycasters // INFO: It could slows down for big objects // (may be move to thread and do not show drag until it finish) - m_raycast_manager.actualize(instance, &condition); + m_raycast_manager.actualize(instance, &condition, &meshes); // wxCoord == int --> wx/types.h Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); @@ -609,7 +647,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) Vec2d mouse_pos = mouse_coord.cast(); Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset; const Camera &camera = wxGetApp().plater()->get_camera(); - auto hit = m_raycast_manager.ray_from_camera(offseted_mouse, camera, &m_surface_drag->condition); + auto hit = priv::ray_from_camera(m_raycast_manager, offseted_mouse, camera, &m_surface_drag->condition); m_surface_drag->exist_hit = hit.has_value(); if (!hit.has_value()) { // cross hair need redraw @@ -3255,7 +3293,7 @@ std::optional priv::calc_surface_offset(const ModelVolume &volume, Raycas Vec3d direction = to_world.linear() * (-Vec3d::UnitZ()); // ray in direction of text projection(from volume zero to z-dir) - std::optional hit_opt = raycast_manager.unproject(point, direction, &cond); + std::optional hit_opt = raycast_manager.closest_hit(point, direction, &cond); // Try to find closest point when no hit object in emboss direction if (!hit_opt.has_value()) { @@ -3981,8 +4019,19 @@ GLVolume * priv::get_hovered_gl_volume(const GLCanvas3D &canvas) { return volumes[hovered_id]; } +std::optional priv::ray_from_camera(const RaycastManager &raycaster, + const Vec2d &mouse_pos, + const Camera &camera, + const RaycastManager::ISkip *skip) +{ + Vec3d point; + Vec3d direction; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + return raycaster.first_hit(point, direction, skip); +} + bool priv::start_create_volume_on_surface_job( - DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster) + DataBase &emboss_data, ModelVolumeType volume_type, const Vec2d &screen_coor, const GLVolume *gl_volume, RaycastManager &raycaster, GLCanvas3D& canvas) { assert(gl_volume != nullptr); if (gl_volume == nullptr) return false; @@ -3995,10 +4044,12 @@ bool priv::start_create_volume_on_surface_job( ModelObject *obj = objects[object_idx]; size_t vol_id = obj->volumes[gl_volume->volume_idx()]->id().id; auto cond = RaycastManager::AllowVolumes({vol_id}); - raycaster.actualize(obj, &cond); + + RaycastManager::Meshes meshes = priv::create_meshes(canvas, cond); + raycaster.actualize(obj, &cond, &meshes); const Camera &camera = plater->get_camera(); - std::optional hit = raycaster.ray_from_camera(screen_coor, camera, &cond); + std::optional hit = priv::ray_from_camera(raycaster, screen_coor, camera, &cond); // context menu for add text could be open only by right click on an // object. After right click, object is selected and object_idx is set diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 3f34c37fcd..3b0ace2a34 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -1,16 +1,11 @@ #include "RaycastManager.hpp" #include -// include for earn camera -#include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/Plater.hpp" -#include "slic3r/GUI/CameraUtils.hpp" - using namespace Slic3r::GUI; namespace priv { using namespace Slic3r; -static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip); +static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr); static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); static RaycastManager::TrKey create_key(const ModelVolume* volume, const ModelInstance* instance){ return std::make_pair(instance->id().id, volume->id().id); } @@ -21,10 +16,10 @@ static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrI return is_lower_key(i1.first, i2.first); }; } -void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) +void RaycastManager::actualize(const ModelObject *object, const ISkip *skip, Meshes *meshes) { // actualize MeshRaycaster - priv::actualize(m_meshes, object->volumes, skip); + priv::actualize(m_meshes, object->volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); @@ -61,11 +56,12 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip) std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } -void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) { +void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip, Meshes *meshes) +{ const ModelVolumePtrs &volumes = instance->get_object()->volumes; // actualize MeshRaycaster - priv::actualize(m_meshes, volumes, skip); + priv::actualize(m_meshes, volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); @@ -101,11 +97,9 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip) std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } -std::optional RaycastManager::ray_from_camera( - const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip) const +std::optional RaycastManager::first_hit(const Vec3d& point, const Vec3d& direction, const ISkip *skip) const { - // Improve it is not neccessaru to use AABBMesh and calc normal in - + // Improve: it is not neccessaru to use AABBMesh and calc normal for every hit struct Result { const AABBMesh *mesh = nullptr; @@ -115,6 +109,7 @@ std::optional RaycastManager::ray_from_camera( const Transform3d *tramsformation; const TrKey *key; }result; + for (const auto &item : m_transformations) { const TrKey &key = item.first; size_t volume_id = key.second; @@ -122,24 +117,23 @@ std::optional RaycastManager::ray_from_camera( const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; const Transform3d &transformation = item.second; - - Vec3d point; - Vec3d direction; - CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); Transform3d inv = transformation.inverse(); - point = inv * point; - direction = inv.linear() * direction; - std::vector hits = mesh->query_ray_hits(point, direction); + + // transform input into mesh world + Vec3d point_ = inv * point; + Vec3d direction_= inv.linear() * direction; + + std::vector hits = mesh->query_ray_hits(point_, direction_); if (hits.empty()) continue; // no intersection found const AABBMesh::hit_result &hit = hits.front(); // convert to world Vec3d hit_world = transformation * hit.position(); - double squared_distance = (camera.get_position() - hit_world).squaredNorm(); + double squared_distance = (point - hit_world).squaredNorm(); if (result.mesh != nullptr && result.squared_distance < squared_distance) - continue; + continue; // exist closer one result.mesh = mesh; result.squared_distance = squared_distance; @@ -152,6 +146,8 @@ std::optional RaycastManager::ray_from_camera( if (result.mesh == nullptr) return {}; + // Calculate normal from transformed triangle + // NOTE: Anisotropic transformation of normal is not perpendiculat to triangle const Vec3i tri = result.mesh->indices(result.face); Vec3d pts[3]; auto tr = result.tramsformation->linear(); @@ -164,7 +160,7 @@ std::optional RaycastManager::ray_from_camera( return RaycastManager::Hit{point_world, *result.key, result.squared_distance}; } -std::optional RaycastManager::unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const +std::optional RaycastManager::closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const { std::optional closest; for (const auto &item : m_transformations) { @@ -236,7 +232,7 @@ Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) cons return item->second; } -void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip) +void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes* inputs) { // check if volume was removed std::vector removed_meshes(meshes.size(), {true}); @@ -248,6 +244,16 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu continue; auto item = std::find_if(meshes.begin(), meshes.end(), [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); if (item == meshes.end()) { + // exist AABB in inputs ? + if (inputs != nullptr) { + auto input = std::find_if(inputs->begin(), inputs->end(), + [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); + if (input != inputs->end()) { + meshes.emplace_back(std::move(*input)); + continue; + } + } + // add new raycaster bool calculate_epsilon = true; auto mesh = std::make_unique(volume->mesh(), calculate_epsilon); diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 5bd28d00a6..4ef9b7ca76 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -7,7 +7,6 @@ #include "libslic3r/Point.hpp" // Transform3d #include "libslic3r/ObjectID.hpp" #include "libslic3r/Model.hpp" // ModelObjectPtrs, ModelObject, ModelInstance, ModelVolume -#include "slic3r/GUI/Camera.hpp" namespace Slic3r::GUI{ @@ -86,8 +85,9 @@ public: /// /// Model representation /// Condifiton for skip actualization - void actualize(const ModelObject *object, const ISkip *skip = nullptr); - void actualize(const ModelInstance *instance, const ISkip *skip = nullptr); + /// Speed up for already created AABBtrees + void actualize(const ModelObject *object, const ISkip *skip = nullptr, Meshes *meshes = nullptr); + void actualize(const ModelInstance *instance, const ISkip *skip = nullptr, Meshes* meshes = nullptr); class SkipVolume: public ISkip { @@ -109,24 +109,24 @@ public: }; /// - /// Unproject on mesh by Mesh raycasters + /// Unproject on mesh and return closest hit to point in given direction /// - /// Position of mouse on screen - /// Projection params + /// Position in space + /// Casted ray direction /// Define which caster will be skipped, null mean no skip /// Position on surface, normal direction in world coorinate /// + key, to know hitted instance and volume - std::optional ray_from_camera(const Vec2d &mouse_pos, const Camera &camera, const ISkip *skip = nullptr) const; + std::optional first_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const; /// - /// Unproject Ray(point direction) on mesh by MeshRaycasters + /// Unproject Ray(point direction) on mesh to find closest hit of surface in given direction /// NOTE: It inspect also oposit direction of ray !! /// /// Start point for ray - /// Direction of ray + /// Direction of ray, orientation doesn't matter, both are used /// Define which caster will be skipped, null mean no skip /// Position on surface, normal direction and transformation key, which define hitted object instance - std::optional unproject(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const; + std::optional closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip = nullptr) const; /// /// Search of closest point From d3e212486bc336c883805b91f805ebc2aed74c59 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 23 Feb 2023 12:01:26 +0100 Subject: [PATCH 32/64] Try to fix conversion from uri to string path 'file://System/Library/Fonts/Helvetica.ttc#postscript-name=Helvetica' -> '/System/Library/Fonts/Helvetica.ttc' --- src/slic3r/Utils/WxFontUtils.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index a56f331412..cf1939b13d 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -43,6 +43,20 @@ static bool is_valid_ttf(std::string_view file_path) return true; } +static std::string get_path_from_file_uri(const wxString &file_uri) +{ + wxURI uri(file_uri); + const wxString &path = uri.GetPath(); + BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ")."; + std::string file_path(path.c_str()); + size_t start = std::string("file://").size(); + if (file_path.empty() || file_path.size() <= start) + return {}; + // remove prefix file:// + file_path = file_path.substr(start, file_path.size() - start); + return file_path; +} + // get filepath from wxFont on Mac OsX static std::string get_file_path(const wxFont& font) { const wxNativeFontInfo *info = font.GetNativeFontInfo(); @@ -55,13 +69,7 @@ static std::string get_file_path(const wxFont& font) { if (url == NULL) return {}; wxString file_uri; wxCFTypeRef(url).GetValue(file_uri); - std::string file_path(wxURI::Unescape(file_uri).c_str()); - size_t start = std::string("file://").size(); - if (file_path.empty() || file_path.size() <= start) - return {}; - // remove prefix file:// - file_path = file_path.substr(start, file_path.size() - start); - return file_path; + return get_path_from_file_uri(file_uri); } #endif // __APPLE__ } // namespace From 650447cf021d04d31b5ca8a6b40f3d4307e3346d Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 23 Feb 2023 12:01:26 +0100 Subject: [PATCH 33/64] Second try to fix conversion from uri to string path 'file://System/Library/Fonts/Helvetica.ttc#postscript-name=Helvetica' -> '/System/Library/Fonts/Helvetica.ttc' --- src/slic3r/Utils/WxFontUtils.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index a56f331412..ebcd0d9a01 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -55,13 +55,10 @@ static std::string get_file_path(const wxFont& font) { if (url == NULL) return {}; wxString file_uri; wxCFTypeRef(url).GetValue(file_uri); - std::string file_path(wxURI::Unescape(file_uri).c_str()); - size_t start = std::string("file://").size(); - if (file_path.empty() || file_path.size() <= start) - return {}; - // remove prefix file:// - file_path = file_path.substr(start, file_path.size() - start); - return file_path; + wxURI uri(file_uri); + const wxString &path = uri.GetPath(); + BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ")."; + return std::string(path.c_str()); } #endif // __APPLE__ } // namespace From 142a21d00e4d231b9dc1703e4fabab12e9cef95b Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 23 Feb 2023 16:38:07 +0100 Subject: [PATCH 34/64] unescape uri path on MacOs --- src/slic3r/Utils/WxFontUtils.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index ebcd0d9a01..2851098f2e 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -57,8 +57,9 @@ static std::string get_file_path(const wxFont& font) { wxCFTypeRef(url).GetValue(file_uri); wxURI uri(file_uri); const wxString &path = uri.GetPath(); - BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ")."; - return std::string(path.c_str()); + std::string path_str(wxURI::Unescape(path).c_str()); + BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ") string(" << path_str << ")."; + return path_str; } #endif // __APPLE__ } // namespace From ce3785fdb1888303ce8aaac63b9adc7fa83b6894 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 24 Feb 2023 11:45:24 +0100 Subject: [PATCH 35/64] Search input not change case of letter. --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 6a4aa40955..c4bacf0311 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2183,7 +2183,6 @@ void GLGizmoEmboss::draw_font_list() const char *popup_id = "##font_list_popup"; const char *input_id = "##font_list_input"; ImGui::SetNextItemWidth(m_gui_cfg->input_width); - ImGuiInputTextFlags input_flags = ImGuiInputTextFlags_CharsUppercase; // change color of hint to normal text bool is_popup_open = ImGui::IsPopupOpen(popup_id); @@ -2195,11 +2194,18 @@ void GLGizmoEmboss::draw_font_list() m_face_names.search.clear(); } - if (ImGui::InputTextWithHint(input_id, selected, &m_face_names.search, input_flags)) { + if (ImGui::InputTextWithHint(input_id, selected, &m_face_names.search)) { // update filtration result m_face_names.hide = std::vector(m_face_names.faces.size(), {false}); + + // search to uppercase + std::string search = m_face_names.search; // copy + std::transform(search.begin(), search.end(), search.begin(), ::toupper); + for (FaceName &face : m_face_names.faces) { - size_t index = &face - &m_face_names.faces.front(); + size_t index = &face - &m_face_names.faces.front(); + + // font name to uppercase std::string name(face.wx_name.ToUTF8().data()); std::transform(name.begin(), name.end(), name.begin(), ::toupper); From 870561469f3cd27bb140ad8b9c54079377cd37ef Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 24 Feb 2023 11:47:44 +0100 Subject: [PATCH 36/64] fix --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index c4bacf0311..b12aeea66c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2210,7 +2210,7 @@ void GLGizmoEmboss::draw_font_list() std::transform(name.begin(), name.end(), name.begin(), ::toupper); // It should use C++ 20 feature https://en.cppreference.com/w/cpp/string/basic_string/starts_with - bool start_with = boost::starts_with(name, m_face_names.search); + bool start_with = boost::starts_with(name, search); m_face_names.hide[index] = !start_with; } } From 9d8883036593143c4e9453f5ffca80553796c3a3 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 24 Feb 2023 13:31:20 +0100 Subject: [PATCH 37/64] Separate icon manager. Create well public interface for future work and improve --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 72 ++++-------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 10 +- src/slic3r/GUI/IconManager.cpp | 150 ++++++++++++++++++++++++ src/slic3r/GUI/IconManager.hpp | 110 +++++++++++++++++ 5 files changed, 287 insertions(+), 57 deletions(-) create mode 100644 src/slic3r/GUI/IconManager.cpp create mode 100644 src/slic3r/GUI/IconManager.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 453d7eeb5a..299705ce86 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -97,6 +97,8 @@ set(SLIC3R_GUI_SOURCES GUI/GUI_Geometry.hpp GUI/I18N.cpp GUI/I18N.hpp + GUI/IconManager.cpp + GUI/IconManager.hpp GUI/MainFrame.cpp GUI/MainFrame.hpp GUI/Plater.cpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index b12aeea66c..f6b49d1cb4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3795,23 +3795,9 @@ void GLGizmoEmboss::init_icons() std::string path = resources_dir() + "/icons/"; for (std::string &filename : filenames) filename = path + filename; - // state order has to match the enum IconState - std::vector> states; - states.push_back(std::make_pair(1, false)); // Activable - states.push_back(std::make_pair(0, false)); // Hovered - states.push_back(std::make_pair(2, false)); // Disabled - - bool compress = false; - bool is_loaded = m_icons_texture.load_from_svg_files_as_sprites_array( - filenames, states, m_gui_cfg->icon_width, compress); - - if (!is_loaded || - (size_t)m_icons_texture.get_width() < (states.size() * m_gui_cfg->icon_width) || - (size_t)m_icons_texture.get_height() < (filenames.size() * m_gui_cfg->icon_width)) { - // bad load of icons, but all usage of m_icons_texture check that texture is initialized - assert(false); - m_icons_texture.reset(); - } + ImVec2 size(m_gui_cfg->icon_width, m_gui_cfg->icon_width); + auto type = IconManager::RasterType::color_wite_gray; + m_icons = m_icon_manager.init(filenames, size, type); } void GLGizmoEmboss::draw_icon(IconType icon, IconState state, ImVec2 size) @@ -3820,48 +3806,32 @@ void GLGizmoEmboss::draw_icon(IconType icon, IconState state, ImVec2 size) assert(icon != IconType::_count); if (icon == IconType::_count) return; - unsigned int icons_texture_id = m_icons_texture.get_id(); - int tex_width = m_icons_texture.get_width(); - int tex_height = m_icons_texture.get_height(); - // is icon loaded - if ((icons_texture_id == 0) || (tex_width <= 1) || (tex_height <= 1)){ - ImGui::Text("â–®"); - return; - } - - int icon_width = m_gui_cfg->icon_width; - ImTextureID tex_id = (void *) (intptr_t) (GLuint) icons_texture_id; - int start_x = static_cast(state) * (icon_width + 1) + 1, - start_y = static_cast(icon) * (icon_width + 1) + 1; - - ImVec2 uv0(start_x / (float) tex_width, - start_y / (float) tex_height); - ImVec2 uv1((start_x + icon_width) / (float) tex_width, - (start_y + icon_width) / (float) tex_height); - - if (size.x < 1 || size.y < 1) - size = ImVec2(m_gui_cfg->icon_width, m_gui_cfg->icon_width); - - ImGui::Image(tex_id, size, uv0, uv1); + const auto &i = *m_icons[static_cast(icon)][static_cast(state)]; + IconManager::draw(i); } void GLGizmoEmboss::draw_transparent_icon() { - unsigned int icons_texture_id = m_icons_texture.get_id(); - int tex_width = m_icons_texture.get_width(); - int tex_height = m_icons_texture.get_height(); - // is icon loaded - if ((icons_texture_id == 0) || (tex_width <= 1) || (tex_height <= 1)) { + // use top left corner of first icon + IconManager::Icon icon = *m_icons.front().front(); // copy + + if (!icon.is_valid()) { ImGui::Text("â–¯"); return; } - ImTextureID tex_id = (void *) (intptr_t) (GLuint) icons_texture_id; - int icon_width = m_gui_cfg->icon_width; - ImVec2 icon_size(icon_width, icon_width); - ImVec2 pixel_size(1.f / tex_width, 1.f / tex_height); - // zero pixel is transparent in texture - ImGui::Image(tex_id, icon_size, ImVec2(0, 0), pixel_size); + // size UV texture coors [in texture ratio] + ImVec2 size_uv( + icon.br.x-icon.tl.x, + icon.br.y-icon.tl.y); + ImVec2 one_px( + size_uv.x/icon.size.x, + size_uv.y/icon.size.y); + // reduce uv coors to one pixel + icon.br = ImVec2( + icon.tl.x + one_px.x, + icon.tl.y + one_px.y); + IconManager::draw(icon); } bool GLGizmoEmboss::draw_clickable( diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index f17293074b..76d8707265 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -5,15 +5,12 @@ // which overrides our localization "L" macro. #include "GLGizmoBase.hpp" #include "GLGizmoRotate.hpp" -#include "slic3r/GUI/GLTexture.hpp" +#include "slic3r/GUI/IconManager.hpp" #include "slic3r/Utils/RaycastManager.hpp" #include "slic3r/Utils/EmbossStyleManager.hpp" -#include "admesh/stl.h" // indexed_triangle_set #include #include -#include -#include #include #include "libslic3r/Emboss.hpp" @@ -33,7 +30,6 @@ namespace Slic3r{ } namespace Slic3r::GUI { -class MeshRaycaster; class GLGizmoEmboss : public GLGizmoBase { public: @@ -346,7 +342,9 @@ private: void calculate_scale(); // drawing icons - GLTexture m_icons_texture; + IconManager m_icon_manager; + std::vector m_icons; + void init_icons(); enum class IconType : unsigned { rename = 0, diff --git a/src/slic3r/GUI/IconManager.cpp b/src/slic3r/GUI/IconManager.cpp new file mode 100644 index 0000000000..0cde271d06 --- /dev/null +++ b/src/slic3r/GUI/IconManager.cpp @@ -0,0 +1,150 @@ +#include "IconManager.hpp" + +#include + +using namespace Slic3r::GUI; + +namespace priv { +// set shared pointer to point on bad texture +static void clear(IconManager::Icons &icons); +static const std::vector>& get_states(IconManager::RasterType type); +} + +IconManager::~IconManager() { + priv::clear(m_icons); + // release opengl texture is made in ~GLTexture() +} + +std::vector IconManager::init(const InitTypes &input) +{ + BOOST_LOG_TRIVIAL(error) << "Not implemented yet"; + return {}; +} + +std::vector IconManager::init(const std::vector &file_paths, const ImVec2 &size, RasterType type) +{ + // TODO: remove in future + if (!m_icons.empty()) { + // not first initialization + priv::clear(m_icons); + m_icons.clear(); + m_icons_texture.reset(); + } + + // only rectangle are supported + assert(size.x == size.y); + // no subpixel supported + unsigned int width = static_cast(std::fabs(std::round(size.x))); + assert(size.x == static_cast(width)); + + // state order has to match the enum IconState + const auto& states = priv::get_states(type); + + bool compress = false; + bool is_loaded = m_icons_texture.load_from_svg_files_as_sprites_array(file_paths, states, width, compress); + if (!is_loaded || (size_t) m_icons_texture.get_width() < (states.size() * width) || + (size_t) m_icons_texture.get_height() < (file_paths.size() * width)) { + // bad load of icons, but all usage of m_icons_texture check that texture is initialized + assert(false); + m_icons_texture.reset(); + return {}; + } + + unsigned count_files = file_paths.size(); + // count icons per file + unsigned count = states.size(); + // create result + std::vector result; + result.reserve(count_files); + + Icon def_icon; + def_icon.tex_id = m_icons_texture.get_id(); + def_icon.size = size; + + // float beacouse of dividing + float tex_height = static_cast(m_icons_texture.get_height()); + float tex_width = static_cast(m_icons_texture.get_width()); + + //for (const auto &f: file_paths) { + for (unsigned f = 0; f < count_files; ++f) { + // NOTE: there are space between icons + unsigned start_y = static_cast(f) * (width + 1) + 1; + float y1 = start_y / tex_height; + float y2 = (start_y + width) / tex_height; + Icons file_icons; + file_icons.reserve(count); + //for (const auto &s : states) { + for (unsigned j = 0; j < count; ++j) { + auto icon = std::make_shared(def_icon); + // NOTE: there are space between icons + unsigned start_x = static_cast(j) * (width + 1) + 1; + float x1 = start_x / tex_width; + float x2 = (start_x + width) / tex_width; + icon->tl = ImVec2(x1, y1); + icon->br = ImVec2(x2, y2); + file_icons.push_back(icon); + m_icons.push_back(std::move(icon)); + } + result.emplace_back(std::move(file_icons)); + } + return result; +} + +void IconManager::release() { + BOOST_LOG_TRIVIAL(error) << "Not implemented yet"; +} + +void IconManager::draw(const Icon &icon, const ImVec2 &size, const ImVec4 &tint_col, const ImVec4 &border_col) +{ + // is icon loaded + if (!icon.is_valid()) { + ImGui::Text("?"); + return; + } + + ImTextureID id = (void *) icon.tex_id; + const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size; + ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col); +} + +void priv::clear(IconManager::Icons &icons) { + std::string message; + for (auto &icon : icons) { + // Exist more than this instance of shared ptr? + long count = icon.use_count(); + if (count != 1) { + // in existing icon change texture to non existing one + icon->tex_id = 0; + + std::string descr = + ((count > 2) ? (std::to_string(count - 1) + "x") : "") + // count + std::to_string(icon->size.x) + "x" + std::to_string(icon->size.y); // resolution + if (message.empty()) + message = descr; + else + message += ", " + descr; + } + } + + if (!message.empty()) + BOOST_LOG_TRIVIAL(warning) << "There is still used icons(" << message << ")."; +} + +const std::vector> &priv::get_states(IconManager::RasterType type) { + static std::vector> color = {std::make_pair(0, false)}; + static std::vector> white = {std::make_pair(1, false)}; + static std::vector> gray = {std::make_pair(2, false)}; + static std::vector> color_wite_gray = { + std::make_pair(1, false), // Activable + std::make_pair(0, false), // Hovered + std::make_pair(2, false) // Disabled + }; + + switch (type) { + case IconManager::RasterType::color: return color; + case IconManager::RasterType::white_only_data: return white; + case IconManager::RasterType::gray_only_data: return gray; + case IconManager::RasterType::color_wite_gray: return color_wite_gray; + default: return color; + } +} diff --git a/src/slic3r/GUI/IconManager.hpp b/src/slic3r/GUI/IconManager.hpp new file mode 100644 index 0000000000..91177fe8a2 --- /dev/null +++ b/src/slic3r/GUI/IconManager.hpp @@ -0,0 +1,110 @@ +#ifndef slic3r_IconManager_hpp_ +#define slic3r_IconManager_hpp_ + +#include +#include +#include "imgui/imgui.h" // ImVec2 +#include "slic3r/GUI/GLTexture.hpp" // texture storage + +namespace Slic3r::GUI { + +/// +/// Keep texture with icons for UI +/// Manage texture live -> create and destruct texture +/// by live of icon shared pointers. +/// +class IconManager +{ +public: + /// + /// Release texture + /// Set shared pointers to invalid texture + /// + ~IconManager(); + + /// + /// Define way to convert svg data to raster + /// + enum class RasterType: int{ + color = 1 << 1, + white_only_data = 1 << 2, + gray_only_data = 1 << 3, + color_wite_gray = color | white_only_data | gray_only_data + // TODO: add type with backgrounds + }; + + struct InitType { + // path to file with image .. svg + std::string filepath; + + // resolution of stored rasterized icon + ImVec2 size; // float will be rounded + + // could contain more than one type + RasterType type = RasterType::color; + // together color, white and gray = color | white_only_data | gray_only_data + }; + using InitTypes = std::vector; + + /// + /// Data for render texture with icon + /// + struct Icon { + // stored texture size + ImVec2 size = ImVec2(-1, -1); // [in px] --> unsigned int values stored as float + + // SubTexture UV coordinate in range from 0. to 1. + ImVec2 tl; // top left -> uv0 + ImVec2 br; // bottom right -> uv1 + + // OpenGL texture id + unsigned int tex_id = 0; + bool is_valid() const { return tex_id != 0;} + // && size.x > 0 && size.y > 0 && tl.x != br.x && tl.y != br.y; + }; + using Icons = std::vector >; + + /// + /// Initialize raster texture on GPU with given images + /// NOTE: Have to be called after OpenGL initialization + /// + /// Define files and its + /// Rasterized icons stored on GPU, + /// Same size and order as input, each item of vector is set of texture in order by RasterType + std::vector init(const InitTypes &input); + + /// + /// Initialize multiple icons with same settings for size and type + /// NOTE: Have to be called after OpenGL initialization + /// + /// Define files with icon + /// Size of stored texture[in px], float will be rounded + /// Define way to rasterize icon, + /// together color, white and gray = RasterType::color | RasterType::white_only_data | RasterType::gray_only_data + /// Rasterized icons stored on GPU, + /// Same size and order as file_paths, each item of vector is set of texture in order by RasterType + std::vector init(const std::vector &file_paths, const ImVec2 &size, RasterType type = RasterType::color); + + /// + /// Release icons which are hold only by this manager + /// May change texture and position of icons. + /// + void release(); + + /// + /// Draw imgui image with icon + /// + /// Place in texture + /// [optional]Size of image, wen zero than use same size as stored texture + /// viz ImGui::Image + /// viz ImGui::Image + static void draw(const Icon &icon, const ImVec2 &size = ImVec2(0, 0), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); + +private: + // keep data stored on GPU + GLTexture m_icons_texture; + Icons m_icons; +}; + +} // namespace Slic3r::GUI +#endif // slic3r_IconManager_hpp_ \ No newline at end of file From 970cbbd1325ed7ee9b28b5f25b5d74ef670b6929 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 24 Feb 2023 14:27:51 +0100 Subject: [PATCH 38/64] fix transparent icon --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index f6b49d1cb4..e54deb9081 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3828,9 +3828,8 @@ void GLGizmoEmboss::draw_transparent_icon() size_uv.x/icon.size.x, size_uv.y/icon.size.y); // reduce uv coors to one pixel - icon.br = ImVec2( - icon.tl.x + one_px.x, - icon.tl.y + one_px.y); + icon.tl = ImVec2(0, 0); + icon.br = one_px; IconManager::draw(icon); } From 4d0b8679ebd35d8b5f51c5e01d374066a394fbc7 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 24 Feb 2023 16:03:55 +0100 Subject: [PATCH 39/64] Move functionality from emboss gizmo into icon manager --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 79 ++++-------------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 6 +- src/slic3r/GUI/IconManager.cpp | 80 +++++++++++++++++++++---- src/slic3r/GUI/IconManager.hpp | 38 +++++++++--- 4 files changed, 111 insertions(+), 92 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index e54deb9081..2992b957f0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2798,7 +2798,7 @@ bool GLGizmoEmboss::draw_italic_button() const std::optional &wx_font_opt = m_style_manager.get_wx_font(); const auto& ff = m_style_manager.get_font_file_with_cache(); if (!wx_font_opt.has_value() || !ff.has_value()) { - draw_icon(IconType::italic, IconState::disabled); + draw(*m_icons[(int) IconType::italic][(int)IconState::disabled]); return false; } const wxFont& wx_font = *wx_font_opt; @@ -2807,8 +2807,8 @@ bool GLGizmoEmboss::draw_italic_button() bool is_font_italic = skew.has_value() || WxFontUtils::is_italic(wx_font); if (is_font_italic) { // unset italic - if (draw_clickable(IconType::italic, IconState::hovered, - IconType::unitalic, IconState::hovered)) { + if (clickable(get_icon(IconType::italic, IconState::hovered), + get_icon(IconType::unitalic, IconState::hovered))) { skew.reset(); if (wx_font.GetStyle() != wxFontStyle::wxFONTSTYLE_NORMAL) { wxFont new_wx_font = wx_font; // copy @@ -2845,7 +2845,7 @@ bool GLGizmoEmboss::draw_bold_button() { const std::optional &wx_font_opt = m_style_manager.get_wx_font(); const auto& ff = m_style_manager.get_font_file_with_cache(); if (!wx_font_opt.has_value() || !ff.has_value()) { - draw_icon(IconType::bold, IconState::disabled); + draw(get_icon(IconType::bold, IconState::disabled)); return false; } const wxFont &wx_font = *wx_font_opt; @@ -2854,8 +2854,8 @@ bool GLGizmoEmboss::draw_bold_button() { bool is_font_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font); if (is_font_bold) { // unset bold - if (draw_clickable(IconType::bold, IconState::hovered, - IconType::unbold, IconState::hovered)) { + if (clickable(get_icon(IconType::bold, IconState::hovered), + get_icon(IconType::unbold, IconState::hovered))) { boldness.reset(); if (wx_font.GetWeight() != wxFontWeight::wxFONTWEIGHT_NORMAL) { wxFont new_wx_font = wx_font; // copy @@ -3800,67 +3800,14 @@ void GLGizmoEmboss::init_icons() m_icons = m_icon_manager.init(filenames, size, type); } -void GLGizmoEmboss::draw_icon(IconType icon, IconState state, ImVec2 size) +const IconManager::Icon &GLGizmoEmboss::get_icon(IconType type, IconState state) { return *m_icons[(unsigned) type][(unsigned) state]; } +bool GLGizmoEmboss::draw_button(IconType type, bool disable) { - // canot draw count - assert(icon != IconType::_count); - if (icon == IconType::_count) return; - - const auto &i = *m_icons[static_cast(icon)][static_cast(state)]; - IconManager::draw(i); -} - -void GLGizmoEmboss::draw_transparent_icon() -{ - // use top left corner of first icon - IconManager::Icon icon = *m_icons.front().front(); // copy - - if (!icon.is_valid()) { - ImGui::Text("â–¯"); - return; - } - - // size UV texture coors [in texture ratio] - ImVec2 size_uv( - icon.br.x-icon.tl.x, - icon.br.y-icon.tl.y); - ImVec2 one_px( - size_uv.x/icon.size.x, - size_uv.y/icon.size.y); - // reduce uv coors to one pixel - icon.tl = ImVec2(0, 0); - icon.br = one_px; - IconManager::draw(icon); -} - -bool GLGizmoEmboss::draw_clickable( - IconType icon, IconState state, - IconType hover_icon, IconState hover_state) -{ - // check of hover - float cursor_x = ImGui::GetCursorPosX(); - draw_transparent_icon(); - ImGui::SameLine(cursor_x); - - if (ImGui::IsItemHovered()) { - // redraw image - draw_icon(hover_icon, hover_state); - } else { - // redraw normal image - draw_icon(icon, state); - } - return ImGui::IsItemClicked(); -} - -bool GLGizmoEmboss::draw_button(IconType icon, bool disable) -{ - if (disable) { - draw_icon(icon, IconState::disabled); - return false; - } - return draw_clickable( - icon, IconState::activable, - icon, IconState::hovered + return Slic3r::GUI::button( + get_icon(type, IconState::activable), + get_icon(type, IconState::hovered), + get_icon(type, IconState::disabled), + disable ); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 76d8707265..2bf50ce5e0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -361,10 +361,8 @@ private: // automatic calc of icon's count _count }; - enum class IconState: unsigned { activable = 0, hovered /*1*/, disabled /*2*/}; - void draw_icon(IconType icon, IconState state, ImVec2 size = ImVec2(0,0)); - void draw_transparent_icon(); - bool draw_clickable(IconType icon, IconState state, IconType hover_icon, IconState hover_state); + enum class IconState : unsigned { activable = 0, hovered /*1*/, disabled /*2*/ }; + const IconManager::Icon& get_icon(IconType type, IconState state); bool draw_button(IconType icon, bool disable = false); // only temporary solution diff --git a/src/slic3r/GUI/IconManager.cpp b/src/slic3r/GUI/IconManager.cpp index 0cde271d06..78ae673efd 100644 --- a/src/slic3r/GUI/IconManager.cpp +++ b/src/slic3r/GUI/IconManager.cpp @@ -8,6 +8,7 @@ namespace priv { // set shared pointer to point on bad texture static void clear(IconManager::Icons &icons); static const std::vector>& get_states(IconManager::RasterType type); +static void draw_transparent_icon(const IconManager::Icon &icon); // only help function } IconManager::~IconManager() { @@ -94,19 +95,6 @@ void IconManager::release() { BOOST_LOG_TRIVIAL(error) << "Not implemented yet"; } -void IconManager::draw(const Icon &icon, const ImVec2 &size, const ImVec4 &tint_col, const ImVec4 &border_col) -{ - // is icon loaded - if (!icon.is_valid()) { - ImGui::Text("?"); - return; - } - - ImTextureID id = (void *) icon.tex_id; - const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size; - ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col); -} - void priv::clear(IconManager::Icons &icons) { std::string message; for (auto &icon : icons) { @@ -148,3 +136,69 @@ const std::vector> &priv::get_states(IconManager::RasterTyp default: return color; } } + +void priv::draw_transparent_icon(const IconManager::Icon &icon) +{ + // Check input + if (!icon.is_valid()) { + assert(false); + BOOST_LOG_TRIVIAL(warning) << "Drawing invalid Icon."; + ImGui::Text("?"); + return; + } + + // size UV texture coors [in texture ratio] + ImVec2 size_uv(icon.br.x - icon.tl.x, icon.br.y - icon.tl.y); + ImVec2 one_px(size_uv.x / icon.size.x, size_uv.y / icon.size.y); + + // use top left corner of first icon + IconManager::Icon icon_px = icon; // copy + // reduce uv coors to one pixel + icon_px.tl = ImVec2(0, 0); + icon_px.br = one_px; + draw(icon_px); +} + +namespace Slic3r::GUI { + +void draw(const IconManager::Icon &icon, const ImVec2 &size, const ImVec4 &tint_col, const ImVec4 &border_col) +{ + // Check input + if (!icon.is_valid()) { + assert(false); + BOOST_LOG_TRIVIAL(warning) << "Drawing invalid Icon."; + ImGui::Text("?"); + return; + } + + ImTextureID id = (void *) icon.tex_id; + const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size; + ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col); +} + +bool clickable(const IconManager::Icon &icon, const IconManager::Icon &icon_hover) +{ + // check of hover + float cursor_x = ImGui::GetCursorPosX(); + priv::draw_transparent_icon(icon); + ImGui::SameLine(cursor_x); + if (ImGui::IsItemHovered()) { + // redraw image + draw(icon_hover); + } else { + // redraw normal image + draw(icon); + } + return ImGui::IsItemClicked(); +} + +bool button(const IconManager::Icon &activ, const IconManager::Icon &hover, const IconManager::Icon &disable, bool disabled) +{ + if (disabled) { + draw(disable); + return false; + } + return clickable(activ, hover); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/IconManager.hpp b/src/slic3r/GUI/IconManager.hpp index 91177fe8a2..78b1395dce 100644 --- a/src/slic3r/GUI/IconManager.hpp +++ b/src/slic3r/GUI/IconManager.hpp @@ -91,20 +91,40 @@ public: /// void release(); - /// - /// Draw imgui image with icon - /// - /// Place in texture - /// [optional]Size of image, wen zero than use same size as stored texture - /// viz ImGui::Image - /// viz ImGui::Image - static void draw(const Icon &icon, const ImVec2 &size = ImVec2(0, 0), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); - private: // keep data stored on GPU GLTexture m_icons_texture; Icons m_icons; }; +/// +/// Draw imgui image with icon +/// +/// Place in texture +/// [optional]Size of image, wen zero than use same size as stored texture +/// viz ImGui::Image +/// viz ImGui::Image +void draw(const IconManager::Icon &icon, + const ImVec2 &size = ImVec2(0, 0), + const ImVec4 &tint_col = ImVec4(1, 1, 1, 1), + const ImVec4 &border_col = ImVec4(0, 0, 0, 0)); + +/// +/// Draw icon which change on hover +/// +/// Draw when no hover +/// Draw when hover +/// True when click, otherwise False +bool clickable(const IconManager::Icon &icon, const IconManager::Icon &icon_hover); + +/// +/// Use icon as button with 3 states activ hover and disabled +/// +/// Not disabled not hovered image +/// Hovered image +/// Disabled image +/// True when click on enabled, otherwise False +bool button(const IconManager::Icon &activ, const IconManager::Icon &hover, const IconManager::Icon &disable, bool disabled = false); + } // namespace Slic3r::GUI #endif // slic3r_IconManager_hpp_ \ No newline at end of file From e86dff528d0e23a2fe2156011821024370d042a6 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 09:17:25 +0100 Subject: [PATCH 40/64] Separate drag manager --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GLCanvas3D.cpp | 90 ++++ src/slic3r/GUI/GLCanvas3D.hpp | 15 +- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 529 +++++++----------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 111 +---- src/slic3r/GUI/IconManager.hpp | 6 +- src/slic3r/GUI/Selection.cpp | 23 ++ src/slic3r/GUI/Selection.hpp | 4 + src/slic3r/GUI/SurfaceDrag.cpp | 244 +++++++++++ src/slic3r/GUI/SurfaceDrag.hpp | 47 +++ src/slic3r/Utils/RaycastManager.cpp | 44 ++ src/slic3r/Utils/RaycastManager.hpp | 23 ++ 12 files changed, 646 insertions(+), 492 deletions(-) create mode 100644 src/slic3r/GUI/SurfaceDrag.cpp create mode 100644 src/slic3r/GUI/SurfaceDrag.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 299705ce86..2ca1998e31 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -159,6 +159,8 @@ set(SLIC3R_GUI_SOURCES GUI/RemovableDriveManager.hpp GUI/SendSystemInfoDialog.cpp GUI/SendSystemInfoDialog.hpp + GUI/SurfaceDrag.cpp + GUI/SurfaceDrag.hpp GUI/BonjourDialog.cpp GUI/BonjourDialog.hpp GUI/ButtonsDescription.cpp diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 427c0e99fc..f4573d3c4e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7112,5 +7112,95 @@ const ModelVolume *get_model_volume(const GLVolume &v, const Model &model) return ret; } +const ModelVolume *get_model_volume(const ObjectID &volume_id, const ModelObjectPtrs &objects) +{ + for (const ModelObject *obj : objects) + for (const ModelVolume *vol : obj->volumes) + if (vol->id() == volume_id) + return vol; + return nullptr; +} + +ModelVolume *get_model_volume(const GLVolume &v, const ModelObject& object) { + if (v.volume_idx() < 0) + return nullptr; + + size_t volume_idx = static_cast(v.volume_idx()); + if (volume_idx >= object.volumes.size()) + return nullptr; + + return object.volumes[volume_idx]; +} + +ModelVolume *get_model_volume(const GLVolume &v, const ModelObjectPtrs &objects) +{ + if (v.object_idx() < 0) + return nullptr; + size_t objext_idx = static_cast(v.object_idx()); + if (objext_idx >= objects.size()) + return nullptr; + if (objects[objext_idx] == nullptr) + return nullptr; + return get_model_volume(v, *objects[objext_idx]); +} + +GLVolume *get_first_hovered_gl_volume(const GLCanvas3D &canvas) { + int hovered_id_signed = canvas.get_first_hover_volume_idx(); + if (hovered_id_signed < 0) + return nullptr; + + size_t hovered_id = static_cast(hovered_id_signed); + const GLVolumePtrs &volumes = canvas.get_volumes().volumes; + if (hovered_id >= volumes.size()) + return nullptr; + + return volumes[hovered_id]; +} + +GLVolume *get_selected_gl_volume(const GLCanvas3D &canvas) { + const GLVolume *gl_volume = get_selected_gl_volume(canvas.get_selection()); + if (gl_volume == nullptr) + return nullptr; + + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + for (GLVolume *v : gl_volumes) + if (v->composite_id == gl_volume->composite_id) + return v; + return nullptr; +} + +ModelObject *get_model_object(const GLVolume &gl_volume, const Model &model) { + return get_model_object(gl_volume, model.objects); +} + +ModelObject *get_model_object(const GLVolume &gl_volume, const ModelObjectPtrs &objects) { + if (gl_volume.object_idx() < 0) + return nullptr; + size_t objext_idx = static_cast(gl_volume.object_idx()); + if (objext_idx >= objects.size()) + return nullptr; + return objects[objext_idx]; +} + +ModelInstance *get_model_instance(const GLVolume &gl_volume, const Model& model) { + return get_model_instance(gl_volume, model.objects); +} + +ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObjectPtrs &objects) { + if (gl_volume.instance_idx() < 0) + return nullptr; + ModelObject *object = get_model_object(gl_volume, objects); + return get_model_instance(gl_volume, *object); +} + +ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObject &object) { + if (gl_volume.instance_idx() < 0) + return nullptr; + size_t instance_idx = static_cast(gl_volume.instance_idx()); + if (instance_idx >= object.instances.size()) + return nullptr; + return object.instances[instance_idx]; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index f8f5a0efcd..573ef879af 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -1056,7 +1056,20 @@ private: float get_overlay_window_width() { return LayersEditing::get_overlay_window_width(); } }; -const ModelVolume * get_model_volume(const GLVolume &v, const Model &model); +const ModelVolume *get_model_volume(const GLVolume &v, const Model &model); +const ModelVolume *get_model_volume(const ObjectID &volume_id, const ModelObjectPtrs &objects); +ModelVolume *get_model_volume(const GLVolume &v, const ModelObjectPtrs &objects); +ModelVolume *get_model_volume(const GLVolume &v, const ModelObject &object); + +GLVolume *get_first_hovered_gl_volume(const GLCanvas3D &canvas); +GLVolume *get_selected_gl_volume(const GLCanvas3D &canvas); + +ModelObject *get_model_object(const GLVolume &gl_volume, const Model &model); +ModelObject *get_model_object(const GLVolume &gl_volume, const ModelObjectPtrs &objects); + +ModelInstance *get_model_instance(const GLVolume &gl_volume, const Model &model); +ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObjectPtrs &objects); +ModelInstance *get_model_instance(const GLVolume &gl_volume, const ModelObject &object); } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 2992b957f0..14e6c7aa14 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -18,7 +18,7 @@ // TODO: remove include #include "libslic3r/SVG.hpp" // debug store #include "libslic3r/Geometry.hpp" // covex hull 2d -#include "libslic3r/Timer.hpp" // covex hull 2d +#include "libslic3r/Timer.hpp" #include "libslic3r/NSVGUtils.hpp" #include "libslic3r/Model.hpp" @@ -109,11 +109,6 @@ static const struct Limits // Define where is up vector on model constexpr double up_limit = 0.9; -static bool is_text_empty(const std::string &text){ - return text.empty() || - text.find_first_not_of(" \n\t\r") == std::string::npos; -} - // Normalize radian angle from -PI to PI template void to_range_pi_pi(T& angle) { @@ -123,6 +118,63 @@ template void to_range_pi_pi(T& angle) } } } // namespace priv +using namespace priv; + +// This configs holds GUI layout size given by translated texts. +// etc. When language changes, GUI is recreated and this class constructed again, +// so the change takes effect. (info by GLGizmoFdmSupports.hpp) +struct GLGizmoEmboss::GuiCfg +{ + // Detect invalid config values when change monitor DPI + double screen_scale; + float main_toolbar_height; + + // Zero means it is calculated in init function + ImVec2 minimal_window_size = ImVec2(0, 0); + ImVec2 minimal_window_size_with_advance = ImVec2(0, 0); + ImVec2 minimal_window_size_with_collections = ImVec2(0, 0); + float height_of_volume_type_selector = 0.f; + float input_width = 0.f; + float delete_pos_x = 0.f; + float max_style_name_width = 0.f; + unsigned int icon_width = 0; + + // maximal width and height of style image + Vec2i max_style_image_size = Vec2i(0, 0); + + float indent = 0.f; + float input_offset = 0.f; + float advanced_input_offset = 0.f; + + ImVec2 text_size; + + // maximal size of face name image + Vec2i face_name_size = Vec2i(100, 0); + float face_name_max_width = 100.f; + float face_name_texture_offset_x = 105.f; + + // maximal texture generate jobs running at once + unsigned int max_count_opened_font_files = 10; + + // Only translations needed for calc GUI size + struct Translations + { + std::string font; + std::string size; + std::string depth; + std::string use_surface; + + // advanced + std::string char_gap; + std::string line_gap; + std::string boldness; + std::string italic; + std::string surface_distance; + std::string angle; + std::string collection; + }; + Translations translations; +}; GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) : GLGizmoBase(parent, M_ICON_FILENAME, -2) @@ -141,7 +193,6 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) // Private namespace with helper function for create volume namespace priv { - /// /// Prepare data for emboss /// @@ -165,21 +216,6 @@ static void start_create_volume_job(const ModelObject *object, DataBase &emboss_data, ModelVolumeType volume_type); -static GLVolume *get_hovered_gl_volume(const GLCanvas3D &canvas); - -/// -/// Unproject on mesh by Mesh raycasters -/// -/// Position of mouse on screen -/// Projection params -/// Define which caster will be skipped, null mean no skip -/// Position on surface, normal direction in world coorinate -/// + key, to know hitted instance and volume -static std::optional ray_from_camera(const RaycastManager &raycaster, - const Vec2d &mouse_pos, - const Camera &camera, - const RaycastManager::ISkip *skip); - /// /// Start job for add new volume on surface of object defined by screen coor /// @@ -220,13 +256,26 @@ static void find_closest_volume(const Selection &selection, /// Screen coordinat, where to create new object laying on bed static void start_create_object_job(DataBase &emboss_data, const Vec2d &coor); -/// -/// Search if exist model volume for given id in object lists -/// -/// List to search volume -/// Unique Identifier of volume -/// Volume when found otherwise nullptr -static const ModelVolume *get_volume(const ModelObjectPtrs &objects, const ObjectID &volume_id); +// Have to match order of files in function GLGizmoEmboss::init_icons() +enum class IconType : unsigned { + rename = 0, + erase, + add, + save, + undo, + italic, + unitalic, + bold, + unbold, + system_selector, + open_file, + // automatic calc of icon's count + _count +}; +// Define rendered version of icon +enum class IconState : unsigned { activable = 0, hovered /*1*/, disabled /*2*/ }; +const IconManager::Icon &get_icon(const IconManager::VIcons& icons, IconType type, IconState state); +bool draw_button(const IconManager::VIcons& icons, IconType type, bool disable = false); } // namespace priv @@ -246,7 +295,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous m_style_manager.discard_style_changes(); set_default_text(); - GLVolume *gl_volume = priv::get_hovered_gl_volume(m_parent); + GLVolume *gl_volume = get_first_hovered_gl_volume(m_parent); DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); if (gl_volume != nullptr) { // Try to cast ray into scene and find object for add volume @@ -353,33 +402,6 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) } namespace priv { - -/// -/// Access to model from gl_volume -/// TODO: it is more general function --> move to utils -/// -/// Volume to model belongs to -/// Object containing gl_volume -/// Model for volume -static ModelVolume *get_model_volume(const GLVolume *gl_volume, const ModelObject *object); - -/// -/// Access to model from gl_volume -/// TODO: it is more general function --> move to utils -/// -/// Volume to model belongs to -/// All objects -/// Model for volume -static ModelVolume *get_model_volume(const GLVolume *gl_volume, const ModelObjectPtrs &objects); - -/// -/// Access to model by selection -/// TODO: it is more general function --> move to select utils -/// -/// Actual selection -/// Model from selection -static ModelVolume *get_selected_volume(const Selection &selection); - /// /// Calculate offset from mouse position to center of text /// @@ -388,14 +410,6 @@ static ModelVolume *get_selected_volume(const Selection &selection); /// Offset in screan coordinate static Vec2d calc_mouse_to_center_text_offset(const Vec2d &mouse, const ModelVolume &mv); -/// -/// Access to one selected volume -/// -/// Containe what is selected -/// Slected when only one volume otherwise nullptr -static const GLVolume *get_gl_volume(const Selection &selection); -static GLVolume *get_gl_volume(const GLCanvas3D &canvas); - /// /// Get transformation to world /// - use fix after store to 3mf when exists @@ -414,28 +428,6 @@ static Transform3d world_matrix(const Selection &selection); static void change_window_position(std::optional &output_window_offset, bool try_to_fix); } // namespace priv -const GLVolume *priv::get_gl_volume(const Selection &selection) { - // return selection.get_first_volume(); - const auto &list = selection.get_volume_idxs(); - if (list.size() != 1) - return nullptr; - unsigned int volume_idx = *list.begin(); - return selection.get_volume(volume_idx); -} - -GLVolume *priv::get_gl_volume(const GLCanvas3D &canvas) { - const GLVolume *gl_volume = get_gl_volume(canvas.get_selection()); - if (gl_volume == nullptr) - return nullptr; - - const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; - for (GLVolume *v : gl_volumes) - if (v->composite_id == gl_volume->composite_id) - return v; - - return nullptr; -} - Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model) { if (!gl_volume) @@ -444,7 +436,8 @@ Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model) if (!model) return res; - ModelVolume* mv = get_model_volume(gl_volume, model->objects); + + const ModelVolume* mv = get_model_volume(*gl_volume, model->objects); if (!mv) return res; @@ -461,7 +454,7 @@ Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model) Transform3d priv::world_matrix(const Selection &selection) { - const GLVolume *gl_volume = get_gl_volume(selection); + const GLVolume *gl_volume = get_selected_gl_volume(selection); return world_matrix(gl_volume, selection.get_model()); } @@ -502,231 +495,47 @@ Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolu return nearest_offset; } -namespace priv { - // Calculate scale in world -static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) -{ - Vec3d from_dir = from * dir; - Vec3d to_dir = to * dir; - double from_scale_sq = from_dir.squaredNorm(); - double to_scale_sq = to_dir.squaredNorm(); - if (is_approx(from_scale_sq, to_scale_sq, 1e-3)) - return {}; // no scale - return sqrt(from_scale_sq / to_scale_sq); -}; - -RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes& condition) -{ - SceneRaycaster::EType type = SceneRaycaster::EType::Volume; - auto scene_casters = canvas.get_raycasters_for_picking(type); - const std::vector> &casters = *scene_casters; - const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; - const ModelObjectPtrs &objects = canvas.get_model()->objects; - - RaycastManager::Meshes meshes; - for (const std::shared_ptr &caster : casters) { - int index = SceneRaycaster::decode_id(type, caster->get_id()); - if (index < 0 || index >= gl_volumes.size()) continue; - const GLVolume *gl_volume = gl_volumes[index]; - const ModelVolume *volume = priv::get_model_volume(gl_volume, objects); - size_t id = volume->id().id; - if (condition.skip(id)) - continue; - auto mesh = std::make_unique(caster->get_raycaster()->get_aabb_mesh()); - meshes.emplace_back(std::make_pair(id, std::move(mesh))); - } - return meshes; -} -} - bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { - // Fix when leave window during dragging - // Fix when click right button - if (m_surface_drag.has_value() && !mouse_event.Dragging()) { - // write transformation from UI into model - m_parent.do_move(L("Surface move")); + // exist selected volume? + if (m_volume == nullptr) + return false; + + const Camera &camera = wxGetApp().plater()->get_camera(); + bool was_dragging = m_surface_drag.has_value(); + bool res = on_mouse_surface_drag(mouse_event, camera, m_surface_drag, m_parent, m_raycast_manager); + bool is_dragging = m_surface_drag.has_value(); - // Update surface by new position + // End with surface dragging? + if (was_dragging && !is_dragging) { + // Update surface by new position if (m_volume->text_configuration->style.prop.use_surface) process(); // Show correct value of height & depth inside of inputs calculate_scale(); - - // allow moving with object again - m_parent.enable_moving(true); - m_surface_drag.reset(); - - // only left up is correct - // otherwise it is fix state and return false - return mouse_event.LeftUp(); } - if (mouse_event.Moving()) - return false; - - // detect start text dragging - if (mouse_event.LeftDown()) { - // exist selected volume? - if (m_volume == nullptr) - return false; - - GLVolume *gl_volume = priv::get_gl_volume(m_parent); - if (gl_volume == nullptr) - return false; - - // is text hovered? - const GLVolumePtrs& gl_volumes = m_parent.get_volumes().volumes; - int hovered_idx = m_parent.get_first_hover_volume_idx(); - if (hovered_idx < 0 || hovered_idx >= gl_volumes.size() || - gl_volumes[hovered_idx] != gl_volume) - return false; - - // hovered object must be actual text volume - const ModelObjectPtrs &objects = m_parent.get_model()->objects; - if (m_volume != priv::get_model_volume(gl_volume, objects)) - return false; - - const ModelInstancePtrs instances = m_volume->get_object()->instances; - int instance_id = gl_volume->instance_idx(); - if (instance_id < 0 || static_cast(instance_id) >= instances.size()) - return false; // should not happen - const ModelInstance *instance = instances[instance_id]; - - const ModelVolumePtrs &volumes = m_volume->get_object()->volumes; - std::vector allowed_volumes_id; - if (volumes.size() > 1) { - allowed_volumes_id.reserve(volumes.size() - 1); - for (auto &v : volumes) { - if (v->id() == m_volume->id()) - continue; - if (!v->is_model_part()) - continue; - allowed_volumes_id.emplace_back(v->id().id); - } - } - RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); - RaycastManager::Meshes meshes = priv::create_meshes(m_parent, condition); - // initialize raycasters - // INFO: It could slows down for big objects - // (may be move to thread and do not show drag until it finish) - m_raycast_manager.actualize(instance, &condition, &meshes); - - // wxCoord == int --> wx/types.h - Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); - Vec2d mouse_pos = mouse_coord.cast(); - Vec2d mouse_offset = priv::calc_mouse_to_center_text_offset(mouse_pos, *m_volume); - Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix(); - TextConfiguration &tc = *m_volume->text_configuration; - // fix baked transformation from .3mf store process - if (tc.fix_3mf_tr.has_value()) - volume_tr = volume_tr * tc.fix_3mf_tr->inverse(); - - Transform3d instance_tr = gl_volume->get_instance_transformation().get_matrix(); - Transform3d instance_tr_inv = instance_tr.inverse(); - Transform3d world_tr = instance_tr * volume_tr; - m_surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition}; - + // Start with dragging + else if (!was_dragging && is_dragging) { // Cancel job to prevent interuption of dragging (duplicit result) - if (m_job_cancel != nullptr) + if (m_job_cancel != nullptr) m_job_cancel->store(true); - - // disable moving with object by mouse - m_parent.enable_moving(false); - return true; } - // Dragging starts out of window - if (!m_surface_drag.has_value()) - return false; - - if (mouse_event.Dragging()) { - // wxCoord == int --> wx/types.h - Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); - Vec2d mouse_pos = mouse_coord.cast(); - Vec2d offseted_mouse = mouse_pos + m_surface_drag->mouse_offset; - const Camera &camera = wxGetApp().plater()->get_camera(); - auto hit = priv::ray_from_camera(m_raycast_manager, offseted_mouse, camera, &m_surface_drag->condition); - m_surface_drag->exist_hit = hit.has_value(); - if (!hit.has_value()) { - // cross hair need redraw - m_parent.set_as_dirty(); - return true; - } - - auto world_linear = m_surface_drag->world.linear(); - // Calculate offset: transformation to wanted position - { - // Reset skew of the text Z axis: - // Project the old Z axis into a new Z axis, which is perpendicular to the old XY plane. - Vec3d old_z = world_linear.col(2); - Vec3d new_z = world_linear.col(0).cross(world_linear.col(1)); - world_linear.col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); - } - - Vec3d text_z_world = world_linear.col(2); // world_linear * Vec3d::UnitZ() - auto z_rotation = Eigen::Quaternion::FromTwoVectors(text_z_world, hit->normal); - Transform3d world_new = z_rotation * m_surface_drag->world; - auto world_new_linear = world_new.linear(); - - if (true) - { - // Fix direction of up vector - Vec3d z_world = world_new_linear.col(2); - z_world.normalize(); - Vec3d wanted_up = suggest_up(z_world); - - Vec3d y_world = world_new_linear.col(1); - auto y_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); - - world_new = y_rotation * world_new; - world_new_linear = world_new.linear(); - } - - // Edit position from right - Transform3d volume_new{Eigen::Translation(m_surface_drag->instance_inv * hit->position)}; - volume_new.linear() = m_surface_drag->instance_inv.linear() * world_new_linear; - - // Check that transformation matrix is valid transformation - assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN - if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) - return true; - - // Check that scale in world did not changed - assert(!priv::calc_scale(world_linear, world_new_linear, Vec3d::UnitY()).has_value()); - assert(!priv::calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value()); - - const TextConfiguration &tc = *m_volume->text_configuration; - // fix baked transformation from .3mf store process - if (tc.fix_3mf_tr.has_value()) - volume_new = volume_new * (*tc.fix_3mf_tr); - - // apply move in Z direction and rotation by up vector - apply_transformation(tc.style.prop, volume_new); - - // Update transformation for all instances - for (GLVolume *vol : m_parent.get_volumes().volumes) { - if (vol->object_idx() != m_surface_drag->gl_volume->object_idx() || - vol->volume_idx() != m_surface_drag->gl_volume->volume_idx()) - continue; - vol->set_volume_transformation(volume_new); - } - + // during drag + else if (was_dragging && is_dragging) { // update scale of selected volume --> should be approx the same calculate_scale(); - - m_parent.set_as_dirty(); - return true; } - return false; + return res; } bool GLGizmoEmboss::on_mouse(const wxMouseEvent &mouse_event) { // not selected volume if (m_volume == nullptr || - priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr || + get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr || !m_volume->text_configuration.has_value()) return false; if (on_mouse_for_rotation(mouse_event)) return true; @@ -755,7 +564,7 @@ std::string GLGizmoEmboss::on_get_name() const { return _u8L("Emboss"); } void GLGizmoEmboss::on_render() { // no volume selected if (m_volume == nullptr || - priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr) + get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr) return; Selection &selection = m_parent.get_selection(); if (selection.is_empty()) return; @@ -861,7 +670,7 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) set_volume_by_selection(); // Do not render window for not selected text volume if (m_volume == nullptr || - priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr || + get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr || !m_volume->text_configuration.has_value()) { close(); return; @@ -870,15 +679,15 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) // Configuration creation double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); float main_toolbar_height = m_parent.get_main_toolbar_height(); - if (!m_gui_cfg.has_value() || // Exist configuration - first run + if (m_gui_cfg == nullptr || // Exist configuration - first run m_gui_cfg->screen_scale != screen_scale || // change of DPI m_gui_cfg->main_toolbar_height != main_toolbar_height // change size of view port ) { // Create cache for gui offsets - GuiCfg cfg = create_gui_configuration(); + GuiCfg cfg = create_gui_configuration(); cfg.screen_scale = screen_scale; cfg.main_toolbar_height = main_toolbar_height; - m_gui_cfg.emplace(std::move(cfg)); + m_gui_cfg = std::make_unique(std::move(cfg)); // set position near toolbar m_set_window_offset = ImVec2(-1.f, -1.f); @@ -1009,13 +818,13 @@ void GLGizmoEmboss::on_set_state() // when open window by "T" and no valid volume is selected, so Create new one if (m_volume == nullptr || - priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr ) { + get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr ) { // reopen gizmo when new object is created GLGizmoBase::m_state = GLGizmoBase::Off; if (wxGetApp().get_mode() == comSimple) // It's impossible to add a part in simple mode return; - // start creating new object + // start creating new object create_volume(ModelVolumeType::MODEL_PART); } @@ -1023,7 +832,7 @@ void GLGizmoEmboss::on_set_state() if (m_allow_open_near_volume) { m_set_window_offset = priv::calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size()); } else { - if (m_gui_cfg.has_value()) + if (m_gui_cfg != nullptr) priv::change_window_position(m_set_window_offset, false); else m_set_window_offset = ImVec2(-1, -1); @@ -1226,7 +1035,7 @@ void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); } void GLGizmoEmboss::set_volume_by_selection() { const Selection &selection = m_parent.get_selection(); - ModelVolume *vol = priv::get_selected_volume(selection); + ModelVolume *vol = get_selected_volume(selection); // is same volume selected? if (vol != nullptr && vol->id() == m_volume_id) return; @@ -1351,7 +1160,7 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) // The change of volume could show or hide part with setter on volume type if (m_volume == nullptr || - priv::get_volume(m_parent.get_selection().get_model()->objects, m_volume_id) == nullptr || + get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr || (m_volume->get_object()->volumes.size() == 1) != (volume->get_object()->volumes.size() == 1)){ m_should_set_minimal_windows_size = true; @@ -1409,35 +1218,6 @@ void GLGizmoEmboss::calculate_scale() { m_style_manager.clear_imgui_font(); } -ModelVolume *priv::get_model_volume(const GLVolume *gl_volume, const ModelObject *object) -{ - int volume_id = gl_volume->volume_idx(); - if (volume_id < 0 || static_cast(volume_id) >= object->volumes.size()) return nullptr; - return object->volumes[volume_id]; -} - -ModelVolume *priv::get_model_volume(const GLVolume *gl_volume, const ModelObjectPtrs &objects) -{ - int object_id = gl_volume->object_idx(); - if (object_id < 0 || static_cast(object_id) >= objects.size()) return nullptr; - return get_model_volume(gl_volume, objects[object_id]); -} - -ModelVolume *priv::get_selected_volume(const Selection &selection) -{ - int object_idx = selection.get_object_idx(); - // is more object selected? - if (object_idx == -1) return nullptr; - - auto volume_idxs = selection.get_volume_idxs(); - // is more volumes selected? - if (volume_idxs.size() != 1) return nullptr; - unsigned int vol_id_gl = *volume_idxs.begin(); - const GLVolume *vol_gl = selection.get_volume(vol_id_gl); - const ModelObjectPtrs &objects = selection.get_model()->objects; - return get_model_volume(vol_gl, objects); -} - // Run Job on main thread (blocking) - ONLY DEBUG static inline void execute_job(std::shared_ptr j) { @@ -1529,6 +1309,10 @@ bool GLGizmoEmboss::process() return true; } +namespace priv { +static bool is_text_empty(const std::string &text) { return text.empty() || text.find_first_not_of(" \n\t\r") == std::string::npos; } +} + void GLGizmoEmboss::close() { // remove volume when text is empty @@ -1579,10 +1363,10 @@ bool priv::apply_camera_dir(const Camera &camera, GLCanvas3D &canvas) { if (is_approx(cam_dir_tr, emboss_dir)) return false; assert(sel.get_volume_idxs().size() == 1); - GLVolume *vol = sel.get_volume(*sel.get_volume_idxs().begin()); + GLVolume *gl_volume = sel.get_volume(*sel.get_volume_idxs().begin()); Transform3d vol_rot; - Transform3d vol_tr = vol->get_volume_transformation().get_matrix(); + Transform3d vol_tr = gl_volume->get_volume_transformation().get_matrix(); // check whether cam_dir is opposit to emboss dir if (is_approx(cam_dir_tr, -emboss_dir)) { // rotate 180 DEG by y @@ -1602,8 +1386,8 @@ bool priv::apply_camera_dir(const Camera &camera, GLCanvas3D &canvas) { vol_rot * Eigen::Translation(offset_inv); //Transform3d res = vol_tr * vol_rot; - vol->set_volume_transformation(Geometry::Transformation(res)); - priv::get_model_volume(vol, sel.get_model()->objects)->set_transformation(res); + gl_volume->set_volume_transformation(Geometry::Transformation(res)); + get_model_volume(*gl_volume, sel.get_model()->objects)->set_transformation(res); return true; } @@ -2467,7 +2251,7 @@ void GLGizmoEmboss::draw_style_rename_button() bool can_rename = m_style_manager.exist_stored_style(); std::string title = _u8L("Rename style"); const char * popup_id = title.c_str(); - if (draw_button(IconType::rename, !can_rename)) { + if (priv::draw_button(m_icons, IconType::rename, !can_rename)) { assert(m_style_manager.get_stored_style()); ImGui::OpenPopup(popup_id); } @@ -2484,7 +2268,7 @@ void GLGizmoEmboss::draw_style_rename_button() void GLGizmoEmboss::draw_style_save_button(bool is_modified) { - if (draw_button(IconType::save, !is_modified)) { + if (draw_button(m_icons, IconType::save, !is_modified)) { // save styles to app config m_style_manager.store_styles_to_app_config(); }else if (ImGui::IsItemHovered()) { @@ -2554,7 +2338,7 @@ void GLGizmoEmboss::draw_style_add_button() const char *popup_id = title.c_str(); // save as new style ImGui::SameLine(); - if (draw_button(IconType::add, !can_add)) { + if (draw_button(m_icons, IconType::add, !can_add)) { if (!m_style_manager.exist_stored_style()) { m_style_manager.store_styles_to_app_config(wxGetApp().app_config); } else { @@ -2585,7 +2369,7 @@ void GLGizmoEmboss::draw_delete_style_button() { std::string title = _u8L("Remove style"); const char * popup_id = title.c_str(); static size_t next_style_index = std::numeric_limits::max(); - if (draw_button(IconType::erase, !can_delete)) { + if (draw_button(m_icons, IconType::erase, !can_delete)) { while (true) { // NOTE: can't use previous loaded activ index -> erase could change index size_t active_index = m_style_manager.get_style_index(); @@ -2807,8 +2591,8 @@ bool GLGizmoEmboss::draw_italic_button() bool is_font_italic = skew.has_value() || WxFontUtils::is_italic(wx_font); if (is_font_italic) { // unset italic - if (clickable(get_icon(IconType::italic, IconState::hovered), - get_icon(IconType::unitalic, IconState::hovered))) { + if (clickable(get_icon(m_icons, IconType::italic, IconState::hovered), + get_icon(m_icons, IconType::unitalic, IconState::hovered))) { skew.reset(); if (wx_font.GetStyle() != wxFontStyle::wxFONTSTYLE_NORMAL) { wxFont new_wx_font = wx_font; // copy @@ -2822,7 +2606,7 @@ bool GLGizmoEmboss::draw_italic_button() ImGui::SetTooltip("%s", _u8L("Unset italic").c_str()); } else { // set italic - if (draw_button(IconType::italic)) { + if (draw_button(m_icons, IconType::italic)) { wxFont new_wx_font = wx_font; // copy auto new_ff = WxFontUtils::set_italic(new_wx_font, *ff.font_file); if (new_ff != nullptr) { @@ -2845,7 +2629,7 @@ bool GLGizmoEmboss::draw_bold_button() { const std::optional &wx_font_opt = m_style_manager.get_wx_font(); const auto& ff = m_style_manager.get_font_file_with_cache(); if (!wx_font_opt.has_value() || !ff.has_value()) { - draw(get_icon(IconType::bold, IconState::disabled)); + draw(get_icon(m_icons, IconType::bold, IconState::disabled)); return false; } const wxFont &wx_font = *wx_font_opt; @@ -2854,8 +2638,8 @@ bool GLGizmoEmboss::draw_bold_button() { bool is_font_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font); if (is_font_bold) { // unset bold - if (clickable(get_icon(IconType::bold, IconState::hovered), - get_icon(IconType::unbold, IconState::hovered))) { + if (clickable(get_icon(m_icons, IconType::bold, IconState::hovered), + get_icon(m_icons, IconType::unbold, IconState::hovered))) { boldness.reset(); if (wx_font.GetWeight() != wxFontWeight::wxFONTWEIGHT_NORMAL) { wxFont new_wx_font = wx_font; // copy @@ -2869,7 +2653,7 @@ bool GLGizmoEmboss::draw_bold_button() { ImGui::SetTooltip("%s", _u8L("Unset bold").c_str()); } else { // set bold - if (draw_button(IconType::bold)) { + if (draw_button(m_icons, IconType::bold)) { wxFont new_wx_font = wx_font; // copy auto new_ff = WxFontUtils::set_bold(new_wx_font, *ff.font_file); if (new_ff != nullptr) { @@ -2922,7 +2706,7 @@ bool GLGizmoEmboss::revertible(const std::string &name, // render revert changes button if (changed) { ImGui::SameLine(undo_offset); - if (draw_button(IconType::undo)) { + if (draw_button(m_icons, IconType::undo)) { value = *default_value; return true; } else if (ImGui::IsItemHovered()) @@ -3074,7 +2858,7 @@ void GLGizmoEmboss::draw_style_edit() { EmbossStyle &style = m_style_manager.get_style(); if (exist_change_in_font) { ImGui::SameLine(ImGui::GetStyle().FramePadding.x); - if (draw_button(IconType::undo)) { + if (draw_button(m_icons, IconType::undo)) { const EmbossStyle *stored_style = m_style_manager.get_stored_style(); style.path = stored_style->path; style.prop.boldness = stored_style->prop.boldness; @@ -3293,7 +3077,7 @@ std::optional priv::calc_surface_offset(const ModelVolume &volume, Raycas raycast_manager.actualize(volume.get_object(), &cond); //const Selection &selection = m_parent.get_selection(); - const GLVolume *gl_volume = priv::get_gl_volume(selection); + const GLVolume *gl_volume = get_selected_gl_volume(selection); Transform3d to_world = priv::world_matrix(gl_volume, selection.get_model()); Vec3d point = to_world * Vec3d::Zero(); Vec3d direction = to_world.linear() * (-Vec3d::UnitZ()); @@ -3555,7 +3339,7 @@ void GLGizmoEmboss::draw_advanced() } if (ImGui::Button(_u8L("Set text to face camera").c_str())) { - assert(priv::get_selected_volume(m_parent.get_selection()) == m_volume); + assert(get_selected_volume(m_parent.get_selection()) == m_volume); const Camera &cam = wxGetApp().plater()->get_camera(); bool use_surface = m_style_manager.get_style().prop.use_surface; if (priv::apply_camera_dir(cam, m_parent) && use_surface) @@ -3800,13 +3584,13 @@ void GLGizmoEmboss::init_icons() m_icons = m_icon_manager.init(filenames, size, type); } -const IconManager::Icon &GLGizmoEmboss::get_icon(IconType type, IconState state) { return *m_icons[(unsigned) type][(unsigned) state]; } -bool GLGizmoEmboss::draw_button(IconType type, bool disable) +const IconManager::Icon &priv::get_icon(const IconManager::VIcons& icons, IconType type, IconState state) { return *icons[(unsigned) type][(unsigned) state]; } +bool priv::draw_button(const IconManager::VIcons &icons, IconType type, bool disable) { return Slic3r::GUI::button( - get_icon(type, IconState::activable), - get_icon(type, IconState::hovered), - get_icon(type, IconState::disabled), + get_icon(icons, type, IconState::activable), + get_icon(icons, type, IconState::hovered), + get_icon(icons, type, IconState::disabled), disable ); } @@ -3921,37 +3705,6 @@ void priv::start_create_volume_job(const ModelObject *object, queue_job(worker, std::move(job)); } -const ModelVolume *priv::get_volume(const ModelObjectPtrs &objects, const ObjectID &volume_id) -{ - for (const ModelObject *obj : objects) - for (const ModelVolume *vol : obj->volumes) - if (vol->id() == volume_id) - return vol; - return nullptr; -}; - -GLVolume * priv::get_hovered_gl_volume(const GLCanvas3D &canvas) { - int hovered_id_signed = canvas.get_first_hover_volume_idx(); - if (hovered_id_signed < 0) return nullptr; - - size_t hovered_id = static_cast(hovered_id_signed); - const GLVolumePtrs &volumes = canvas.get_volumes().volumes; - if (hovered_id >= volumes.size()) return nullptr; - - return volumes[hovered_id]; -} - -std::optional priv::ray_from_camera(const RaycastManager &raycaster, - const Vec2d &mouse_pos, - const Camera &camera, - const RaycastManager::ISkip *skip) -{ - Vec3d point; - Vec3d direction; - CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); - return raycaster.first_hit(point, direction, skip); -} - 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) { @@ -3967,11 +3720,11 @@ bool priv::start_create_volume_on_surface_job( size_t vol_id = obj->volumes[gl_volume->volume_idx()]->id().id; auto cond = RaycastManager::AllowVolumes({vol_id}); - RaycastManager::Meshes meshes = priv::create_meshes(canvas, cond); + RaycastManager::Meshes meshes = create_meshes(canvas, cond); raycaster.actualize(obj, &cond, &meshes); const Camera &camera = plater->get_camera(); - std::optional hit = priv::ray_from_camera(raycaster, screen_coor, camera, &cond); + std::optional hit = ray_from_camera(raycaster, screen_coor, camera, &cond); // context menu for add text could be open only by right click on an // object. After right click, object is selected and object_idx is set @@ -4009,7 +3762,7 @@ void priv::find_closest_volume(const Selection &selection, double center_sq_distance = std::numeric_limits::max(); for (unsigned int id : indices) { const GLVolume *gl_volume = selection.get_volume(id); - ModelVolume *volume = priv::get_model_volume(gl_volume, objects); + const ModelVolume *volume = get_model_volume(*gl_volume, objects); if (!volume->is_model_part()) continue; Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *gl_volume); Vec2d c = hull.centroid().cast(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 2bf50ce5e0..5c5a63ebe5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -6,6 +6,7 @@ #include "GLGizmoBase.hpp" #include "GLGizmoRotate.hpp" #include "slic3r/GUI/IconManager.hpp" +#include "slic3r/GUI/SurfaceDrag.hpp" #include "slic3r/Utils/RaycastManager.hpp" #include "slic3r/Utils/EmbossStyleManager.hpp" @@ -25,7 +26,6 @@ class wxFont; namespace Slic3r{ class AppConfig; class GLVolume; - enum class ModelVolumeType : int; } @@ -145,7 +145,7 @@ private: template bool revertible(const std::string &name, T &value, const T *default_value, const std::string &undo_tooltip, float undo_offset, Draw draw); - bool m_should_set_minimal_windows_size = false; + bool m_should_set_minimal_windows_size = false; void set_minimal_window_size(bool is_advance_edit_style); ImVec2 get_minimal_window_size() const; @@ -161,65 +161,12 @@ private: bool m_is_unknown_font; void create_notification_not_valid_font(const TextConfiguration& tc); void remove_notification_not_valid_font(); - - // This configs holds GUI layout size given by translated texts. - // etc. When language changes, GUI is recreated and this class constructed again, - // so the change takes effect. (info by GLGizmoFdmSupports.hpp) - struct GuiCfg - { - // Detect invalid config values when change monitor DPI - double screen_scale; - float main_toolbar_height; - - // Zero means it is calculated in init function - ImVec2 minimal_window_size = ImVec2(0, 0); - ImVec2 minimal_window_size_with_advance = ImVec2(0, 0); - ImVec2 minimal_window_size_with_collections = ImVec2(0, 0); - float height_of_volume_type_selector = 0.f; - float input_width = 0.f; - float delete_pos_x = 0.f; - float max_style_name_width = 0.f; - unsigned int icon_width = 0; - - // maximal width and height of style image - Vec2i max_style_image_size = Vec2i(0, 0); - - float indent = 0.f; - float input_offset = 0.f; - float advanced_input_offset = 0.f; - - ImVec2 text_size; - - // maximal size of face name image - Vec2i face_name_size = Vec2i(100, 0); - float face_name_max_width = 100.f; - float face_name_texture_offset_x = 105.f; - - // maximal texture generate jobs running at once - unsigned int max_count_opened_font_files = 10; - - // Only translations needed for calc GUI size - struct Translations - { - std::string font; - std::string size; - std::string depth; - std::string use_surface; - - // advanced - std::string char_gap; - std::string line_gap; - std::string boldness; - std::string italic; - std::string surface_distance; - std::string angle; - std::string collection; - }; - Translations translations; - }; - std::optional m_gui_cfg; + + struct GuiCfg; + std::unique_ptr m_gui_cfg = nullptr; static GuiCfg create_gui_configuration(); + // Is open tree with advanced options bool m_is_advanced_edit_style = false; // when true window will appear near to text volume when open @@ -228,6 +175,7 @@ private: // setted only when wanted to use - not all the time std::optional m_set_window_offset; + // Keep information about stored styles and loaded actual style to compare with Emboss::StyleManager m_style_manager; struct FaceName{ @@ -290,7 +238,8 @@ private: // Text to emboss std::string m_text; - // actual volume + // current selected volume + // NOTE: Be carefull could be uninitialized (removed from Model) ModelVolume *m_volume; // When work with undo redo stack there could be situation that @@ -308,27 +257,6 @@ private: // Value is set only when dragging rotation to calculate actual angle std::optional m_rotate_start_angle; - // Data for drag&drop over surface with mouse - struct SurfaceDrag - { - // hold screen coor offset of cursor from object center - Vec2d mouse_offset; - - // Start dragging text transformations to world - Transform3d world; - - // Invers transformation of text volume instance - // Help convert world transformation to instance space - Transform3d instance_inv; - - // Dragged gl volume - GLVolume *gl_volume; - - // condition for raycaster - RaycastManager::AllowVolumes condition; - - bool exist_hit = true; - }; // Keep data about dragging only during drag&drop std::optional m_surface_drag; @@ -343,27 +271,8 @@ private: // drawing icons IconManager m_icon_manager; - std::vector m_icons; - + IconManager::VIcons m_icons; void init_icons(); - enum class IconType : unsigned { - rename = 0, - erase, - add, - save, - undo, - italic, - unitalic, - bold, - unbold, - system_selector, - open_file, - // automatic calc of icon's count - _count - }; - enum class IconState : unsigned { activable = 0, hovered /*1*/, disabled /*2*/ }; - const IconManager::Icon& get_icon(IconType type, IconState state); - bool draw_button(IconType icon, bool disable = false); // only temporary solution static const std::string M_ICON_FILENAME; diff --git a/src/slic3r/GUI/IconManager.hpp b/src/slic3r/GUI/IconManager.hpp index 78b1395dce..aa7afda800 100644 --- a/src/slic3r/GUI/IconManager.hpp +++ b/src/slic3r/GUI/IconManager.hpp @@ -63,6 +63,8 @@ public: // && size.x > 0 && size.y > 0 && tl.x != br.x && tl.y != br.y; }; using Icons = std::vector >; + // Vector of icons, each vector contain multiple use of a SVG render + using VIcons = std::vector; /// /// Initialize raster texture on GPU with given images @@ -71,7 +73,7 @@ public: /// Define files and its /// Rasterized icons stored on GPU, /// Same size and order as input, each item of vector is set of texture in order by RasterType - std::vector init(const InitTypes &input); + VIcons init(const InitTypes &input); /// /// Initialize multiple icons with same settings for size and type @@ -83,7 +85,7 @@ public: /// together color, white and gray = RasterType::color | RasterType::white_only_data | RasterType::gray_only_data /// Rasterized icons stored on GPU, /// Same size and order as file_paths, each item of vector is set of texture in order by RasterType - std::vector init(const std::vector &file_paths, const ImVec2 &size, RasterType type = RasterType::color); + VIcons init(const std::vector &file_paths, const ImVec2 &size, RasterType type = RasterType::color); /// /// Release icons which are hold only by this manager diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index fd0a5ec595..3e85d1d884 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -3364,5 +3364,28 @@ void Selection::transform_volume_relative(GLVolume& volume, const VolumeCache& v } #endif // ENABLE_WORLD_COORDINATE +ModelVolume *get_selected_volume(const Selection &selection) +{ + const GLVolume *vol_gl = get_selected_gl_volume(selection); + const ModelObjectPtrs &objects = selection.get_model()->objects; + return get_model_volume(*vol_gl, objects); +} + +const GLVolume *get_selected_gl_volume(const Selection &selection) +{ + int object_idx = selection.get_object_idx(); + // is more object selected? + if (object_idx == -1) + return nullptr; + + const auto &list = selection.get_volume_idxs(); + // is more volumes selected? + if (list.size() != 1) + return nullptr; + + unsigned int volume_idx = *list.begin(); + return selection.get_volume(volume_idx); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index c1c97bd2e6..a000f661bf 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -17,6 +17,7 @@ namespace Slic3r { class Shader; class Model; class ModelObject; +class ModelVolume; class GLVolume; class GLArrow; class GLCurvedArrow; @@ -519,6 +520,9 @@ private: #endif // ENABLE_WORLD_COORDINATE }; +ModelVolume *get_selected_volume(const Selection &selection); +const GLVolume *get_selected_gl_volume(const Selection &selection); + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp new file mode 100644 index 0000000000..d2986bbf42 --- /dev/null +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -0,0 +1,244 @@ +#include "SurfaceDrag.hpp" + +#include "libslic3r/Model.hpp" // ModelVolume +#include "GLCanvas3D.hpp" +#include "slic3r/Utils/RaycastManager.hpp" + +#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/CameraUtils.hpp" + +#include "libslic3r/Emboss.hpp" + +// getter on camera needs +//#include "slic3r/GUI/GUI_App.hpp" +//#include "Plater.hpp" + +namespace Slic3r::GUI { + +static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const ModelVolume &volume, const Camera &camera) +{ + const Transform3d &volume_tr = volume.get_matrix(); + assert(volume.text_configuration.has_value()); + + auto calc_offset = [&screen_coor, &volume_tr, &camera, &volume](const Transform3d &instrance_tr) -> Vec2d { + Transform3d to_world = instrance_tr * volume_tr; + + // Use fix of .3mf loaded tranformation when exist + if (volume.text_configuration->fix_3mf_tr.has_value()) + to_world = to_world * (*volume.text_configuration->fix_3mf_tr); + // zero point of volume in world coordinate system + Vec3d volume_center = to_world.translation(); + // screen coordinate of volume center + Vec2i coor = CameraUtils::project(camera, volume_center); + return coor.cast() - screen_coor; + }; + + auto object = volume.get_object(); + assert(!object->instances.empty()); + // Speed up for one instance + if (object->instances.size() == 1) + return calc_offset(object->instances.front()->get_matrix()); + + Vec2d nearest_offset; + double nearest_offset_size = std::numeric_limits::max(); + for (const ModelInstance *instance : object->instances) { + Vec2d offset = calc_offset(instance->get_matrix()); + double offset_size = offset.norm(); + if (nearest_offset_size < offset_size) + continue; + nearest_offset_size = offset_size; + nearest_offset = offset; + } + return nearest_offset; +} + + // Calculate scale in world +static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) +{ + Vec3d from_dir = from * dir; + Vec3d to_dir = to * dir; + double from_scale_sq = from_dir.squaredNorm(); + double to_scale_sq = to_dir.squaredNorm(); + if (is_approx(from_scale_sq, to_scale_sq, 1e-3)) + return {}; // no scale + return sqrt(from_scale_sq / to_scale_sq); +} + +bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, + const Camera &camera, + std::optional &surface_drag, + GLCanvas3D &canvas, + RaycastManager &raycast_manager) +{ + // Fix when leave window during dragging + // Fix when click right button + if (surface_drag.has_value() && !mouse_event.Dragging()) { + // write transformation from UI into model + canvas.do_move(L("Surface move")); + + // allow moving with object again + canvas.enable_moving(true); + surface_drag.reset(); + + // only left up is correct + // otherwise it is fix state and return false + return mouse_event.LeftUp(); + } + + if (mouse_event.Moving()) + return false; + + // detect start text dragging + if (mouse_event.LeftDown()) { + // selected volume + GLVolume *gl_volume = get_selected_gl_volume(canvas); + if (gl_volume == nullptr) + return false; + + // is selected volume closest hovered? + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + int hovered_idx = canvas.get_first_hover_volume_idx(); + if (hovered_idx < 0 || + hovered_idx >= gl_volumes.size() || + gl_volumes[hovered_idx] != gl_volume) + return false; + + const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects); + const ModelInstance *instance = get_model_instance(*gl_volume, *object); + const ModelVolume *volume = get_model_volume(*gl_volume, *object); + + assert(object != nullptr && instance != nullptr && volume != nullptr); + if (object == nullptr || instance == nullptr || volume == nullptr) + return false; + + const ModelVolumePtrs &volumes = object->volumes; + std::vector allowed_volumes_id; + if (volumes.size() > 1) { + allowed_volumes_id.reserve(volumes.size() - 1); + for (auto &v : volumes) { + // skip actual selected object + if (v->id() == volume->id()) + continue; + // drag only above part not modifiers or negative surface + if (!v->is_model_part()) + continue; + allowed_volumes_id.emplace_back(v->id().id); + } + } + RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id)); + RaycastManager::Meshes meshes = create_meshes(canvas, condition); + // initialize raycasters + // INFO: It could slows down for big objects + // (may be move to thread and do not show drag until it finish) + raycast_manager.actualize(instance, &condition, &meshes); + + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + Vec2d mouse_offset = calc_screen_offset_to_volume_center(mouse_pos, *volume, camera); + + Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix(); + + if (volume->text_configuration.has_value()) { + const TextConfiguration &tc = *volume->text_configuration; + // fix baked transformation from .3mf store process + if (tc.fix_3mf_tr.has_value()) + volume_tr = volume_tr * tc.fix_3mf_tr->inverse(); + } + + Transform3d instance_tr = instance->get_matrix(); + Transform3d instance_tr_inv = instance_tr.inverse(); + Transform3d world_tr = instance_tr * volume_tr; + surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition}; + + // disable moving with object by mouse + canvas.enable_moving(false); + return true; + } + + // Dragging starts out of window + if (!surface_drag.has_value()) + return false; + + if (mouse_event.Dragging()) { + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + Vec2d offseted_mouse = mouse_pos + surface_drag->mouse_offset; + + std::optional hit = ray_from_camera( + raycast_manager, offseted_mouse, camera, &surface_drag->condition); + + surface_drag->exist_hit = hit.has_value(); + if (!hit.has_value()) { + // cross hair need redraw + canvas.set_as_dirty(); + return true; + } + + auto world_linear = surface_drag->world.linear(); + // Calculate offset: transformation to wanted position + { + // Reset skew of the text Z axis: + // Project the old Z axis into a new Z axis, which is perpendicular to the old XY plane. + Vec3d old_z = world_linear.col(2); + Vec3d new_z = world_linear.col(0).cross(world_linear.col(1)); + world_linear.col(2) = new_z * (old_z.dot(new_z) / new_z.squaredNorm()); + } + + Vec3d text_z_world = world_linear.col(2); // world_linear * Vec3d::UnitZ() + auto z_rotation = Eigen::Quaternion::FromTwoVectors(text_z_world, hit->normal); + Transform3d world_new = z_rotation * surface_drag->world; + auto world_new_linear = world_new.linear(); + + // Fix direction of up vector + { + Vec3d z_world = world_new_linear.col(2); + z_world.normalize(); + Vec3d wanted_up = Emboss::suggest_up(z_world); + + Vec3d y_world = world_new_linear.col(1); + auto y_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); + + world_new = y_rotation * world_new; + world_new_linear = world_new.linear(); + } + + // Edit position from right + Transform3d volume_new{Eigen::Translation(surface_drag->instance_inv * hit->position)}; + volume_new.linear() = surface_drag->instance_inv.linear() * world_new_linear; + + // Check that transformation matrix is valid transformation + assert(volume_new.matrix()(0, 0) == volume_new.matrix()(0, 0)); // Check valid transformation not a NAN + if (volume_new.matrix()(0, 0) != volume_new.matrix()(0, 0)) + return true; + + // Check that scale in world did not changed + assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitY()).has_value()); + assert(!calc_scale(world_linear, world_new_linear, Vec3d::UnitZ()).has_value()); + + const ModelVolume *volume = get_model_volume(*surface_drag->gl_volume, canvas.get_model()->objects); + if (volume != nullptr && volume->text_configuration.has_value()) { + const TextConfiguration &tc = *volume->text_configuration; + // fix baked transformation from .3mf store process + if (tc.fix_3mf_tr.has_value()) + volume_new = volume_new * (*tc.fix_3mf_tr); + + // apply move in Z direction and rotation by up vector + Emboss::apply_transformation(tc.style.prop, volume_new); + } + + // Update transformation for all instances + for (GLVolume *vol : canvas.get_volumes().volumes) { + if (vol->object_idx() != surface_drag->gl_volume->object_idx() || vol->volume_idx() != surface_drag->gl_volume->volume_idx()) + continue; + vol->set_volume_transformation(volume_new); + } + + canvas.set_as_dirty(); + return true; + } + return false; +} + +} // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/SurfaceDrag.hpp b/src/slic3r/GUI/SurfaceDrag.hpp new file mode 100644 index 0000000000..c3dc9cd2db --- /dev/null +++ b/src/slic3r/GUI/SurfaceDrag.hpp @@ -0,0 +1,47 @@ +#ifndef slic3r_SurfaceDrag_hpp_ +#define slic3r_SurfaceDrag_hpp_ + +#include +#include "libslic3r/Point.hpp" // Vec2d, Transform3d +#include "slic3r/Utils/RaycastManager.hpp" +#include "wx/event.h" // wxMouseEvent + +namespace Slic3r { +class GLVolume; +} // namespace Slic3r + +namespace Slic3r::GUI { +class GLCanvas3D; +struct Camera; + +// Data for drag&drop over surface with mouse +struct SurfaceDrag +{ + // hold screen coor offset of cursor from object center + Vec2d mouse_offset; + + // Start dragging text transformations to world + Transform3d world; + + // Invers transformation of text volume instance + // Help convert world transformation to instance space + Transform3d instance_inv; + + // Dragged gl volume + GLVolume *gl_volume; + + // condition for raycaster + RaycastManager::AllowVolumes condition; + + // Flag whether coordinate hit some volume + bool exist_hit = true; +}; + +bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, + const Camera &camera, + std::optional &surface_drag, + GLCanvas3D &canvas, + RaycastManager &raycast_manager); + +} // namespace Slic3r::GUI +#endif // slic3r_SurfaceDrag_hpp_ \ No newline at end of file diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 3b0ace2a34..18c9bb2f19 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -295,3 +295,47 @@ RaycastManager::TrItems::iterator priv::find(RaycastManager::TrItems &items, con return items.end(); return it; } + +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/CameraUtils.hpp" + +namespace Slic3r::GUI{ + +RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes &condition) +{ + SceneRaycaster::EType type = SceneRaycaster::EType::Volume; + auto scene_casters = canvas.get_raycasters_for_picking(type); + const std::vector> &casters = *scene_casters; + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + const ModelObjectPtrs &objects = canvas.get_model()->objects; + + RaycastManager::Meshes meshes; + for (const std::shared_ptr &caster : casters) { + int index = SceneRaycaster::decode_id(type, caster->get_id()); + if (index < 0 || index >= gl_volumes.size()) + continue; + const GLVolume *gl_volume = gl_volumes[index]; + const ModelVolume *volume = get_model_volume(*gl_volume, objects); + size_t id = volume->id().id; + if (condition.skip(id)) + continue; + auto mesh = std::make_unique(caster->get_raycaster()->get_aabb_mesh()); + meshes.emplace_back(std::make_pair(id, std::move(mesh))); + } + return meshes; +} + + +std::optional ray_from_camera(const RaycastManager &raycaster, + const Vec2d &mouse_pos, + const Camera &camera, + const RaycastManager::ISkip *skip) +{ + Vec3d point; + Vec3d direction; + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + return raycaster.first_hit(point, direction, skip); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index 4ef9b7ca76..a3cd3ef910 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -144,6 +144,29 @@ public: Transform3d get_transformation(const TrKey &tr_key) const; }; +class GLCanvas3D; +/// +/// Use scene Raycasters and prepare data for actualize RaycasterManager +/// +/// contain Scene raycasters +/// Limit for scene casters +/// Meshes +RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes &condition); + +struct Camera; +/// +/// Unproject on mesh by Mesh raycasters +/// +/// Position of mouse on screen +/// Projection params +/// Define which caster will be skipped, null mean no skip +/// Position on surface, normal direction in world coorinate +/// + key, to know hitted instance and volume +std::optional ray_from_camera(const RaycastManager &raycaster, + const Vec2d &mouse_pos, + const Camera &camera, + const RaycastManager::ISkip *skip); + } // namespace Slic3r::GUI #endif // slic3r_RaycastManager_hpp_ From 6300396707ba8e75881ebf04d6dcccde52c35344 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 10:39:59 +0100 Subject: [PATCH 41/64] More trace for detect place of crash on Mac --- src/slic3r/Utils/WxFontUtils.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index 2851098f2e..4156bb9983 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -174,8 +174,11 @@ std::string WxFontUtils::store_wxFont(const wxFont &font) wxFont WxFontUtils::load_wxFont(const std::string &font_descriptor) { + BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor << "'font descriptor string param of load_wxFont()"; wxString font_descriptor_wx(font_descriptor); + BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor_wx.c_str() << "' wx string descriptor"; wxFont wx_font(font_descriptor_wx); + BOOST_LOG_TRIVIAL(trace) << "loaded font is '" << get_human_readable_name(wx_font) << "'."; return wx_font; } From 6b2c834466ba1bf8ad30124924559aec58e502c0 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 11:17:32 +0100 Subject: [PATCH 42/64] Fix: ../src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp:3076:44: warning: braces around scalar initializer [-Wbraced-scalar-init] ../src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp:3693:74: warning: suggest braces around initialization of subobject [-Wmissing-braces] ../src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp:461:13: warning: unused function 'calc_mouse_to_center_text_offset' [-Wunused-function] ../src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp:165:12: note: forward declaration of 'Slic3r::GUI::GLGizmoEmboss::GuiCfg' --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 115 ++---------------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 58 +++++++++++- src/slic3r/GUI/SurfaceDrag.cpp | 15 ++-- 3 files changed, 71 insertions(+), 117 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 298829c01b..90325e2117 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -120,62 +120,6 @@ template void to_range_pi_pi(T& angle) } // namespace priv using namespace priv; -// This configs holds GUI layout size given by translated texts. -// etc. When language changes, GUI is recreated and this class constructed again, -// so the change takes effect. (info by GLGizmoFdmSupports.hpp) -struct GLGizmoEmboss::GuiCfg -{ - // Detect invalid config values when change monitor DPI - double screen_scale; - float main_toolbar_height; - - // Zero means it is calculated in init function - ImVec2 minimal_window_size = ImVec2(0, 0); - ImVec2 minimal_window_size_with_advance = ImVec2(0, 0); - ImVec2 minimal_window_size_with_collections = ImVec2(0, 0); - float height_of_volume_type_selector = 0.f; - float input_width = 0.f; - float delete_pos_x = 0.f; - float max_style_name_width = 0.f; - unsigned int icon_width = 0; - - // maximal width and height of style image - Vec2i max_style_image_size = Vec2i(0, 0); - - float indent = 0.f; - float input_offset = 0.f; - float advanced_input_offset = 0.f; - - ImVec2 text_size; - - // maximal size of face name image - Vec2i face_name_size = Vec2i(100, 0); - float face_name_max_width = 100.f; - float face_name_texture_offset_x = 105.f; - - // maximal texture generate jobs running at once - unsigned int max_count_opened_font_files = 10; - - // Only translations needed for calc GUI size - struct Translations - { - std::string font; - std::string size; - std::string depth; - std::string use_surface; - - // advanced - std::string char_gap; - std::string line_gap; - std::string boldness; - std::string italic; - std::string surface_distance; - std::string angle; - std::string collection; - }; - Translations translations; -}; - GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) : GLGizmoBase(parent, M_ICON_FILENAME, -2) , m_volume(nullptr) @@ -402,14 +346,6 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) } namespace priv { -/// -/// Calculate offset from mouse position to center of text -/// -/// Screan mouse position -/// Selected volume(text) -/// Offset in screan coordinate -static Vec2d calc_mouse_to_center_text_offset(const Vec2d &mouse, const ModelVolume &mv); - /// /// Get transformation to world /// - use fix after store to 3mf when exists @@ -458,43 +394,6 @@ Transform3d priv::world_matrix(const Selection &selection) return world_matrix(gl_volume, selection.get_model()); } -Vec2d priv::calc_mouse_to_center_text_offset(const Vec2d& mouse, const ModelVolume& mv) { - const Transform3d &volume_tr = mv.get_matrix(); - const Camera &camera = wxGetApp().plater()->get_camera(); - assert(mv.text_configuration.has_value()); - - auto calc_offset = [&mouse, &volume_tr, &camera, &mv] - (const Transform3d &instrance_tr) -> Vec2d { - Transform3d to_world = instrance_tr * volume_tr; - - // Use fix of .3mf loaded tranformation when exist - if (mv.text_configuration->fix_3mf_tr.has_value()) - to_world = to_world * (*mv.text_configuration->fix_3mf_tr); - // zero point of volume in world coordinate system - Vec3d volume_center = to_world.translation(); - // screen coordinate of volume center - Vec2i coor = CameraUtils::project(camera, volume_center); - return coor.cast() - mouse; - }; - - auto object = mv.get_object(); - assert(!object->instances.empty()); - // Speed up for one instance - if (object->instances.size() == 1) - return calc_offset(object->instances.front()->get_matrix()); - - Vec2d nearest_offset; - double nearest_offset_size = std::numeric_limits::max(); - for (const ModelInstance *instance : object->instances) { - Vec2d offset = calc_offset(instance->get_matrix()); - double offset_size = offset.norm(); - if (nearest_offset_size < offset_size) continue; - nearest_offset_size = offset_size; - nearest_offset = offset; - } - return nearest_offset; -} - bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { // exist selected volume? @@ -679,7 +578,7 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) // Configuration creation double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); float main_toolbar_height = m_parent.get_main_toolbar_height(); - if (m_gui_cfg == nullptr || // Exist configuration - first run + if (!m_gui_cfg.has_value() || // Exist configuration - first run m_gui_cfg->screen_scale != screen_scale || // change of DPI m_gui_cfg->main_toolbar_height != main_toolbar_height // change size of view port ) { @@ -687,7 +586,7 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) GuiCfg cfg = create_gui_configuration(); cfg.screen_scale = screen_scale; cfg.main_toolbar_height = main_toolbar_height; - m_gui_cfg = std::make_unique(std::move(cfg)); + m_gui_cfg.emplace(std::move(cfg)); // set position near toolbar m_set_window_offset = ImVec2(-1.f, -1.f); @@ -816,7 +715,7 @@ void GLGizmoEmboss::on_set_state() // Try(when exist) set text configuration by volume set_volume_by_selection(); - // when open window by "T" and no valid volume is selected, so Create new one + // when open window by "T" and no valid volume is selected, so Create new one if (m_volume == nullptr || get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr ) { // reopen gizmo when new object is created @@ -832,7 +731,7 @@ void GLGizmoEmboss::on_set_state() if (m_allow_open_near_volume) { m_set_window_offset = priv::calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size()); } else { - if (m_gui_cfg != nullptr) + if (m_gui_cfg.has_value()) priv::change_window_position(m_set_window_offset, false); else m_set_window_offset = ImVec2(-1, -1); @@ -3073,7 +2972,7 @@ void GLGizmoEmboss::do_rotate(float relative_z_angle) std::optional priv::calc_surface_offset(const ModelVolume &volume, RaycastManager &raycast_manager, const Selection &selection) { // Move object on surface - auto cond = RaycastManager::SkipVolume({volume.id().id}); + auto cond = RaycastManager::SkipVolume(volume.id().id); raycast_manager.actualize(volume.get_object(), &cond); //const Selection &selection = m_parent.get_selection(); @@ -3690,8 +3589,8 @@ void priv::start_create_volume_job(const ModelObject *object, 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); - CreateSurfaceVolumeData surface_data{std::move(emboss_data), volume_trmat, is_outside, - std::move(sources), volume_type, object->id()}; + SurfaceVolumeData sfvd{volume_trmat, is_outside, 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)); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 5c5a63ebe5..ca4d053317 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -162,8 +162,62 @@ private: void create_notification_not_valid_font(const TextConfiguration& tc); void remove_notification_not_valid_font(); - struct GuiCfg; - std::unique_ptr m_gui_cfg = nullptr; + // This configs holds GUI layout size given by translated texts. + // etc. When language changes, GUI is recreated and this class constructed again, + // so the change takes effect. (info by GLGizmoFdmSupports.hpp) + struct GuiCfg + { + // Detect invalid config values when change monitor DPI + double screen_scale; + float main_toolbar_height; + + // Zero means it is calculated in init function + ImVec2 minimal_window_size = ImVec2(0, 0); + ImVec2 minimal_window_size_with_advance = ImVec2(0, 0); + ImVec2 minimal_window_size_with_collections = ImVec2(0, 0); + float height_of_volume_type_selector = 0.f; + float input_width = 0.f; + float delete_pos_x = 0.f; + float max_style_name_width = 0.f; + unsigned int icon_width = 0; + + // maximal width and height of style image + Vec2i max_style_image_size = Vec2i(0, 0); + + float indent = 0.f; + float input_offset = 0.f; + float advanced_input_offset = 0.f; + + ImVec2 text_size; + + // maximal size of face name image + Vec2i face_name_size = Vec2i(100, 0); + float face_name_max_width = 100.f; + float face_name_texture_offset_x = 105.f; + + // maximal texture generate jobs running at once + unsigned int max_count_opened_font_files = 10; + + // Only translations needed for calc GUI size + struct Translations + { + std::string font; + std::string size; + std::string depth; + std::string use_surface; + + // advanced + std::string char_gap; + std::string line_gap; + std::string boldness; + std::string italic; + std::string surface_distance; + std::string angle; + std::string collection; + }; + Translations translations; + }; + std::optional m_gui_cfg; static GuiCfg create_gui_configuration(); // Is open tree with advanced options diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index d2986bbf42..46b6121588 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -3,18 +3,19 @@ #include "libslic3r/Model.hpp" // ModelVolume #include "GLCanvas3D.hpp" #include "slic3r/Utils/RaycastManager.hpp" - #include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/CameraUtils.hpp" - #include "libslic3r/Emboss.hpp" -// getter on camera needs -//#include "slic3r/GUI/GUI_App.hpp" -//#include "Plater.hpp" - namespace Slic3r::GUI { - + +/// +/// Calculate offset from mouse position to center of text +/// +/// Position on screen[in Px] e.g. mouse position +/// Selected volume(text) +/// Actual position and view direction of camera +/// Offset in screen coordinate static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const ModelVolume &volume, const Camera &camera) { const Transform3d &volume_tr = volume.get_matrix(); From a1a57eb61c5823eb2757b80a5b6e6aff49138a97 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 15:24:33 +0100 Subject: [PATCH 43/64] Separate drawing of cross hair --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 57 ++++++++++--------------- src/slic3r/GUI/ImGuiWrapper.cpp | 11 +++++ src/slic3r/GUI/ImGuiWrapper.hpp | 14 ++++++ 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 90325e2117..7bf9635a4d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -137,6 +137,14 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent) // Private namespace with helper function for create volume namespace priv { + +/// +/// Check if volume type is possible use for new text volume +/// +/// Type +/// True when allowed otherwise false +static bool is_valid(ModelVolumeType volume_type); + /// /// Prepare data for emboss /// @@ -146,8 +154,6 @@ namespace priv { /// Base data for emboss text static DataBase create_emboss_data_base(const std::string &text, StyleManager &style_manager, std::shared_ptr> &cancel); -static bool is_valid(ModelVolumeType volume_type); - /// /// Start job for add new volume to object with given transformation /// @@ -200,6 +206,7 @@ static void find_closest_volume(const Selection &selection, /// Screen coordinat, where to create new object laying on bed static void start_create_object_job(DataBase &emboss_data, const Vec2d &coor); +// Loaded icons enum // Have to match order of files in function GLGizmoEmboss::init_icons() enum class IconType : unsigned { rename = 0, @@ -218,21 +225,13 @@ enum class IconType : unsigned { }; // Define rendered version of icon enum class IconState : unsigned { activable = 0, hovered /*1*/, disabled /*2*/ }; +// selector for icon by enum const IconManager::Icon &get_icon(const IconManager::VIcons& icons, IconType type, IconState state); +// short call of Slic3r::GUI::button bool draw_button(const IconManager::VIcons& icons, IconType type, bool disable = false); } // namespace priv -bool priv::is_valid(ModelVolumeType volume_type){ - if (volume_type == ModelVolumeType::MODEL_PART || - volume_type == ModelVolumeType::NEGATIVE_VOLUME || - volume_type == ModelVolumeType::PARAMETER_MODIFIER) - return true; - - BOOST_LOG_TRIVIAL(error) << "Can't create embossed volume with this type: " << (int)volume_type; - return false; -} - void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos) { if (!priv::is_valid(volume_type)) return; @@ -350,7 +349,7 @@ namespace priv { /// Get transformation to world /// - use fix after store to 3mf when exists /// -/// +/// Scene volume /// To identify MovelVolume with fix transformation /// static Transform3d world_matrix(const GLVolume *gl_volume, const Model *model); @@ -544,26 +543,6 @@ static void draw_mouse_offset(const std::optional &offset) } #endif // SHOW_OFFSET_DURING_DRAGGING -namespace priv { -static void draw_cross_hair(const ImVec2 &position, - float radius = 16.f, - ImU32 color = ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, .75f)), - int num_segments = 0, - float thickness = 4.f); -} // namespace priv - -void priv::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, int num_segments, float thickness) -{ - auto draw_list = ImGui::GetOverlayDrawList(); - draw_list->AddCircle(position, radius, color, num_segments, thickness); - auto dirs = {ImVec2{0, 1}, ImVec2{1, 0}, ImVec2{0, -1}, ImVec2{-1, 0}}; - for (const ImVec2 &dir : dirs) { - ImVec2 start(position.x + dir.x * 0.5 * radius, position.y + dir.y * 0.5 * radius); - ImVec2 end(position.x + dir.x * 1.5 * radius, position.y + dir.y * 1.5 * radius); - draw_list->AddLine(start, end, color, thickness); - } -} - void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) { set_volume_by_selection(); @@ -610,7 +589,7 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) ImVec4(1.f, .3f, .3f, .75f) ); // Warning color const float radius = 16.f; - priv::draw_cross_hair(center, radius, color); + ImGuiWrapper::draw_cross_hair(center, radius, color); } #ifdef SHOW_FINE_POSITION @@ -3519,6 +3498,16 @@ std::string GLGizmoEmboss::get_file_name(const std::string &file_path) // priv namespace implementation /////////////// +bool priv::is_valid(ModelVolumeType volume_type) +{ + if (volume_type == ModelVolumeType::MODEL_PART || volume_type == ModelVolumeType::NEGATIVE_VOLUME || + volume_type == ModelVolumeType::PARAMETER_MODIFIER) + return true; + + BOOST_LOG_TRIVIAL(error) << "Can't create embossed volume with this type: " << (int) volume_type; + return false; +} + DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &style_manager, std::shared_ptr>& cancel) { // create volume_name diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 6057570518..55bf576028 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1514,6 +1514,17 @@ void ImGuiWrapper::draw( } } +void ImGuiWrapper::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, int num_segments, float thickness) { + auto draw_list = ImGui::GetOverlayDrawList(); + draw_list->AddCircle(position, radius, color, num_segments, thickness); + auto dirs = {ImVec2{0, 1}, ImVec2{1, 0}, ImVec2{0, -1}, ImVec2{-1, 0}}; + for (const ImVec2 &dir : dirs) { + ImVec2 start(position.x + dir.x * 0.5 * radius, position.y + dir.y * 0.5 * radius); + ImVec2 end(position.x + dir.x * 1.5 * radius, position.y + dir.y * 1.5 * radius); + draw_list->AddLine(start, end, color, thickness); + } +} + bool ImGuiWrapper::contain_all_glyphs(const ImFont *font, const std::string &text) { diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 56b5daee66..077bf568de 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -199,6 +199,20 @@ public: ImU32 color = ImGui::GetColorU32(COL_ORANGE_LIGHT), float thickness = 3.f); + /// + /// Draw symbol of cross hair + /// + /// Center of cross hair + /// Circle radius + /// Color of symbol + /// Precission of circle + /// Thickness of Line in symbol + static void draw_cross_hair(const ImVec2 &position, + float radius = 16.f, + ImU32 color = ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, .75f)), + int num_segments = 0, + float thickness = 4.f); + /// /// Check that font ranges contain all chars in string /// (rendered Unicodes are stored in GlyphRanges) From b7f4159d57b1769dfbdf0f91c16afc01b604d02c Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 16:17:09 +0100 Subject: [PATCH 44/64] extend functionality of surface drag --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 104 +----------------------- src/slic3r/GUI/SurfaceDrag.cpp | 99 +++++++++++++++++++++- src/slic3r/GUI/SurfaceDrag.hpp | 38 +++++++++ src/slic3r/Utils/RaycastManager.cpp | 22 ++--- src/slic3r/Utils/RaycastManager.hpp | 4 +- 5 files changed, 149 insertions(+), 118 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 7bf9635a4d..a0a367d938 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -345,16 +345,6 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) } namespace priv { -/// -/// Get transformation to world -/// - use fix after store to 3mf when exists -/// -/// Scene volume -/// To identify MovelVolume with fix transformation -/// -static Transform3d world_matrix(const GLVolume *gl_volume, const Model *model); -static Transform3d world_matrix(const Selection &selection); - /// /// Change position of emboss window /// @@ -363,36 +353,6 @@ static Transform3d world_matrix(const Selection &selection); static void change_window_position(std::optional &output_window_offset, bool try_to_fix); } // namespace priv -Transform3d priv::world_matrix(const GLVolume *gl_volume, const Model *model) -{ - if (!gl_volume) - return Transform3d::Identity(); - Transform3d res = gl_volume->world_matrix(); - - if (!model) - return res; - - const ModelVolume* mv = get_model_volume(*gl_volume, model->objects); - if (!mv) - return res; - - const std::optional &tc = mv->text_configuration; - if (!tc.has_value()) - return res; - - const std::optional &fix = tc->fix_3mf_tr; - if (!fix.has_value()) - return res; - - return res * fix->inverse(); -} - -Transform3d priv::world_matrix(const Selection &selection) -{ - const GLVolume *gl_volume = get_selected_gl_volume(selection); - return world_matrix(gl_volume, selection.get_model()); -} - bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { // exist selected volume? @@ -1115,17 +1075,6 @@ static inline void execute_job(std::shared_ptr j) }); } -namespace priv { -/// -/// Calculate translation of text volume onto surface of model -/// -/// Text -/// AABB trees of object. Actualize object containing text -/// Transformation of actual instance -/// Offset of volume in volume coordinate -std::optional calc_surface_offset(const ModelVolume &volume, RaycastManager &raycast_manager, const Selection &selection); -} // namespace priv - bool GLGizmoEmboss::process() { // no volume is selected -> selection from right panel @@ -1160,7 +1109,7 @@ bool GLGizmoEmboss::process() // when it is new applying of use surface than move origin onto surfaca if (!m_volume->text_configuration->style.prop.use_surface) { - auto offset = priv::calc_surface_offset(*m_volume, m_raycast_manager, m_parent.get_selection()); + auto offset = calc_surface_offset(m_parent.get_selection(), m_raycast_manager); if (offset.has_value()) text_tr *= Eigen::Translation(*offset); } @@ -1231,7 +1180,7 @@ bool priv::apply_camera_dir(const Camera &camera, GLCanvas3D &canvas) { if (sel.is_empty()) return false; // camera direction transformed into volume coordinate system - Transform3d to_world = priv::world_matrix(sel); + Transform3d to_world = world_matrix_fixed(sel); Vec3d cam_dir_tr = to_world.inverse().linear() * cam_dir; cam_dir_tr.normalize(); @@ -2949,53 +2898,6 @@ void GLGizmoEmboss::do_rotate(float relative_z_angle) m_parent.do_rotate(snapshot_name); } -std::optional priv::calc_surface_offset(const ModelVolume &volume, RaycastManager &raycast_manager, const Selection &selection) { - // Move object on surface - auto cond = RaycastManager::SkipVolume(volume.id().id); - raycast_manager.actualize(volume.get_object(), &cond); - - //const Selection &selection = m_parent.get_selection(); - const GLVolume *gl_volume = get_selected_gl_volume(selection); - Transform3d to_world = priv::world_matrix(gl_volume, selection.get_model()); - Vec3d point = to_world * Vec3d::Zero(); - Vec3d direction = to_world.linear() * (-Vec3d::UnitZ()); - - // ray in direction of text projection(from volume zero to z-dir) - std::optional hit_opt = raycast_manager.closest_hit(point, direction, &cond); - - // Try to find closest point when no hit object in emboss direction - if (!hit_opt.has_value()) { - std::optional close_point_opt = raycast_manager.closest(point); - - // It should NOT appear. Closest point always exists. - assert(close_point_opt.has_value()); - if (!close_point_opt.has_value()) - return {}; - - // It is no neccesary to move with origin by very small value - if (close_point_opt->squared_distance < EPSILON) - return {}; - - const RaycastManager::ClosePoint &close_point = *close_point_opt; - Transform3d hit_tr = raycast_manager.get_transformation(close_point.tr_key); - Vec3d hit_world = hit_tr * close_point.point; - Vec3d offset_world = hit_world - point; // vector in world - Vec3d offset_volume = to_world.inverse().linear() * offset_world; - return offset_volume; - } - - // It is no neccesary to move with origin by very small value - const RaycastManager::Hit &hit = *hit_opt; - if (hit.squared_distance < EPSILON) - return {}; - Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key); - Vec3d hit_world = hit_tr * hit.position; - Vec3d offset_world = hit_world - point; // vector in world - // TIP: It should be close to only z move - Vec3d offset_volume = to_world.inverse().linear() * offset_world; - return offset_volume; -} - void GLGizmoEmboss::draw_advanced() { const auto &ff = m_style_manager.get_font_file_with_cache(); @@ -3610,7 +3512,7 @@ bool priv::start_create_volume_on_surface_job( 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); diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index 46b6121588..33f64c0c61 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -104,11 +104,14 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, gl_volumes[hovered_idx] != gl_volume) return false; - const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects); + const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects); + assert(object != nullptr); + if (object == nullptr) + return false; + const ModelInstance *instance = get_model_instance(*gl_volume, *object); const ModelVolume *volume = get_model_volume(*gl_volume, *object); - - assert(object != nullptr && instance != nullptr && volume != nullptr); + assert(instance != nullptr && volume != nullptr); if (object == nullptr || instance == nullptr || volume == nullptr) return false; @@ -131,7 +134,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, // initialize raycasters // INFO: It could slows down for big objects // (may be move to thread and do not show drag until it finish) - raycast_manager.actualize(instance, &condition, &meshes); + raycast_manager.actualize(*instance, &condition, &meshes); // wxCoord == int --> wx/types.h Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); @@ -242,4 +245,92 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, return false; } +std::optional calc_surface_offset(const Selection &selection, RaycastManager &raycast_manager) { + const GLVolume *gl_volume_ptr = get_selected_gl_volume(selection); + if (gl_volume_ptr == nullptr) + return {}; + const GLVolume& gl_volume = *gl_volume_ptr; + + const ModelObjectPtrs &objects = selection.get_model()->objects; + const ModelVolume* volume = get_model_volume(gl_volume, objects); + if (volume == nullptr) + return {}; + + const ModelInstance* instance = get_model_instance(gl_volume, objects); + if (instance == nullptr) + return {}; + + // Move object on surface + auto cond = RaycastManager::SkipVolume(volume->id().id); + raycast_manager.actualize(*instance, &cond); + + Transform3d to_world = world_matrix_fixed(gl_volume, selection.get_model()->objects); + Vec3d point = to_world * Vec3d::Zero(); + Vec3d direction = to_world.linear() * (-Vec3d::UnitZ()); + + // ray in direction of text projection(from volume zero to z-dir) + std::optional hit_opt = raycast_manager.closest_hit(point, direction, &cond); + + // Try to find closest point when no hit object in emboss direction + if (!hit_opt.has_value()) { + std::optional close_point_opt = raycast_manager.closest(point); + + // It should NOT appear. Closest point always exists. + assert(close_point_opt.has_value()); + if (!close_point_opt.has_value()) + return {}; + + // It is no neccesary to move with origin by very small value + if (close_point_opt->squared_distance < EPSILON) + return {}; + + const RaycastManager::ClosePoint &close_point = *close_point_opt; + Transform3d hit_tr = raycast_manager.get_transformation(close_point.tr_key); + Vec3d hit_world = hit_tr * close_point.point; + Vec3d offset_world = hit_world - point; // vector in world + Vec3d offset_volume = to_world.inverse().linear() * offset_world; + return offset_volume; + } + + // It is no neccesary to move with origin by very small value + const RaycastManager::Hit &hit = *hit_opt; + if (hit.squared_distance < EPSILON) + return {}; + Transform3d hit_tr = raycast_manager.get_transformation(hit.tr_key); + Vec3d hit_world = hit_tr * hit.position; + Vec3d offset_world = hit_world - point; // vector in world + // TIP: It should be close to only z move + Vec3d offset_volume = to_world.inverse().linear() * offset_world; + return offset_volume; +} + +Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs &objects) +{ + Transform3d res = gl_volume.world_matrix(); + + const ModelVolume *mv = get_model_volume(gl_volume, objects); + if (!mv) + return res; + + const std::optional &tc = mv->text_configuration; + if (!tc.has_value()) + return res; + + const std::optional &fix = tc->fix_3mf_tr; + if (!fix.has_value()) + return res; + + return res * fix->inverse(); +} + +Transform3d world_matrix_fixed(const Selection &selection) +{ + const GLVolume *gl_volume = get_selected_gl_volume(selection); + assert(gl_volume != nullptr); + if (gl_volume == nullptr) + return Transform3d::Identity(); + + return world_matrix_fixed(*gl_volume, selection.get_model()->objects); +} + } // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/SurfaceDrag.hpp b/src/slic3r/GUI/SurfaceDrag.hpp index c3dc9cd2db..a3765f86bf 100644 --- a/src/slic3r/GUI/SurfaceDrag.hpp +++ b/src/slic3r/GUI/SurfaceDrag.hpp @@ -12,6 +12,7 @@ class GLVolume; namespace Slic3r::GUI { class GLCanvas3D; +class Selection; struct Camera; // Data for drag&drop over surface with mouse @@ -37,11 +38,48 @@ struct SurfaceDrag bool exist_hit = true; }; +/// +/// Mouse event handler, when move(drag&drop) volume over model surface +/// NOTE: Dragged volume has to be selected. And also has to be hovered on start of dragging. +/// +/// Contain type of event and mouse position +/// Actual viewport of camera +/// Structure which keep information about dragging +/// Contain gl_volumes and selection +/// AABB trees for raycast in object +/// Refresh state inside of function +/// True when event is processed otherwise false bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, const Camera &camera, std::optional &surface_drag, GLCanvas3D &canvas, RaycastManager &raycast_manager); +/// +/// Calculate translation of volume onto surface of model +/// +/// Must contain only one selected volume, Transformation of current instance +/// AABB trees of object. Actualize object +/// Offset of volume in volume coordinate +std::optional calc_surface_offset(const Selection &selection, RaycastManager &raycast_manager); + +/// +/// Get transformation to world +/// - use fix after store to 3mf when exists +/// +/// Scene volume +/// To identify Model volume with fix transformation +/// Fixed Transformation of gl_volume +Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs& objects); + +/// +/// Get transformation to world +/// - use fix after store to 3mf when exists +/// NOTE: when not one volume selected return identity +/// +/// Selected volume +/// Fixed Transformation of selected volume in selection +Transform3d world_matrix_fixed(const Selection &selection); + } // namespace Slic3r::GUI #endif // slic3r_SurfaceDrag_hpp_ \ No newline at end of file diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 18c9bb2f19..63cb580dbe 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -7,8 +7,8 @@ namespace priv { using namespace Slic3r; static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr); static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); -static RaycastManager::TrKey create_key(const ModelVolume* volume, const ModelInstance* instance){ - return std::make_pair(instance->id().id, volume->id().id); } +static RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelInstance& instance){ + return std::make_pair(instance.id().id, volume.id().id); } static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; } @@ -16,23 +16,23 @@ static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrI return is_lower_key(i1.first, i2.first); }; } -void RaycastManager::actualize(const ModelObject *object, const ISkip *skip, Meshes *meshes) +void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Meshes *meshes) { // actualize MeshRaycaster - priv::actualize(m_meshes, object->volumes, skip, meshes); + priv::actualize(m_meshes, object.volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); bool need_sort = false; // actualize transformation matrices - for (const ModelVolume *volume : object->volumes) { + for (const ModelVolume *volume : object.volumes) { if (skip != nullptr && skip->skip(volume->id().id)) continue; const Transform3d &volume_tr = volume->get_matrix(); - for (const ModelInstance *instance : object->instances) { + for (const ModelInstance *instance : object.instances) { const Transform3d &instrance_tr = instance->get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey key = priv::create_key(volume, instance); + TrKey key = priv::create_key(*volume, *instance); auto item = priv::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time @@ -56,9 +56,9 @@ void RaycastManager::actualize(const ModelObject *object, const ISkip *skip, Mes std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); } -void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip, Meshes *meshes) +void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, Meshes *meshes) { - const ModelVolumePtrs &volumes = instance->get_object()->volumes; + const ModelVolumePtrs &volumes = instance.get_object()->volumes; // actualize MeshRaycaster priv::actualize(m_meshes, volumes, skip, meshes); @@ -72,9 +72,9 @@ void RaycastManager::actualize(const ModelInstance *instance, const ISkip *skip, if (skip != nullptr && skip->skip(volume->id().id)) continue; const Transform3d &volume_tr = volume->get_matrix(); - const Transform3d &instrance_tr = instance->get_matrix(); + const Transform3d &instrance_tr = instance.get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey key = priv::create_key(volume, instance); + TrKey key = priv::create_key(*volume, instance); auto item = priv::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index a3cd3ef910..41ec82d6c8 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -86,8 +86,8 @@ public: /// Model representation /// Condifiton for skip actualization /// Speed up for already created AABBtrees - void actualize(const ModelObject *object, const ISkip *skip = nullptr, Meshes *meshes = nullptr); - void actualize(const ModelInstance *instance, const ISkip *skip = nullptr, Meshes* meshes = nullptr); + void actualize(const ModelObject &object, const ISkip *skip = nullptr, Meshes *meshes = nullptr); + void actualize(const ModelInstance &instance, const ISkip *skip = nullptr, Meshes* meshes = nullptr); class SkipVolume: public ISkip { From 4f2cf00323657d352bc1317cd67018aeac1d9a0c Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 16:35:00 +0100 Subject: [PATCH 45/64] Hide execute job --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 176 ++++++++++++------------ 1 file changed, 91 insertions(+), 85 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index a0a367d938..a323f06d86 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -56,6 +56,7 @@ #define SHOW_WX_WEIGHT_INPUT #define DRAW_PLACE_TO_ADD_TEXT // Interactive draw of window position #define ALLOW_OPEN_NEAR_VOLUME +#define EXECUTE_PROCESS_ON_MAIN_THREAD // debug execution on main thread #endif // ALLOW_DEBUG_MODE using namespace Slic3r; @@ -228,7 +229,15 @@ enum class IconState : unsigned { activable = 0, hovered /*1*/, disabled /*2*/ } // selector for icon by enum const IconManager::Icon &get_icon(const IconManager::VIcons& icons, IconType type, IconState state); // short call of Slic3r::GUI::button -bool draw_button(const IconManager::VIcons& icons, IconType type, bool disable = false); +static bool draw_button(const IconManager::VIcons& icons, IconType type, bool disable = false); + +/// +/// Apply camera direction for emboss direction +/// +/// Define view vector +/// Containe Selected Model to modify +/// True when apply change otherwise false +static bool apply_camera_dir(const Camera &camera, GLCanvas3D &canvas); } // namespace priv @@ -344,15 +353,6 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) return used; } -namespace priv { -/// -/// Change position of emboss window -/// -/// -/// When True Only move to be full visible otherwise reset position -static void change_window_position(std::optional &output_window_offset, bool try_to_fix); -} // namespace priv - bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) { // exist selected volume? @@ -604,22 +604,14 @@ namespace priv { /// Move window for edit emboss text near to embossed object /// NOTE: embossed object must be selected /// -ImVec2 calc_fine_position(const Selection &selection, const ImVec2 &windows_size, const Size &canvas_size) -{ - const Selection::IndicesList indices = selection.get_volume_idxs(); - // no selected volume - if (indices.empty()) return {}; - const GLVolume *volume = selection.get_volume(*indices.begin()); - // bad volume selected (e.g. deleted one) - if (volume == nullptr) return {}; +static ImVec2 calc_fine_position(const Selection &selection, const ImVec2 &windows_size, const Size &canvas_size); - const Camera &camera = wxGetApp().plater()->get_camera(); - Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *volume); - - ImVec2 c_size(canvas_size.get_width(), canvas_size.get_height()); - ImVec2 offset = ImGuiWrapper::suggest_location(windows_size, hull, c_size); - return offset; -} +/// +/// Change position of emboss window +/// +/// +/// When True Only move to be full visible otherwise reset position +static void change_window_position(std::optional &output_window_offset, bool try_to_fix); } // namespace priv void GLGizmoEmboss::on_set_state() @@ -1056,6 +1048,8 @@ void GLGizmoEmboss::calculate_scale() { m_style_manager.clear_imgui_font(); } +#ifdef EXECUTE_PROCESS_ON_MAIN_THREAD +namespace priv { // Run Job on main thread (blocking) - ONLY DEBUG static inline void execute_job(std::shared_ptr j) { @@ -1074,6 +1068,8 @@ static inline void execute_job(std::shared_ptr j) j->finalize(false, e_ptr); }); } +} // namespace priv +#endif bool GLGizmoEmboss::process() { @@ -1124,12 +1120,13 @@ bool GLGizmoEmboss::process() job = std::make_unique(std::move(data)); } - //* +#ifndef EXECUTE_PROCESS_ON_MAIN_THREAD auto &worker = wxGetApp().plater()->get_ui_job_worker(); queue_job(worker, std::move(job)); - /*/ // Run Job on main thread (blocking) - ONLY DEBUG - execute_job(std::move(job)); - // */ +#else + // Run Job on main thread (blocking) - ONLY DEBUG + priv::execute_job(std::move(job)); +#endif // EXECUTE_PROCESS_ON_MAIN_THREAD // notification is removed befor object is changed by job remove_notification_not_valid_font(); @@ -1162,62 +1159,6 @@ void GLGizmoEmboss::close() mng.open_gizmo(GLGizmosManager::Emboss); } -namespace priv { - -/// -/// Apply camera direction for emboss direction -/// -/// Define view vector -/// Containe Selected Model to modify -/// True when apply change otherwise false -static bool apply_camera_dir(const Camera &camera, GLCanvas3D &canvas); -} - -bool priv::apply_camera_dir(const Camera &camera, GLCanvas3D &canvas) { - const Vec3d &cam_dir = camera.get_dir_forward(); - - Selection &sel = canvas.get_selection(); - if (sel.is_empty()) return false; - - // camera direction transformed into volume coordinate system - Transform3d to_world = world_matrix_fixed(sel); - Vec3d cam_dir_tr = to_world.inverse().linear() * cam_dir; - cam_dir_tr.normalize(); - - Vec3d emboss_dir(0., 0., -1.); - - // check wether cam_dir is already used - if (is_approx(cam_dir_tr, emboss_dir)) return false; - - assert(sel.get_volume_idxs().size() == 1); - GLVolume *gl_volume = sel.get_volume(*sel.get_volume_idxs().begin()); - - Transform3d vol_rot; - Transform3d vol_tr = gl_volume->get_volume_transformation().get_matrix(); - // check whether cam_dir is opposit to emboss dir - if (is_approx(cam_dir_tr, -emboss_dir)) { - // rotate 180 DEG by y - vol_rot = Eigen::AngleAxis(M_PI_2, Vec3d(0., 1., 0.)); - } else { - // calc params for rotation - Vec3d axe = emboss_dir.cross(cam_dir_tr); - axe.normalize(); - double angle = std::acos(emboss_dir.dot(cam_dir_tr)); - vol_rot = Eigen::AngleAxis(angle, axe); - } - - Vec3d offset = vol_tr * Vec3d::Zero(); - Vec3d offset_inv = vol_rot.inverse() * offset; - Transform3d res = vol_tr * - Eigen::Translation(-offset) * - vol_rot * - Eigen::Translation(offset_inv); - //Transform3d res = vol_tr * vol_rot; - gl_volume->set_volume_transformation(Geometry::Transformation(res)); - get_model_volume(*gl_volume, sel.get_model()->objects)->set_transformation(res); - return true; -} - void GLGizmoEmboss::draw_window() { #ifdef ALLOW_DEBUG_MODE @@ -3570,6 +3511,25 @@ void priv::find_closest_volume(const Selection &selection, } } +ImVec2 priv::calc_fine_position(const Selection &selection, const ImVec2 &windows_size, const Size &canvas_size) +{ + const Selection::IndicesList indices = selection.get_volume_idxs(); + // no selected volume + if (indices.empty()) + return {}; + const GLVolume *volume = selection.get_volume(*indices.begin()); + // bad volume selected (e.g. deleted one) + if (volume == nullptr) + return {}; + + const Camera &camera = wxGetApp().plater()->get_camera(); + Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *volume); + + ImVec2 c_size(canvas_size.get_width(), canvas_size.get_height()); + ImVec2 offset = ImGuiWrapper::suggest_location(windows_size, hull, c_size); + return offset; +} + // Need internals to get window #include "imgui/imgui_internal.h" void priv::change_window_position(std::optional& output_window_offset, bool try_to_fix) { @@ -3606,5 +3566,51 @@ void priv::change_window_position(std::optional& output_window_offset, b output_window_offset = ImVec2(-1, -1); // Cannot } + +bool priv::apply_camera_dir(const Camera &camera, GLCanvas3D &canvas) { + const Vec3d &cam_dir = camera.get_dir_forward(); + + Selection &sel = canvas.get_selection(); + if (sel.is_empty()) return false; + + // camera direction transformed into volume coordinate system + Transform3d to_world = world_matrix_fixed(sel); + Vec3d cam_dir_tr = to_world.inverse().linear() * cam_dir; + cam_dir_tr.normalize(); + + Vec3d emboss_dir(0., 0., -1.); + + // check wether cam_dir is already used + if (is_approx(cam_dir_tr, emboss_dir)) return false; + + assert(sel.get_volume_idxs().size() == 1); + GLVolume *gl_volume = sel.get_volume(*sel.get_volume_idxs().begin()); + + Transform3d vol_rot; + Transform3d vol_tr = gl_volume->get_volume_transformation().get_matrix(); + // check whether cam_dir is opposit to emboss dir + if (is_approx(cam_dir_tr, -emboss_dir)) { + // rotate 180 DEG by y + vol_rot = Eigen::AngleAxis(M_PI_2, Vec3d(0., 1., 0.)); + } else { + // calc params for rotation + Vec3d axe = emboss_dir.cross(cam_dir_tr); + axe.normalize(); + double angle = std::acos(emboss_dir.dot(cam_dir_tr)); + vol_rot = Eigen::AngleAxis(angle, axe); + } + + Vec3d offset = vol_tr * Vec3d::Zero(); + Vec3d offset_inv = vol_rot.inverse() * offset; + Transform3d res = vol_tr * + Eigen::Translation(-offset) * + vol_rot * + Eigen::Translation(offset_inv); + //Transform3d res = vol_tr * vol_rot; + gl_volume->set_volume_transformation(Geometry::Transformation(res)); + get_model_volume(*gl_volume, sel.get_model()->objects)->set_transformation(res); + return true; +} + // any existing icon filename to not influence GUI const std::string GLGizmoEmboss::M_ICON_FILENAME = "cut.svg"; From ef6ed9708e6ac5ec5612fe44aac070a00ccb63da Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 16:38:28 +0100 Subject: [PATCH 46/64] hide choose SVG file --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 3 +++ src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index a323f06d86..9bcff93f42 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3198,6 +3198,8 @@ bool GLGizmoEmboss::choose_true_type_file() } #endif // ALLOW_ADD_FONT_BY_FILE + +#ifdef ALLOW_DEBUG_MODE bool GLGizmoEmboss::choose_svg_file() { wxArrayString input_files; @@ -3231,6 +3233,7 @@ bool GLGizmoEmboss::choose_svg_file() // svg.draw(polys); //return add_volume(name, its); } +#endif // ALLOW_DEBUG_MODE void GLGizmoEmboss::create_notification_not_valid_font( const TextConfiguration &tc) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index ca4d053317..26c78f8c3a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -153,10 +153,6 @@ private: bool on_mouse_for_rotation(const wxMouseEvent &mouse_event); bool on_mouse_for_translate(const wxMouseEvent &mouse_event); - bool choose_font_by_wxdialog(); - bool choose_true_type_file(); - bool choose_svg_file(); - // When open text loaded from .3mf it could be written with unknown font bool m_is_unknown_font; void create_notification_not_valid_font(const TextConfiguration& tc); From a8c580c957e7b0037c1e0f22f24e83a0d6095978 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 28 Feb 2023 17:01:52 +0100 Subject: [PATCH 47/64] Move function is_font_changed into EmbossStyleManager --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 71 ++++++------------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 3 -- src/slic3r/Utils/EmbossStyleManager.cpp | 39 ++++++++++++++ src/slic3r/Utils/EmbossStyleManager.hpp | 8 ++- 4 files changed, 62 insertions(+), 59 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 9bcff93f42..56c9737195 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2558,46 +2558,6 @@ bool GLGizmoEmboss::rev_checkbox(const std::string &name, undo_offset, draw_offseted_input); } -bool is_font_changed( - const wxFont &wx_font, const wxFont &wx_font_stored, - const FontProp &prop, const FontProp &prop_stored) -{ - // Exist change in face name? - if(wx_font_stored.GetFaceName() != wx_font.GetFaceName()) return true; - - const std::optional &skew = prop.skew; - bool is_italic = skew.has_value() || WxFontUtils::is_italic(wx_font); - const std::optional &skew_stored = prop_stored.skew; - bool is_stored_italic = skew_stored.has_value() || WxFontUtils::is_italic(wx_font_stored); - // is italic changed - if (is_italic != is_stored_italic) - return true; - - const std::optional &boldness = prop.boldness; - bool is_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font); - const std::optional &boldness_stored = prop_stored.boldness; - bool is_stored_bold = boldness_stored.has_value() || WxFontUtils::is_bold(wx_font_stored); - // is bold changed - return is_bold != is_stored_bold; -} - -bool is_font_changed(const StyleManager &mng) { - const std::optional &wx_font_opt = mng.get_wx_font(); - if (!wx_font_opt.has_value()) - return false; - if (!mng.exist_stored_style()) - return false; - const EmbossStyle *stored_style = mng.get_stored_style(); - if (stored_style == nullptr) - return false; - - const std::optional &wx_font_stored_opt = mng.get_stored_wx_font(); - if (!wx_font_stored_opt.has_value()) - return false; - - return is_font_changed(*wx_font_opt, *wx_font_stored_opt, mng.get_style().prop, stored_style->prop); -} - void GLGizmoEmboss::draw_style_edit() { const std::optional &wx_font_opt = m_style_manager.get_wx_font(); assert(wx_font_opt.has_value()); @@ -2606,7 +2566,7 @@ void GLGizmoEmboss::draw_style_edit() { return; } bool exist_stored_style = m_style_manager.exist_stored_style(); - bool exist_change_in_font = is_font_changed(m_style_manager); + bool exist_change_in_font = m_style_manager.is_font_changed(); const GuiCfg::Translations &tr = m_gui_cfg->translations; if (exist_change_in_font || !exist_stored_style) ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr.font); @@ -2714,7 +2674,6 @@ void GLGizmoEmboss::draw_height(bool use_inch) process(); } - bool GLGizmoEmboss::set_depth() { float &value = m_style_manager.get_style().prop.emboss; @@ -2739,7 +2698,6 @@ void GLGizmoEmboss::draw_depth(bool use_inch) process(); } - bool GLGizmoEmboss::rev_slider(const std::string &name, std::optional& value, const std::optional *default_value, @@ -3170,6 +3128,19 @@ bool GLGizmoEmboss::choose_font_by_wxdialog() } #endif // ALLOW_ADD_FONT_BY_OS_SELECTOR +#if defined ALLOW_ADD_FONT_BY_FILE or defined ALLOW_DEBUG_MODE +namespace priv { +static std::string get_file_name(const std::string &file_path) +{ + size_t pos_last_delimiter = file_path.find_last_of("/\\"); + size_t pos_point = file_path.find_last_of('.'); + size_t offset = pos_last_delimiter + 1; + size_t count = pos_point - pos_last_delimiter - 1; + return file_path.substr(offset, count); +} +} // namespace priv +#endif // ALLOW_ADD_FONT_BY_FILE || ALLOW_DEBUG_MODE + #ifdef ALLOW_ADD_FONT_BY_FILE bool GLGizmoEmboss::choose_true_type_file() { @@ -3185,7 +3156,7 @@ bool GLGizmoEmboss::choose_true_type_file() // use first valid font for (auto &input_file : input_files) { std::string path = std::string(input_file.c_str()); - std::string name = get_file_name(path); + std::string name = priv::get_file_name(path); //make_unique_name(name, m_font_list); const FontProp& prop = m_style_manager.get_font_prop(); EmbossStyle style{ name, path, EmbossStyle::Type::file_path, prop }; @@ -3198,7 +3169,6 @@ bool GLGizmoEmboss::choose_true_type_file() } #endif // ALLOW_ADD_FONT_BY_FILE - #ifdef ALLOW_DEBUG_MODE bool GLGizmoEmboss::choose_svg_file() { @@ -3213,7 +3183,7 @@ bool GLGizmoEmboss::choose_svg_file() if (input_files.size() != 1) return false; auto & input_file = input_files.front(); std::string path = std::string(input_file.c_str()); - std::string name = get_file_name(path); + std::string name = priv::get_file_name(path); NSVGimage *image = nsvgParseFromFile(path.c_str(), "mm", 96.0f); ExPolygons polys = NSVGUtils::to_ExPolygons(image); @@ -3331,15 +3301,6 @@ bool GLGizmoEmboss::is_text_object(const ModelVolume *text) { return true; } -std::string GLGizmoEmboss::get_file_name(const std::string &file_path) -{ - size_t pos_last_delimiter = file_path.find_last_of("/\\"); - size_t pos_point = file_path.find_last_of('.'); - size_t offset = pos_last_delimiter + 1; - size_t count = pos_point - pos_last_delimiter - 1; - return file_path.substr(offset, count); -} - ///////////// // priv namespace implementation /////////////// diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 26c78f8c3a..5ee2d396aa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -335,9 +335,6 @@ public: /// Model volume of Text /// True when object otherwise False static bool is_text_object(const ModelVolume *text); - - // TODO: move to file utils - static std::string get_file_name(const std::string &file_path); }; } // namespace Slic3r::GUI diff --git a/src/slic3r/Utils/EmbossStyleManager.cpp b/src/slic3r/Utils/EmbossStyleManager.cpp index af1aae669f..0640dc1860 100644 --- a/src/slic3r/Utils/EmbossStyleManager.cpp +++ b/src/slic3r/Utils/EmbossStyleManager.cpp @@ -228,6 +228,45 @@ bool StyleManager::load_style(const EmbossStyle &style, const wxFont &font) return true; } +bool StyleManager::is_font_changed() const +{ + const std::optional &wx_font_opt = get_wx_font(); + if (!wx_font_opt.has_value()) + return false; + if (!exist_stored_style()) + return false; + const EmbossStyle *stored_style = get_stored_style(); + if (stored_style == nullptr) + return false; + + const std::optional &wx_font_stored_opt = get_stored_wx_font(); + if (!wx_font_stored_opt.has_value()) + return false; + + const wxFont &wx_font = *wx_font_opt; + const wxFont &wx_font_stored = *wx_font_stored_opt; + const FontProp &prop = get_style().prop; + const FontProp &prop_stored = stored_style->prop; + + // Exist change in face name? + if(wx_font_stored.GetFaceName() != wx_font.GetFaceName()) return true; + + const std::optional &skew = prop.skew; + bool is_italic = skew.has_value() || WxFontUtils::is_italic(wx_font); + const std::optional &skew_stored = prop_stored.skew; + bool is_stored_italic = skew_stored.has_value() || WxFontUtils::is_italic(wx_font_stored); + // is italic changed + if (is_italic != is_stored_italic) + return true; + + const std::optional &boldness = prop.boldness; + bool is_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font); + const std::optional &boldness_stored = prop_stored.boldness; + bool is_stored_bold = boldness_stored.has_value() || WxFontUtils::is_bold(wx_font_stored); + // is bold changed + return is_bold != is_stored_bold; +} + bool StyleManager::is_active_font() { return m_style_cache.font_file.has_value(); } const EmbossStyle* StyleManager::get_stored_style() const diff --git a/src/slic3r/Utils/EmbossStyleManager.hpp b/src/slic3r/Utils/EmbossStyleManager.hpp index 08fa72642b..1fd920be33 100644 --- a/src/slic3r/Utils/EmbossStyleManager.hpp +++ b/src/slic3r/Utils/EmbossStyleManager.hpp @@ -124,7 +124,13 @@ public: // True when activ style has same name as some of stored style bool exist_stored_style() const { return m_style_cache.style_index != std::numeric_limits::max(); } - + + /// + /// check whether current style differ to selected + /// + /// + bool is_font_changed() const; + /// /// Setter on wx_font when changed /// From bc437661dcadb48d1170889304a1ceae799a7d26 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Mar 2023 12:52:06 +0100 Subject: [PATCH 48/64] Add log for creation of font descriptor --- src/slic3r/Utils/WxFontUtils.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index 4156bb9983..f209a1f7c9 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -169,6 +169,13 @@ std::string WxFontUtils::store_wxFont(const wxFont &font) { // wxString os = wxPlatformInfo::Get().GetOperatingSystemIdName(); wxString font_descriptor = font.GetNativeFontInfoDesc(); + BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor << "' wx string get from GetNativeFontInfoDesc. wxFont " << + "IsOk(" << font.IsOk() << "), " << + "isNull(" << font.IsNull() << ")" << + "IsFree(" << font.IsFree() << "), " << + "IsFixedWidth(" << font.IsFixedWidth() << "), " << + "IsUsingSizeInPixels(" << font.IsUsingSizeInPixels() << "), " << + "Encoding(" << (int)font.GetEncoding() << "), " ; return std::string(font_descriptor.c_str()); } From 4c3ac0d9774e7108f43afbf16d10681f6a068fd3 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 1 Mar 2023 15:05:38 +0100 Subject: [PATCH 49/64] Disable setting font pixel size into wxFont Do not keep wxFont inside of optional --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 63 ++++++++++++++----------- src/slic3r/Utils/EmbossStyleManager.cpp | 12 ++--- src/slic3r/Utils/EmbossStyleManager.hpp | 8 ++-- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 56c9737195..9a85eacb37 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -59,6 +59,8 @@ #define EXECUTE_PROCESS_ON_MAIN_THREAD // debug execution on main thread #endif // ALLOW_DEBUG_MODE +//#define USE_PIXEL_SIZE_IN_WX_FONT + using namespace Slic3r; using namespace Slic3r::Emboss; using namespace Slic3r::GUI; @@ -1694,11 +1696,13 @@ bool GLGizmoEmboss::select_facename(const wxString &facename) if (!wxFontEnumerator::IsValidFacename(facename)) return false; // Select font const wxFontEncoding &encoding = m_face_names.encoding; - wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(encoding)); + wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(encoding)); if (!wx_font.IsOk()) return false; +#ifdef USE_PIXEL_SIZE_IN_WX_FONT // wx font could change source file by size of font int point_size = static_cast(m_style_manager.get_font_prop().size_in_mm); wx_font.SetPointSize(point_size); +#endif // USE_PIXEL_SIZE_IN_WX_FONT if (!m_style_manager.set_wx_font(wx_font)) return false; process(); return true; @@ -1709,9 +1713,9 @@ void GLGizmoEmboss::draw_font_list() // Set partial wxString actual_face_name; if (m_style_manager.is_active_font()) { - const std::optional &wx_font_opt = m_style_manager.get_wx_font(); - if (wx_font_opt.has_value()) - actual_face_name = wx_font_opt->GetFaceName(); + const wxFont &wx_font = m_style_manager.get_wx_font(); + if (wx_font.IsOk()) + actual_face_name = wx_font.GetFaceName(); } // name of actual selected font const char * selected = (!actual_face_name.empty()) ? @@ -2347,13 +2351,12 @@ void GLGizmoEmboss::draw_style_list() { bool GLGizmoEmboss::draw_italic_button() { - const std::optional &wx_font_opt = m_style_manager.get_wx_font(); + const wxFont &wx_font = m_style_manager.get_wx_font(); const auto& ff = m_style_manager.get_font_file_with_cache(); - if (!wx_font_opt.has_value() || !ff.has_value()) { + if (!wx_font.IsOk() || !ff.has_value()) { draw(*m_icons[(int) IconType::italic][(int)IconState::disabled]); return false; } - const wxFont& wx_font = *wx_font_opt; std::optional &skew = m_style_manager.get_font_prop().skew; bool is_font_italic = skew.has_value() || WxFontUtils::is_italic(wx_font); @@ -2394,13 +2397,12 @@ bool GLGizmoEmboss::draw_italic_button() } bool GLGizmoEmboss::draw_bold_button() { - const std::optional &wx_font_opt = m_style_manager.get_wx_font(); + const wxFont &wx_font = m_style_manager.get_wx_font(); const auto& ff = m_style_manager.get_font_file_with_cache(); - if (!wx_font_opt.has_value() || !ff.has_value()) { + if (!wx_font.IsOk() || !ff.has_value()) { draw(get_icon(m_icons, IconType::bold, IconState::disabled)); return false; } - const wxFont &wx_font = *wx_font_opt; std::optional &boldness = m_style_manager.get_font_prop().boldness; bool is_font_bold = boldness.has_value() || WxFontUtils::is_bold(wx_font); @@ -2558,13 +2560,18 @@ bool GLGizmoEmboss::rev_checkbox(const std::string &name, undo_offset, draw_offseted_input); } -void GLGizmoEmboss::draw_style_edit() { - const std::optional &wx_font_opt = m_style_manager.get_wx_font(); - assert(wx_font_opt.has_value()); - if (!wx_font_opt.has_value()) { - ImGui::TextColored(ImGuiWrapper::COL_ORANGE_DARK, "%s", _u8L("WxFont is not loaded properly.").c_str()); - return; +void GLGizmoEmboss::draw_style_edit() +{ + { + // Check correct WxFont + const wxFont &wx_font = m_style_manager.get_wx_font(); + assert(wx_font.IsOk()); + if (!wx_font.IsOk()) { + ImGui::TextColored(ImGuiWrapper::COL_ORANGE_DARK, "%s", _u8L("WxFont is not loaded properly.").c_str()); + return; + } } + bool exist_stored_style = m_style_manager.exist_stored_style(); bool exist_change_in_font = m_style_manager.is_font_changed(); const GuiCfg::Translations &tr = m_gui_cfg->translations; @@ -2651,13 +2658,15 @@ bool GLGizmoEmboss::set_height() { if (is_approx(value, m_volume->text_configuration->style.prop.size_in_mm)) return false; +#ifdef USE_PIXEL_SIZE_IN_WX_FONT // store font size into path serialization - const std::optional &wx_font_opt = m_style_manager.get_wx_font(); - if (wx_font_opt.has_value()) { - wxFont wx_font = *wx_font_opt; - wx_font.SetPointSize(static_cast(value)); - m_style_manager.set_wx_font(wx_font); + const wxFont &wx_font = m_style_manager.get_wx_font(); + if (wx_font.IsOk()) { + wxFont wx_font_new = wx_font; // copy + wx_font_new.SetPointSize(static_cast(value)); + m_style_manager.set_wx_font(wx_font_new); } +#endif return true; } @@ -3226,9 +3235,9 @@ void GLGizmoEmboss::create_notification_not_valid_font( std::string face_name_by_wx; if (!face_name_opt.has_value()) { - const auto& wx_font = m_style_manager.get_wx_font(); - if (wx_font.has_value()) { - wxString wx_face_name = wx_font->GetFaceName(); + const wxFont& wx_font = m_style_manager.get_wx_font(); + if (wx_font.IsOk()) { + wxString wx_face_name = wx_font.GetFaceName(); face_name_by_wx = std::string((const char *) wx_face_name.ToUTF8()); } } @@ -3331,10 +3340,8 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &st const EmbossStyle &es = style_manager.get_style(); // actualize font path - during changes in gui it could be corrupted // volume must store valid path - assert(style_manager.get_wx_font().has_value()); - assert(style_manager.get_wx_font()->IsOk()); - assert(es.path.compare(WxFontUtils::store_wxFont(*style_manager.get_wx_font())) == 0); - // style.path = WxFontUtils::store_wxFont(*m_style_manager.get_wx_font()); + assert(style_manager.get_wx_font().IsOk()); + assert(es.path.compare(WxFontUtils::store_wxFont(style_manager.get_wx_font())) == 0); TextConfiguration tc{es, text}; // Cancel previous Job, when it is in process diff --git a/src/slic3r/Utils/EmbossStyleManager.cpp b/src/slic3r/Utils/EmbossStyleManager.cpp index 0640dc1860..100a532b89 100644 --- a/src/slic3r/Utils/EmbossStyleManager.cpp +++ b/src/slic3r/Utils/EmbossStyleManager.cpp @@ -230,8 +230,8 @@ bool StyleManager::load_style(const EmbossStyle &style, const wxFont &font) bool StyleManager::is_font_changed() const { - const std::optional &wx_font_opt = get_wx_font(); - if (!wx_font_opt.has_value()) + const wxFont &wx_font = get_wx_font(); + if (!wx_font.IsOk()) return false; if (!exist_stored_style()) return false; @@ -239,12 +239,10 @@ bool StyleManager::is_font_changed() const if (stored_style == nullptr) return false; - const std::optional &wx_font_stored_opt = get_stored_wx_font(); - if (!wx_font_stored_opt.has_value()) + const wxFont &wx_font_stored = get_stored_wx_font(); + if (!wx_font_stored.IsOk()) return false; - const wxFont &wx_font = *wx_font_opt; - const wxFont &wx_font_stored = *wx_font_stored_opt; const FontProp &prop = get_style().prop; const FontProp &prop_stored = stored_style->prop; @@ -533,7 +531,7 @@ bool StyleManager::set_wx_font(const wxFont &wx_font) { bool StyleManager::set_wx_font(const wxFont &wx_font, std::unique_ptr font_file) { if (font_file == nullptr) return false; - m_style_cache.wx_font = wx_font; // copy + m_style_cache.wx_font = wx_font; // copy m_style_cache.font_file = FontFileWithCache(std::move(font_file)); diff --git a/src/slic3r/Utils/EmbossStyleManager.hpp b/src/slic3r/Utils/EmbossStyleManager.hpp index 1fd920be33..dd6b9ca129 100644 --- a/src/slic3r/Utils/EmbossStyleManager.hpp +++ b/src/slic3r/Utils/EmbossStyleManager.hpp @@ -116,8 +116,8 @@ public: const ImFontAtlas &get_atlas() const { return m_style_cache.atlas; } const FontProp &get_font_prop() const { return get_style().prop; } FontProp &get_font_prop() { return get_style().prop; } - const std::optional &get_wx_font() const { return m_style_cache.wx_font; } - const std::optional &get_stored_wx_font() const { return m_style_cache.stored_wx_font; } + const wxFont &get_wx_font() const { return m_style_cache.wx_font; } + const wxFont &get_stored_wx_font() const { return m_style_cache.stored_wx_font; } Slic3r::Emboss::FontFileWithCache &get_font_file_with_cache() { return m_style_cache.font_file; } bool has_collections() const { return m_style_cache.font_file.font_file != nullptr && m_style_cache.font_file.font_file->infos.size() > 1; } @@ -227,7 +227,7 @@ private: ImFontAtlas atlas = {}; // wx widget font - std::optional wx_font = {}; + wxFont wx_font = {}; // cache for view font name with maximal width in imgui std::string truncated_name; @@ -236,7 +236,7 @@ private: EmbossStyle style = {}; // cache for stored wx font to not create every frame - std::optional stored_wx_font; + wxFont stored_wx_font = {}; // index into m_style_items size_t style_index = std::numeric_limits::max(); From e428dcc57e23cfb3dacce63990a1374d45f7be03 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Mar 2023 07:49:28 +0100 Subject: [PATCH 50/64] fix: ../src/slic3r/Utils/WxFontUtils.cpp:175:27: error: no member named 'IsFree' in 'wxFont' "IsFree(" << font.IsFree() << "), " << ~~~~ ^ 1 error generated. --- src/slic3r/Utils/WxFontUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp index f209a1f7c9..1c191606d3 100644 --- a/src/slic3r/Utils/WxFontUtils.cpp +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -172,7 +172,7 @@ std::string WxFontUtils::store_wxFont(const wxFont &font) BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor << "' wx string get from GetNativeFontInfoDesc. wxFont " << "IsOk(" << font.IsOk() << "), " << "isNull(" << font.IsNull() << ")" << - "IsFree(" << font.IsFree() << "), " << + // "IsFree(" << font.IsFree() << "), " << // on MacOs is no function is free "IsFixedWidth(" << font.IsFixedWidth() << "), " << "IsUsingSizeInPixels(" << font.IsUsingSizeInPixels() << "), " << "Encoding(" << (int)font.GetEncoding() << "), " ; From 191f670dbd9842059fa83ca72301e253a5a573e5 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Mar 2023 14:25:06 +0100 Subject: [PATCH 51/64] Volume do not store angle of text any more. (it is stored inside of volume transformation and si calculated on the fly) --- src/libslic3r/Emboss.cpp | 18 ++- src/libslic3r/Emboss.hpp | 1 + src/libslic3r/TextConfiguration.hpp | 8 +- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 191 ++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 16 +- src/slic3r/GUI/Jobs/EmbossJob.cpp | 7 + src/slic3r/GUI/Selection.cpp | 6 +- src/slic3r/GUI/SurfaceDrag.cpp | 16 +- src/slic3r/GUI/SurfaceDrag.hpp | 7 +- 9 files changed, 167 insertions(+), 103 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 0005077795..65aa5a333b 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -1264,15 +1264,17 @@ ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache, return result; } -void Emboss::apply_transformation(const FontProp &font_prop, - Transform3d &transformation) -{ - if (font_prop.angle.has_value()) { - double angle_z = *font_prop.angle; +void Emboss::apply_transformation(const FontProp &font_prop, Transform3d &transformation){ + apply_transformation(font_prop.angle, font_prop.distance, transformation); +} + +void Emboss::apply_transformation(const std::optional& angle, const std::optional& distance, Transform3d &transformation) { + if (angle.has_value()) { + double angle_z = *angle; transformation *= Eigen::AngleAxisd(angle_z, Vec3d::UnitZ()); } - if (font_prop.distance.has_value()) { - Vec3d translate = Vec3d::UnitZ() * (*font_prop.distance); + if (distance.has_value()) { + Vec3d translate = Vec3d::UnitZ() * (*distance); transformation.translate(translate); } } @@ -1583,7 +1585,7 @@ std::optional Emboss::calc_up(const Transform3d &tr, double up_limit) m.row(2) = normal; double det = m.determinant(); - return atan2(det, dot); + return -atan2(det, dot); } Transform3d Emboss::create_transformation_onto_surface(const Vec3d &position, diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index d1ddbb1c37..fc0f0a0a3c 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -193,6 +193,7 @@ namespace Emboss /// Z-rotation as angle to Y axis(FontProp::angle) /// In / Out transformation to modify by property void apply_transformation(const FontProp &font_prop, Transform3d &transformation); + void apply_transformation(const std::optional &angle, const std::optional &distance, Transform3d &transformation); /// /// Read information from naming table of font file diff --git a/src/libslic3r/TextConfiguration.hpp b/src/libslic3r/TextConfiguration.hpp index f303b17e50..1c1ce77567 100644 --- a/src/libslic3r/TextConfiguration.hpp +++ b/src/libslic3r/TextConfiguration.hpp @@ -49,10 +49,12 @@ struct FontProp // used for move over model surface // When not set value is zero and is not stored std::optional distance; // [in mm] - - // change up vector direction of font + + // Angle of rotation around emboss direction (Z axis) + // It is calculate on the fly from volume world transformation + // only StyleManager keep actual value for comparision with style // When not set value is zero and is not stored - std::optional angle; // [in radians] + std::optional angle; // [in radians] form -Pi to Pi // Parameter for True Type Font collections // Select index of font in collection diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 9a85eacb37..c9b493b98e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -325,9 +325,13 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) if (!m_dragging) return used; if (mouse_event.Dragging()) { - auto &angle_opt = m_volume->text_configuration->style.prop.angle; - if (!m_rotate_start_angle.has_value()) - m_rotate_start_angle = angle_opt.has_value() ? *angle_opt : 0.f; + if (!m_rotate_start_angle.has_value()) { + // when m_rotate_start_angle is not set mean it is not Dragging + // when angle_opt is not set mean angle is Zero + const std::optional &angle_opt = m_style_manager.get_font_prop().angle; + m_rotate_start_angle = angle_opt.has_value() ? *angle_opt : 0.f; + } + double angle = m_rotate_gizmo.get_angle(); angle -= PI / 2; // Grabber is upward @@ -339,18 +343,15 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) angle += *m_rotate_start_angle; // move to range <-M_PI, M_PI> priv::to_range_pi_pi(angle); - // propagate angle into property - angle_opt = static_cast(angle); - - // do not store zero - if (is_approx(*angle_opt, 0.f)) - angle_opt.reset(); // set into activ style assert(m_style_manager.is_active_font()); - if (m_style_manager.is_active_font()) + if (m_style_manager.is_active_font()) { + std::optional angle_opt; + if (!is_approx(angle, 0.)) + angle_opt = angle; m_style_manager.get_font_prop().angle = angle_opt; - + } } return used; } @@ -361,9 +362,11 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (m_volume == nullptr) return false; + std::optional up_limit; + if (m_keep_up) up_limit = priv::up_limit; const Camera &camera = wxGetApp().plater()->get_camera(); bool was_dragging = m_surface_drag.has_value(); - bool res = on_mouse_surface_drag(mouse_event, camera, m_surface_drag, m_parent, m_raycast_manager); + bool res = on_mouse_surface_drag(mouse_event, camera, m_surface_drag, m_parent, m_raycast_manager, up_limit); bool is_dragging = m_surface_drag.has_value(); // End with surface dragging? @@ -387,6 +390,17 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) else if (was_dragging && is_dragging) { // update scale of selected volume --> should be approx the same calculate_scale(); + + // Recalculate angle for GUI + if (!m_keep_up) { + const GLVolume *gl_volume = get_selected_gl_volume(m_parent.get_selection()); + assert(gl_volume != nullptr); + assert(m_style_manager.is_active_font()); + if (gl_volume == nullptr || !m_style_manager.is_active_font()) + return res; + + m_style_manager.get_style().prop.angle = calc_up(gl_volume->world_matrix(), priv::up_limit); + } } return res; } @@ -693,6 +707,13 @@ void GLGizmoEmboss::on_stop_dragging() // apply rotation m_parent.do_rotate(L("Text-Rotate")); + // Re-Calculate current angle of up vector + const GLVolume *gl_volume = get_selected_gl_volume(m_parent.get_selection()); + assert(m_style_manager.is_active_font()); + assert(gl_volume != nullptr); + 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); + m_rotate_start_angle.reset(); // recalculate for surface cut @@ -723,32 +744,37 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() int count_letter_M_in_input = 12; cfg.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.height = _u8L("Height"); + tr.depth = _u8L("Depth"); + float max_text_width = std::max({ ImGui::CalcTextSize(tr.font.c_str()).x, - ImGui::CalcTextSize(tr.size.c_str()).x, + ImGui::CalcTextSize(tr.height.c_str()).x, ImGui::CalcTextSize(tr.depth.c_str()).x}); cfg.indent = static_cast(cfg.icon_width); cfg.input_offset = style.WindowPadding.x + cfg.indent + max_text_width + space; - tr.use_surface = _u8L("Use surface"); - tr.char_gap = _u8L("Char gap"); - tr.line_gap = _u8L("Line gap"); - tr.boldness = _u8L("Boldness"); - tr.italic = _u8L("Skew ratio"); - tr.surface_distance = _u8L("Z-move"); - tr.angle = _u8L("Z-rot"); - tr.collection = _u8L("Collection"); + tr.use_surface = _u8L("Use surface"); + tr.char_gap = _u8L("Char gap"); + tr.line_gap = _u8L("Line gap"); + tr.boldness = _u8L("Boldness"); + tr.skew_ration = _u8L("Skew ratio"); + tr.from_surface = _u8L("From surface"); + tr.rotation = _u8L("Rotation"); + tr.keep_up = _u8L("Keep Up"); + tr.collection = _u8L("Collection"); + float max_advanced_text_width = std::max({ ImGui::CalcTextSize(tr.use_surface.c_str()).x, ImGui::CalcTextSize(tr.char_gap.c_str()).x, ImGui::CalcTextSize(tr.line_gap.c_str()).x, ImGui::CalcTextSize(tr.boldness.c_str()).x, - ImGui::CalcTextSize(tr.italic.c_str()).x, - ImGui::CalcTextSize(tr.surface_distance.c_str()).x, - ImGui::CalcTextSize(tr.angle.c_str()).x, + ImGui::CalcTextSize(tr.skew_ration.c_str()).x, + ImGui::CalcTextSize(tr.from_surface.c_str()).x, + ImGui::CalcTextSize(tr.rotation.c_str()).x, + ImGui::CalcTextSize(tr.keep_up.c_str()).x, ImGui::CalcTextSize(tr.collection.c_str()).x }); cfg.advanced_input_offset = max_advanced_text_width + 3 * space + cfg.indent; @@ -774,9 +800,9 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() + 2 * (cfg.icon_width + space); cfg.minimal_window_size = ImVec2(window_width, window_height); - // 6 = charGap, LineGap, Bold, italic, surfDist, angle + // 9 = useSurface, charGap, lineGap, bold, italic, surfDist, rotation, keepUp, textFaceToCamera // 4 = 1px for fix each edit image of drag float - float advance_height = input_height * 8 + 8; + float advance_height = input_height * 9 + 8; cfg.minimal_window_size_with_advance = ImVec2(cfg.minimal_window_size.x, cfg.minimal_window_size.y + advance_height); @@ -867,9 +893,17 @@ void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); } void GLGizmoEmboss::set_volume_by_selection() { const Selection &selection = m_parent.get_selection(); - ModelVolume *vol = get_selected_volume(selection); - // is same volume selected? - if (vol != nullptr && vol->id() == m_volume_id) + const GLVolume *gl_volume = get_selected_gl_volume(selection); + if (gl_volume == nullptr) + return reset_volume(); + + const ModelObjectPtrs &objects = selection.get_model()->objects; + ModelVolume *volume =get_model_volume(*gl_volume, objects); + if (volume == nullptr) + return reset_volume(); + + // is same volume as actual selected? + if (volume->id() == m_volume_id) return; // for changed volume notification is NOT valid @@ -877,30 +911,14 @@ void GLGizmoEmboss::set_volume_by_selection() // Do not use focused input value when switch volume(it must swith value) if (m_volume != nullptr && - m_volume != vol) // when update volume it changed id BUT not pointer + m_volume != volume) // when update volume it changed id BUT not pointer ImGuiWrapper::left_inputs(); - if (vol == nullptr) { - reset_volume(); - return; - } + // Is selected volume text volume? + const std::optional& tc_opt = volume->text_configuration; + if (!tc_opt.has_value()) + return reset_volume(); - // is select embossed volume? - set_volume(vol); - - // Check if user changed up vector by rotation or scale out of emboss gizmo - if (m_volume != nullptr) { - Transform3d world = selection.get_first_volume()->world_matrix(); - std::optional angle = calc_up(world, priv::up_limit); - m_volume->text_configuration->style.prop.angle = angle; - } -} - -bool GLGizmoEmboss::set_volume(ModelVolume *volume) -{ - assert(volume != nullptr); - const std::optional tc_opt = volume->text_configuration; - if (!tc_opt.has_value()) return false; const TextConfiguration &tc = *tc_opt; const EmbossStyle &style = tc.style; @@ -992,7 +1010,7 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) // The change of volume could show or hide part with setter on volume type if (m_volume == nullptr || - get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr || + get_model_volume(m_volume_id, objects) == nullptr || (m_volume->get_object()->volumes.size() == 1) != (volume->get_object()->volumes.size() == 1)){ m_should_set_minimal_windows_size = true; @@ -1008,9 +1026,13 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) m_volume = volume; m_volume_id = volume->id(); + // Calculate current angle of up vector + assert(m_style_manager.is_active_font()); + if (m_style_manager.is_active_font()) + m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), priv::up_limit); + // calculate scale for height and depth inside of scaled object instance - calculate_scale(); - return true; + calculate_scale(); } void GLGizmoEmboss::reset_volume() @@ -1020,9 +1042,9 @@ void GLGizmoEmboss::reset_volume() m_volume = nullptr; m_volume_id.id = 0; - // TODO: check if it is neccessary to set default text - // Idea is to set default text when create object - set_default_text(); + + // No more need of current notification + remove_notification_not_valid_font(); } void GLGizmoEmboss::calculate_scale() { @@ -2677,7 +2699,7 @@ void GLGizmoEmboss::draw_height(bool use_inch) const float *stored = ((stored_style)? &stored_style->prop.size_in_mm : nullptr); const char *size_format = ((use_inch) ? "%.2f in" : "%.1f mm"); const std::string revert_text_size = _u8L("Revert text size."); - const std::string& name = m_gui_cfg->translations.size; + const std::string& name = m_gui_cfg->translations.height; if (rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, m_scale_height)) if (set_height()) process(); @@ -2915,7 +2937,7 @@ void GLGizmoEmboss::draw_advanced() // input italic auto def_skew = stored_style ? &stored_style->prop.skew : nullptr; - if (rev_slider(tr.italic, font_prop.skew, def_skew, _u8L("Undo letter's skew"), + if (rev_slider(tr.skew_ration, font_prop.skew, def_skew, _u8L("Undo letter's skew"), priv::limits.skew.gui.min, priv::limits.skew.gui.max, "%.2f", _L("Italic strength ratio"))){ if (!priv::Limits::apply(font_prop.skew, priv::limits.skew.values) || !m_volume->text_configuration->style.prop.skew.has_value() || @@ -2949,7 +2971,7 @@ void GLGizmoEmboss::draw_advanced() } min_distance *= ObjectManipulation::mm_to_in; max_distance *= ObjectManipulation::mm_to_in; - if (rev_slider(tr.surface_distance, distance_inch, def_distance, undo_move_tooltip, min_distance, max_distance, "%.3f in", move_tooltip)) { + if (rev_slider(tr.from_surface, distance_inch, def_distance, undo_move_tooltip, min_distance, max_distance, "%.3f in", move_tooltip)) { if (distance_inch.has_value()) { font_prop.distance = *distance_inch * ObjectManipulation::in_to_mm; } else { @@ -2958,7 +2980,7 @@ void GLGizmoEmboss::draw_advanced() is_moved = true; } } else { - if (rev_slider(tr.surface_distance, distance, def_distance, undo_move_tooltip, + if (rev_slider(tr.from_surface, distance, def_distance, undo_move_tooltip, min_distance, max_distance, "%.2f mm", move_tooltip)) is_moved = true; } @@ -2971,33 +2993,50 @@ void GLGizmoEmboss::draw_advanced() // slider for Clock-wise angle in degress // stored angle is optional CCW and in radians - std::optional &angle = font_prop.angle; - float prev_angle = angle.has_value() ? *angle : .0f; // Convert stored value to degress // minus create clock-wise roation from CCW - float angle_deg = angle.has_value() ? - static_cast(-(*angle) * 180 / M_PI) : .0f; + const std::optional &angle_opt = m_style_manager.get_font_prop().angle; + float angle = angle_opt.has_value() ? *angle_opt: 0.f; + float angle_deg = static_cast(-angle * 180 / M_PI); float def_angle_deg_val = (!stored_style || !stored_style->prop.angle.has_value()) ? 0.f : (*stored_style->prop.angle * -180 / M_PI); float* def_angle_deg = stored_style ? &def_angle_deg_val : nullptr; - if (rev_slider(tr.angle, angle_deg, def_angle_deg, _u8L("Undo rotation"), + if (rev_slider(tr.rotation, angle_deg, def_angle_deg, _u8L("Undo rotation"), priv::limits.angle.min, priv::limits.angle.max, u8"%.2f °", _L("Rotate text Clock-wise."))) { // convert back to radians and CCW - angle = -angle_deg * M_PI / 180.0; - priv::to_range_pi_pi(*angle); - if (is_approx(*angle, 0.f)) - angle.reset(); + float angle_rad = static_cast(-angle_deg * M_PI / 180.0); + priv::to_range_pi_pi(angle_rad); + + + float diff_angle = angle_rad - angle; + do_rotate(diff_angle); + + // calc angle after rotation + const GLVolume *gl_volume = get_selected_gl_volume(m_parent.get_selection()); + assert(gl_volume != nullptr); + assert(m_style_manager.is_active_font()); + 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); - m_volume->text_configuration->style.prop.angle = angle; - float act_angle = angle.has_value() ? *angle : .0f; - do_rotate(act_angle - prev_angle); // recalculate for surface cut - if (font_prop.use_surface) process(); + if (font_prop.use_surface) + process(); } + ImGui::Text("%s", tr.keep_up.c_str()); + ImGui::SameLine(m_gui_cfg->advanced_input_offset); + if (ImGui::Checkbox("##keep_up", &m_keep_up)) { + if (m_keep_up) { + // copy angle to volume + m_volume->text_configuration->style.prop.angle = font_prop.angle; + } + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", _u8L("Keep text orientation during surface dragging.\nNot stable between horizontal and vertical alignment.").c_str()); + // when more collection add selector if (ff.font_file->infos.size() > 1) { ImGui::Text("%s", tr.collection.c_str()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 5ee2d396aa..7e7c2aa161 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -83,8 +83,6 @@ private: void set_default_text(); void set_volume_by_selection(); - // load text configuration from volume into gizmo - bool set_volume(ModelVolume *volume); void reset_volume(); // create volume from text - main functionality @@ -198,7 +196,7 @@ private: struct Translations { std::string font; - std::string size; + std::string height; std::string depth; std::string use_surface; @@ -206,9 +204,10 @@ private: std::string char_gap; std::string line_gap; std::string boldness; - std::string italic; - std::string surface_distance; - std::string angle; + std::string skew_ration; + std::string from_surface; + std::string rotation; + std::string keep_up; std::string collection; }; Translations translations; @@ -286,7 +285,10 @@ private: static void init_truncated_names(Facenames &face_names, float max_width); // Text to emboss - std::string m_text; + std::string m_text; // Sequence of Unicode UTF8 symbols + + // When true keep up vector otherwise relative rotation + bool m_keep_up = true; // current selected volume // NOTE: Be carefull could be uninitialized (removed from Model) diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index bc80285ea0..f5c315b30e 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -502,6 +502,9 @@ void UpdateJob::update_volume(ModelVolume *volume, volume->calculate_convex_hull(); volume->get_object()->invalidate_bounding_box(); volume->text_configuration = text_configuration; + + // discard information about rotation, should not be stored in volume + volume->text_configuration->style.prop.angle.reset(); GUI_App &app = wxGetApp(); // may be move to input GLCanvas3D *canvas = app.plater()->canvas3D(); @@ -615,6 +618,10 @@ void priv::create_volume( volume->name = data.volume_name; // copy volume->text_configuration = data.text_configuration; // copy + + // discard information about rotation, should not be stored in volume + volume->text_configuration->style.prop.angle.reset(); + volume->set_transformation(trmat); // update printable state on canvas diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 3e85d1d884..55a1627197 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -3366,9 +3366,11 @@ void Selection::transform_volume_relative(GLVolume& volume, const VolumeCache& v ModelVolume *get_selected_volume(const Selection &selection) { - const GLVolume *vol_gl = get_selected_gl_volume(selection); + const GLVolume *gl_volume = get_selected_gl_volume(selection); + if (gl_volume == nullptr) + return nullptr; const ModelObjectPtrs &objects = selection.get_model()->objects; - return get_model_volume(*vol_gl, objects); + return get_model_volume(*gl_volume, objects); } const GLVolume *get_selected_gl_volume(const Selection &selection) diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index 33f64c0c61..0f2f1706ae 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -69,7 +69,8 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, const Camera &camera, std::optional &surface_drag, GLCanvas3D &canvas, - RaycastManager &raycast_manager) + RaycastManager &raycast_manager, + std::optional up_limit) { // Fix when leave window during dragging // Fix when click right button @@ -153,7 +154,10 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, Transform3d instance_tr = instance->get_matrix(); Transform3d instance_tr_inv = instance_tr.inverse(); Transform3d world_tr = instance_tr * volume_tr; - surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition}; + std::optional start_angle; + if (up_limit.has_value()) + start_angle = Emboss::calc_up(world_tr, *up_limit); + surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition, start_angle}; // disable moving with object by mouse canvas.enable_moving(false); @@ -195,11 +199,11 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, Transform3d world_new = z_rotation * surface_drag->world; auto world_new_linear = world_new.linear(); - // Fix direction of up vector - { + // Fix direction of up vector to zero initial rotation + if(up_limit.has_value()){ Vec3d z_world = world_new_linear.col(2); z_world.normalize(); - Vec3d wanted_up = Emboss::suggest_up(z_world); + Vec3d wanted_up = Emboss::suggest_up(z_world, *up_limit); Vec3d y_world = world_new_linear.col(1); auto y_rotation = Eigen::Quaternion::FromTwoVectors(y_world, wanted_up); @@ -229,7 +233,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, volume_new = volume_new * (*tc.fix_3mf_tr); // apply move in Z direction and rotation by up vector - Emboss::apply_transformation(tc.style.prop, volume_new); + Emboss::apply_transformation(surface_drag->start_angle, tc.style.prop.distance, volume_new); } // Update transformation for all instances diff --git a/src/slic3r/GUI/SurfaceDrag.hpp b/src/slic3r/GUI/SurfaceDrag.hpp index a3765f86bf..bb2600c28f 100644 --- a/src/slic3r/GUI/SurfaceDrag.hpp +++ b/src/slic3r/GUI/SurfaceDrag.hpp @@ -34,6 +34,9 @@ struct SurfaceDrag // condition for raycaster RaycastManager::AllowVolumes condition; + // initial rotation in Z axis of volume + std::optional start_angle; + // Flag whether coordinate hit some volume bool exist_hit = true; }; @@ -48,12 +51,14 @@ struct SurfaceDrag /// Contain gl_volumes and selection /// AABB trees for raycast in object /// Refresh state inside of function +/// When set than use correction of up vector /// True when event is processed otherwise false bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, const Camera &camera, std::optional &surface_drag, GLCanvas3D &canvas, - RaycastManager &raycast_manager); + RaycastManager &raycast_manager, + std::optional up_limit = {}); /// /// Calculate translation of volume onto surface of model From 8e8c5652b96dc4ab7d7739bfc2b89b9ae729ef61 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Thu, 2 Mar 2023 16:40:48 +0100 Subject: [PATCH 52/64] Fix for: ../src/slic3r/GUI/IconManager.cpp:38:57: error: 'fabs' is not a member of 'std'; did you mean 'abs'? 38 | unsigned int width = static_cast(std::fabs(std::round(size.x))); | ^~~~ | abs ../src/slic3r/GUI/IconManager.cpp:38:67: error: 'round' is not a member of 'std'; did you mean 'std::chrono::round'? 38 | unsigned int width = static_cast(std::fabs(std::round(size.x))); | ^~~~~ --- src/slic3r/GUI/IconManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/IconManager.cpp b/src/slic3r/GUI/IconManager.cpp index 78ae673efd..1974e917cb 100644 --- a/src/slic3r/GUI/IconManager.cpp +++ b/src/slic3r/GUI/IconManager.cpp @@ -1,5 +1,5 @@ #include "IconManager.hpp" - +#include #include using namespace Slic3r::GUI; @@ -35,7 +35,7 @@ std::vector IconManager::init(const std::vector // only rectangle are supported assert(size.x == size.y); // no subpixel supported - unsigned int width = static_cast(std::fabs(std::round(size.x))); + unsigned int width = static_cast(std::abs(std::round(size.x))); assert(size.x == static_cast(width)); // state order has to match the enum IconState @@ -171,7 +171,7 @@ void draw(const IconManager::Icon &icon, const ImVec2 &size, const ImVec4 &tint_ return; } - ImTextureID id = (void *) icon.tex_id; + ImTextureID id = (void *) icon.tex_id; const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size; ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col); } From 49bd17a0af7801926a1553cfea731a14507e19db Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 8 Mar 2023 15:10:36 +0100 Subject: [PATCH 53/64] Less agressive warning about bad visualization inside of text input --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 86 ++++++++++++------------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 6deda777e5..88ccde4bb1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -223,6 +223,7 @@ enum class IconType : unsigned { unbold, system_selector, open_file, + exclamation, // automatic calc of icon's count _count }; @@ -1283,6 +1284,8 @@ void GLGizmoEmboss::draw_window() #endif // ALLOW_FLOAT_WINDOW } +#include "imgui/imgui_internal.h" // scroll bar existence + void GLGizmoEmboss::draw_text_input() { auto create_range_text_prep = [&mng = m_style_manager, &text = m_text, &exist_unknown = m_text_contain_unknown_glyph]() { @@ -1312,75 +1315,69 @@ void GLGizmoEmboss::draw_text_input() if (exist_font) ImGui::PushFont(imgui_font); // show warning about incorrectness view of font - std::string warning; - std::string tool_tip; + std::string warning_tool_tip; if (!exist_font) { - warning = _u8L("Can't write text by selected font."); - tool_tip = _u8L("Try to choose another font."); + warning_tool_tip = _u8L("Can't write text by selected font.Try to choose another font."); } else { - std::string who; - auto append_warning = [&who, &tool_tip](std::string w, std::string t) { - if (!w.empty()) { - if (!who.empty()) who += " & "; - who += w; - } - if (!t.empty()) { - if (!tool_tip.empty()) tool_tip += "\n"; - tool_tip += t; - } + auto append_warning = [&warning_tool_tip](std::string t) { + if (!warning_tool_tip.empty()) + warning_tool_tip += "\n"; + warning_tool_tip += t; }; - if (priv::is_text_empty(m_text)) append_warning(_u8L("Empty"), _u8L("Embossed text can NOT contain only white spaces.")); + if (priv::is_text_empty(m_text)) + append_warning(_u8L("Embossed text can NOT contain only white spaces.")); if (m_text_contain_unknown_glyph) - append_warning(_u8L("Bad symbol"), _u8L("Text contain character glyph (represented by '?') unknown by font.")); + append_warning(_u8L("Text contain character glyph (represented by '?') unknown by font.")); const FontProp &prop = m_style_manager.get_font_prop(); - if (prop.skew.has_value()) append_warning(_u8L("Skew"), _u8L("Unsupported visualization of font skew for text input.")); - if (prop.boldness.has_value()) append_warning(_u8L("Boldness"), _u8L("Unsupported visualization of font boldness for text input.")); + if (prop.skew.has_value()) append_warning(_u8L("Text input do not show font skew.")); + if (prop.boldness.has_value()) append_warning(_u8L("Text input do not show font boldness.")); if (prop.line_gap.has_value()) - append_warning(_u8L("Line gap"), _u8L("Unsupported visualization of gap between lines inside text input.")); + append_warning(_u8L("Text input do not show gap between lines.")); auto &ff = m_style_manager.get_font_file_with_cache(); float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file, scale); if (imgui_size > StyleManager::max_imgui_font_size) - append_warning(_u8L("Too tall"), _u8L("Diminished font height inside text input.")); + append_warning(_u8L("Too tall, diminished font height inside text input.")); if (imgui_size < StyleManager::min_imgui_font_size) - append_warning(_u8L("Too small"), _u8L("Enlarged font height inside text input.")); - if (!who.empty()) warning = GUI::format(_L("%1% is NOT shown."), who); + append_warning(_u8L("Too small, enlarged font height inside text input.")); } - - // add border around input when warning appears - ScopeGuard input_border_sg; - if (!warning.empty()) { - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Border, ImGuiWrapper::COL_ORANGE_LIGHT); - input_border_sg.closure = []() { ImGui::PopStyleColor(); ImGui::PopStyleVar(); }; - } - + // flag for extend font ranges if neccessary // ranges can't be extend during font is activ(pushed) std::string range_text; float window_height = ImGui::GetWindowHeight(); float minimal_height = get_minimal_window_size().y; float extra_height = window_height - minimal_height; - ImVec2 text_size(m_gui_cfg->text_size.x, - m_gui_cfg->text_size.y + extra_height); + ImVec2 input_size(m_gui_cfg->text_size.x, m_gui_cfg->text_size.y + extra_height); const ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_AutoSelectAll; - if (ImGui::InputTextMultiline("##Text", &m_text, text_size, flags)) { + if (ImGui::InputTextMultiline("##Text", &m_text, input_size, flags)) { process(); range_text = create_range_text_prep(); } if (exist_font) ImGui::PopFont(); - if (!warning.empty()) { - if (ImGui::IsItemHovered() && !tool_tip.empty()) - ImGui::SetTooltip("%s", tool_tip.c_str()); + // warning tooltip has to be with default font + if (!warning_tool_tip.empty()) { + // Multiline input has hidden window for scrolling + ImGuiWindow *input = ImGui::GetCurrentWindow()->DC.ChildWindows.front(); + + const ImGuiStyle &style = ImGui::GetStyle(); + float scrollbar_width = (input->ScrollbarY) ? style.ScrollbarSize : 0.f; + float scrollbar_height = (input->ScrollbarX) ? style.ScrollbarSize : 0.f; + + bool hovered = ImGui::IsItemHovered(); + if (hovered) + ImGui::SetTooltip("%s", warning_tool_tip.c_str()); + ImVec2 cursor = ImGui::GetCursorPos(); float width = ImGui::GetContentRegionAvailWidth(); - ImVec2 size = ImGui::CalcTextSize(warning.c_str()); - ImVec2 padding = ImGui::GetStyle().FramePadding; - ImGui::SetCursorPos(ImVec2(width - size.x + padding.x, - cursor.y - size.y - padding.y)); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, warning); + const ImVec2& padding = style.FramePadding; + ImVec2 icon_pos(width - m_gui_cfg->icon_width - scrollbar_width + padding.x, + cursor.y - m_gui_cfg->icon_width - scrollbar_height - 2*padding.y); + + ImGui::SetCursorPos(icon_pos); + draw(get_icon(m_icons, IconType::exclamation, IconState::hovered)); ImGui::SetCursorPos(cursor); } @@ -2376,7 +2373,7 @@ bool GLGizmoEmboss::draw_italic_button() const wxFont &wx_font = m_style_manager.get_wx_font(); const auto& ff = m_style_manager.get_font_file_with_cache(); if (!wx_font.IsOk() || !ff.has_value()) { - draw(*m_icons[(int) IconType::italic][(int)IconState::disabled]); + draw(get_icon(m_icons, IconType::italic, IconState::disabled)); return false; } @@ -3316,7 +3313,8 @@ void GLGizmoEmboss::init_icons() "make_bold.svg", "make_unbold.svg", "search.svg", - "open.svg" + "open.svg", + "exclamation.svg" }; assert(filenames.size() == static_cast(IconType::_count)); std::string path = resources_dir() + "/icons/"; From ca5b310e433013204878f6f78a5d56fd893ebebc Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 15 Mar 2023 11:25:17 +0100 Subject: [PATCH 54/64] Fix: moving with text object(not volume) over build plate by canvas dragging --- src/libslic3r/Model.cpp | 19 +++++++++++++++++++ src/libslic3r/Model.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 18 ++++-------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 9 --------- src/slic3r/GUI/SurfaceDrag.cpp | 4 ++++ 5 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 5e1574e60d..6fdec8b14b 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1397,6 +1397,25 @@ void ModelObject::clone_for_cut(ModelObject** obj) (*obj)->input_file.clear(); } +bool ModelVolume::is_the_only_one_part() const +{ + if (m_type != ModelVolumeType::MODEL_PART) + return false; + if (object == nullptr) + return false; + for (const ModelVolume *v : object->volumes) { + if (v == nullptr) + continue; + // is this volume? + if (v->id() == this->id()) + continue; + // exist another model part in object? + if (v->type() == ModelVolumeType::MODEL_PART) + return false; + } + return true; +} + void ModelVolume::reset_extra_facets() { this->supported_facets.reset(); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 08fa794810..3fd9f21bc2 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -835,6 +835,7 @@ public: bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; } bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; } bool is_text() const { return text_configuration.has_value(); } + bool is_the_only_one_part() const; // behave like an object t_model_material_id material_id() const { return m_material_id; } void reset_extra_facets(); void apply_tolerance(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 88ccde4bb1..bc0b5e8110 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -1169,7 +1169,8 @@ void GLGizmoEmboss::close() m_volume->text_configuration.has_value() && priv::is_text_empty(m_text)) { Plater &p = *wxGetApp().plater(); - if (is_text_object(m_volume)) { + // is the text object? + if (m_volume->is_the_only_one_part()) { // delete whole object p.remove(m_parent.get_selection().get_object_idx()); } else { @@ -1912,7 +1913,7 @@ void GLGizmoEmboss::draw_font_list() void GLGizmoEmboss::draw_model_type() { - bool is_last_solid_part = is_text_object(m_volume); + bool is_last_solid_part = m_volume->is_the_only_one_part(); std::string title = _u8L("Text is to object"); if (is_last_solid_part) { ImVec4 color{.5f, .5f, .5f, 1.f}; @@ -2945,7 +2946,7 @@ void GLGizmoEmboss::draw_advanced() // input surface distance bool allowe_surface_distance = !m_volume->text_configuration->style.prop.use_surface && - !is_text_object(m_volume); + !m_volume->is_the_only_one_part(); std::optional &distance = font_prop.distance; float prev_distance = distance.has_value() ? *distance : .0f, min_distance = -2 * font_prop.emboss, @@ -3336,17 +3337,6 @@ bool priv::draw_button(const IconManager::VIcons &icons, IconType type, bool dis ); } -bool GLGizmoEmboss::is_text_object(const ModelVolume *text) { - if (text == nullptr) return false; - if (!text->text_configuration.has_value()) return false; - if (text->type() != ModelVolumeType::MODEL_PART) return false; - for (const ModelVolume *v : text->get_object()->volumes) { - if (v == text) continue; - if (v->type() == ModelVolumeType::MODEL_PART) return false; - } - return true; -} - ///////////// // priv namespace implementation /////////////// diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 7e7c2aa161..730b7354ca 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -328,15 +328,6 @@ private: // only temporary solution static const std::string M_ICON_FILENAME; - -public: - /// - /// Check if text is last solid part of object - /// TODO: move to emboss gui utils - /// - /// Model volume of Text - /// True when object otherwise False - static bool is_text_object(const ModelVolume *text); }; } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index 0f2f1706ae..f32113698b 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -116,6 +116,10 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, if (object == nullptr || instance == nullptr || volume == nullptr) return false; + // allowed drag&drop by canvas for object + if (volume->is_the_only_one_part()) + return false; + const ModelVolumePtrs &volumes = object->volumes; std::vector allowed_volumes_id; if (volumes.size() > 1) { From 19d02e6d749b844c2bcd5b8c158f923720380b99 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 17 Mar 2023 09:29:28 +0100 Subject: [PATCH 55/64] Fix: ../src/slic3r/GUI/IconManager.cpp:174:22: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] ../src/slic3r/GUI/SurfaceDrag.cpp:104:25: warning: comparison of integer expressions of different signedness: 'int' and 'std::vector::size_type' {aka 'long unsigned int'} [-Wsign-compare] ../src/slic3r/GUI/SurfaceDrag.cpp:57:30: warning: 'std::optional Slic3r::GUI::calc_scale(const Matrix3d&, const Matrix3d&, const Vec3d&)' defined but not used [-Wunused-function] ../src/slic3r/Utils/RaycastManager.cpp:14:56: warning: suggest parentheses around '&&' within '||' [-Wparentheses] ../src/slic3r/Utils/RaycastManager.cpp:316:32: warning: comparison of integer expressions of different signedness: 'int' and 'std::vector::size_type' {aka 'long unsigned int'} [-Wsign-compare] --- src/slic3r/GUI/IconManager.cpp | 2 +- src/slic3r/GUI/SurfaceDrag.cpp | 16 +++++++++------- src/slic3r/Utils/RaycastManager.cpp | 9 ++++++--- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/IconManager.cpp b/src/slic3r/GUI/IconManager.cpp index 1974e917cb..45c76887c3 100644 --- a/src/slic3r/GUI/IconManager.cpp +++ b/src/slic3r/GUI/IconManager.cpp @@ -171,7 +171,7 @@ void draw(const IconManager::Icon &icon, const ImVec2 &size, const ImVec4 &tint_ return; } - ImTextureID id = (void *) icon.tex_id; + ImTextureID id = (void *)static_cast(icon.tex_id); const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size; ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col); } diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index f32113698b..4a48ced29f 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -53,8 +53,8 @@ static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const return nearest_offset; } - // Calculate scale in world -static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) + // Calculate scale in world for check in debug +[[maybe_unused]] static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) { Vec3d from_dir = from * dir; Vec3d to_dir = to * dir; @@ -98,11 +98,13 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, return false; // is selected volume closest hovered? - const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; - int hovered_idx = canvas.get_first_hover_volume_idx(); - if (hovered_idx < 0 || - hovered_idx >= gl_volumes.size() || - gl_volumes[hovered_idx] != gl_volume) + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + if (int hovered_idx = canvas.get_first_hover_volume_idx(); + hovered_idx < 0) + return false; + else if (auto hovered_idx_ = static_cast(hovered_idx); + hovered_idx_ >= gl_volumes.size() || + gl_volumes[hovered_idx_] != gl_volume) return false; const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects); diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 63cb580dbe..80a7167554 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -11,7 +11,7 @@ static RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelIn return std::make_pair(instance.id().id, volume.id().id); } static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { - return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; } + return k1.first < k2.first || (k1.first == k2.first && k1.second < k2.second); } static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { return is_lower_key(i1.first, i2.first); }; } @@ -313,9 +313,12 @@ RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::A RaycastManager::Meshes meshes; for (const std::shared_ptr &caster : casters) { int index = SceneRaycaster::decode_id(type, caster->get_id()); - if (index < 0 || index >= gl_volumes.size()) + if (index < 0) continue; - const GLVolume *gl_volume = gl_volumes[index]; + auto index_ = static_cast(index); + if(index_ >= gl_volumes.size()) + continue; + const GLVolume *gl_volume = gl_volumes[index_]; const ModelVolume *volume = get_model_volume(*gl_volume, objects); size_t id = volume->id().id; if (condition.skip(id)) From 5ab9532e394238b9a580b8c10fe52abcec7b439e Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 13:45:41 +0100 Subject: [PATCH 56/64] Fix bad actualization without mesh source sorting --- src/slic3r/Utils/RaycastManager.cpp | 211 +++++++++++++++------------- 1 file changed, 110 insertions(+), 101 deletions(-) diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 80a7167554..62722b0769 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -1,25 +1,31 @@ #include "RaycastManager.hpp" #include +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/CameraUtils.hpp" + using namespace Slic3r::GUI; -namespace priv { +namespace{ using namespace Slic3r; -static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr); -static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); -static RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelInstance& instance){ +void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr); +const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); +RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelInstance& instance){ return std::make_pair(instance.id().id, volume.id().id); } -static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); -static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { +RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); +RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &items, const RaycastManager::TrKey &key); +bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { return k1.first < k2.first || (k1.first == k2.first && k1.second < k2.second); } -static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { +bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { return is_lower_key(i1.first, i2.first); }; +template inline void erase(std::vector &vec, const std::vector &flags); } void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Meshes *meshes) { // actualize MeshRaycaster - priv::actualize(m_meshes, object.volumes, skip, meshes); + ::actualize(m_meshes, object.volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); @@ -32,8 +38,8 @@ void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Mes for (const ModelInstance *instance : object.instances) { const Transform3d &instrance_tr = instance->get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey key = priv::create_key(*volume, *instance); - auto item = priv::find(m_transformations, key); + TrKey key = ::create_key(*volume, *instance); + auto item = ::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time item->second = transformation; @@ -41,19 +47,17 @@ void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Mes removed_transf[index] = false; } else { // add new transformation - m_transformations.emplace_back(std::make_pair(key, transformation)); + m_transformations.emplace_back(key, transformation); need_sort = true; } } } // clean other transformation - for (int i = removed_transf.size() - 1; i >= 0; --i) - if (removed_transf[i]) - m_transformations.erase(m_transformations.begin() + i); + ::erase(m_transformations, removed_transf); if (need_sort) - std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); + std::sort(m_transformations.begin(), m_transformations.end(), ::is_lower); } void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, Meshes *meshes) @@ -61,7 +65,7 @@ void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, const ModelVolumePtrs &volumes = instance.get_object()->volumes; // actualize MeshRaycaster - priv::actualize(m_meshes, volumes, skip, meshes); + ::actualize(m_meshes, volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); @@ -74,8 +78,8 @@ void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, const Transform3d &volume_tr = volume->get_matrix(); const Transform3d &instrance_tr = instance.get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey key = priv::create_key(*volume, instance); - auto item = priv::find(m_transformations, key); + TrKey key = ::create_key(*volume, instance); + auto item = ::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time item->second = transformation; @@ -83,40 +87,35 @@ void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, removed_transf[index] = false; } else { // add new transformation - m_transformations.emplace_back(std::make_pair(key, transformation)); + m_transformations.emplace_back(key, transformation); need_sort = true; } } // clean other transformation - for (int i = removed_transf.size() - 1; i >= 0; --i) - if (removed_transf[i]) - m_transformations.erase(m_transformations.begin() + i); + ::erase(m_transformations, removed_transf); if (need_sort) - std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); + std::sort(m_transformations.begin(), m_transformations.end(), ::is_lower); } std::optional RaycastManager::first_hit(const Vec3d& point, const Vec3d& direction, const ISkip *skip) const { // Improve: it is not neccessaru to use AABBMesh and calc normal for every hit - struct Result - { - const AABBMesh *mesh = nullptr; - double squared_distance; - int face; - Vec3d hit_world; - const Transform3d *tramsformation; - const TrKey *key; - }result; + + // Results + const AABBMesh *hit_mesh = nullptr; + double hit_squared_distance = 0.; + int hit_face = -1; + Vec3d hit_world; + const Transform3d *hit_tramsformation = nullptr; + const TrKey *hit_key = nullptr; - for (const auto &item : m_transformations) { - const TrKey &key = item.first; + for (const auto &[key, transformation]: m_transformations) { size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; - const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; - const Transform3d &transformation = item.second; Transform3d inv = transformation.inverse(); // transform input into mesh world @@ -129,46 +128,44 @@ std::optional RaycastManager::first_hit(const Vec3d& point, const AABBMesh::hit_result &hit = hits.front(); // convert to world - Vec3d hit_world = transformation * hit.position(); - double squared_distance = (point - hit_world).squaredNorm(); - if (result.mesh != nullptr && - result.squared_distance < squared_distance) + Vec3d world = transformation * hit.position(); + double squared_distance = (point - world).squaredNorm(); + if (hit_mesh != nullptr && + hit_squared_distance < squared_distance) continue; // exist closer one - result.mesh = mesh; - result.squared_distance = squared_distance; - result.face = hit.face(); - result.hit_world = hit_world; - result.tramsformation = &transformation; - result.key = &key; + hit_mesh = mesh; + hit_squared_distance = squared_distance; + hit_face = hit.face(); + hit_world = world; + hit_tramsformation = &transformation; + hit_key = &key; } - if (result.mesh == nullptr) + if (hit_mesh == nullptr) return {}; // Calculate normal from transformed triangle // NOTE: Anisotropic transformation of normal is not perpendiculat to triangle - const Vec3i tri = result.mesh->indices(result.face); - Vec3d pts[3]; - auto tr = result.tramsformation->linear(); + const Vec3i tri = hit_mesh->indices(hit_face); + std::array pts; + auto tr = hit_tramsformation->linear(); for (int i = 0; i < 3; ++i) - pts[i] = tr * result.mesh->vertices(tri[i]).cast(); + pts[i] = tr * hit_mesh->vertices(tri[i]).cast(); Vec3d normal_world = (pts[1] - pts[0]).cross(pts[2] - pts[1]); normal_world.normalize(); - SurfacePoint point_world{result.hit_world, normal_world}; - return RaycastManager::Hit{point_world, *result.key, result.squared_distance}; + SurfacePoint point_world{hit_world, normal_world}; + return RaycastManager::Hit{point_world, *hit_key, hit_squared_distance}; } std::optional RaycastManager::closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const { std::optional closest; - for (const auto &item : m_transformations) { - const TrKey &key = item.first; + for (const auto &[key, transformation] : m_transformations) { size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; - const Transform3d &transformation = item.second; - const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; Transform3d tr_inv = transformation.inverse(); Vec3d mesh_point = tr_inv * point; @@ -196,14 +193,12 @@ std::optional RaycastManager::closest_hit(const Vec3d &poin std::optional RaycastManager::closest(const Vec3d &point, const ISkip *skip) const { std::optional closest; - for (const auto &item : m_transformations) { - const TrKey &key = item.first; + for (const auto &[key, transformation] : m_transformations) { size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; - const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; - const Transform3d &transformation = item.second; Transform3d tr_inv = transformation.inverse(); Vec3d mesh_point = tr_inv * point; @@ -222,17 +217,15 @@ std::optional RaycastManager::closest(const Vec3d &p } Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const { - // TODO: transformations are sorted use lower bound - auto item = std::find_if(m_transformations.begin(), - m_transformations.end(), - [&tr_key](const TrItem &it) -> bool { - return it.first == tr_key; - }); - if (item == m_transformations.end()) return Transform3d::Identity(); - return item->second; + auto tr = ::find(m_transformations, tr_key); + if (tr == m_transformations.end()) + return Transform3d::Identity(); + return tr->second; } -void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes* inputs) + +namespace { +void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes* inputs) { // check if volume was removed std::vector removed_meshes(meshes.size(), {true}); @@ -242,33 +235,33 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu size_t oid = volume->id().id; if (skip != nullptr && skip->skip(oid)) continue; - auto item = std::find_if(meshes.begin(), meshes.end(), [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); - if (item == meshes.end()) { - // exist AABB in inputs ? - if (inputs != nullptr) { - auto input = std::find_if(inputs->begin(), inputs->end(), - [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); - if (input != inputs->end()) { - meshes.emplace_back(std::move(*input)); - continue; - } - } - - // add new raycaster - bool calculate_epsilon = true; - auto mesh = std::make_unique(volume->mesh(), calculate_epsilon); - meshes.emplace_back(std::make_pair(oid, std::move(mesh))); - need_sort = true; - } else { + auto is_oid = [oid](const RaycastManager::Mesh &it) { return oid == it.first; }; + if (auto item = std::find_if(meshes.begin(), meshes.end(), is_oid); + item != meshes.end()) { size_t index = item - meshes.begin(); removed_meshes[index] = false; + continue; } + + // exist AABB in inputs ? + if (inputs != nullptr) { + auto input = std::find_if(inputs->begin(), inputs->end(), is_oid); + if (input != inputs->end()) { + meshes.emplace_back(std::move(*input)); + need_sort = true; + continue; + } + } + + // add new raycaster + bool calculate_epsilon = true; + auto mesh = std::make_unique(volume->mesh(), calculate_epsilon); + meshes.emplace_back(std::make_pair(oid, std::move(mesh))); + need_sort = true; } // clean other raycasters - for (int i = removed_meshes.size() - 1; i >= 0; --i) - if (removed_meshes[i]) - meshes.erase(meshes.begin() + i); + erase(meshes, removed_meshes); // All the time meshes must be sorted by volume id - for faster search if (need_sort) { @@ -277,28 +270,44 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu } } -const Slic3r::AABBMesh *priv::get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id) +const Slic3r::AABBMesh *get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id) { - auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) -> bool { return m.first < i; }; + auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) { return m.first < i; }; auto it = std::lower_bound(meshes.begin(), meshes.end(), volume_id, is_lower_index); if (it == meshes.end() || it->first != volume_id) return nullptr; return &(*(it->second)); } -RaycastManager::TrItems::iterator priv::find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) { - auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &key)->bool { - return priv::is_lower_key(it.first, key); - }; +RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) { + auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &l_key) { return is_lower_key(it.first, l_key); }; auto it = std::lower_bound(items.begin(), items.end(), key, fnc); - if (it == items.end() || it->first != key) + if (it != items.end() && it->first != key) return items.end(); return it; } -#include "slic3r/GUI/GLCanvas3D.hpp" -#include "slic3r/GUI/Camera.hpp" -#include "slic3r/GUI/CameraUtils.hpp" +RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &items, const RaycastManager::TrKey &key) +{ + auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &l_key) { return is_lower_key(it.first, l_key); }; + auto it = std::lower_bound(items.begin(), items.end(), key, fnc); + if (it != items.end() && it->first != key) + return items.end(); + return it; +} + +template inline void erase(std::vector &vec, const std::vector &flags) +{ + assert(vec.size() == flags.size()); + if (flags.empty()) return; + + // reverse iteration over flags to erase indices from back to front. + for (int i = static_cast(flags.size()) - 1; i >= 0; --i) + if (flags[i]) + vec.erase(vec.begin() + i); +} + +} // namespace namespace Slic3r::GUI{ From 201e9d493f1607c8e8162ab541f52fc2f207e797 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 15:32:21 +0100 Subject: [PATCH 57/64] Remove side effects from setter for enable picking. --- src/slic3r/GUI/GLCanvas3D.cpp | 1 - src/slic3r/GUI/GUI_Preview.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f95d748ba0..e369e0f400 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1407,7 +1407,6 @@ void GLCanvas3D::enable_legend_texture(bool enable) void GLCanvas3D::enable_picking(bool enable) { m_picking_enabled = enable; - m_selection.set_mode(Selection::Instance); } void GLCanvas3D::enable_moving(bool enable) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index d9dfc7db22..656bc0b538 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -65,6 +65,7 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig m_canvas->allow_multisample(OpenGLManager::can_multisample()); m_canvas->enable_picking(true); + m_canvas->get_selection().set_mode(Selection::Instance); m_canvas->enable_moving(true); // XXX: more config from 3D.pm m_canvas->set_model(model); From d24472675c938986d0dc31ccc7edb6fa77e47cb0 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 15:33:00 +0100 Subject: [PATCH 58/64] Keep color of object during drag over surface. --- src/slic3r/GUI/SurfaceDrag.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index 4a48ced29f..ecee0a4e06 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -80,6 +80,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, // allow moving with object again canvas.enable_moving(true); + canvas.enable_picking(true); surface_drag.reset(); // only left up is correct @@ -167,6 +168,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, // disable moving with object by mouse canvas.enable_moving(false); + canvas.enable_picking(false); return true; } From 7afabcde959640b90e376752a334495ce5d76dc6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 20 Mar 2023 15:55:18 +0100 Subject: [PATCH 59/64] PlaceholderParser: Implemented one_of() matching function: 1st parameter is the text to match against, the rest of the parameters are pattern to be matched: either strings, then the match is exact, or regex enclosed in // or regex string starting with ~ For example one_of("a", "a", "b") finds a in "a", "b" one_of("abc", /.*a.*/) matches "abc" using regular expression /.*a.*/ --- src/libslic3r/PlaceholderParser.cpp | 62 +++++++++++++++++++-- tests/libslic3r/test_placeholder_parser.cpp | 12 ++++ 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index cb0760d4df..5f8624ad30 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -587,7 +587,7 @@ namespace client param1.set_s(buf); } - static void regex_op(expr &lhs, boost::iterator_range &rhs, char op) + static void regex_op(const expr &lhs, boost::iterator_range &rhs, char op, expr &out) { const std::string *subject = nullptr; if (lhs.type() == TYPE_STRING) { @@ -601,7 +601,7 @@ namespace client bool result = SLIC3R_REGEX_NAMESPACE::regex_match(*subject, SLIC3R_REGEX_NAMESPACE::regex(pattern)); if (op == '!') result = ! result; - lhs.set_b(result); + out.set_b(result); } catch (SLIC3R_REGEX_NAMESPACE::regex_error &ex) { // Syntax error in the regular expression boost::throw_exception(qi::expectation_failure( @@ -609,8 +609,37 @@ namespace client } } - static void regex_matches (expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '='); } - static void regex_doesnt_match(expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '!'); } + static void regex_matches (expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '=', lhs); } + static void regex_doesnt_match(expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '!', lhs); } + + static void one_of_test_init(expr &out) { + out.set_b(false); + } + template + static void one_of_test(const expr &match, const expr &pattern, expr &out) { + if (! out.b()) { + if (match.type() != TYPE_STRING) + match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value"); + if (pattern.type() != TYPE_STRING) + match.throw_exception("one_of(): Pattern has to be a string value"); + if (RegEx) { + try { + out.set_b(SLIC3R_REGEX_NAMESPACE::regex_match(match.s(), SLIC3R_REGEX_NAMESPACE::regex(pattern.s()))); + } catch (SLIC3R_REGEX_NAMESPACE::regex_error &) { + // Syntax error in the regular expression + pattern.throw_exception("Regular expression compilation failed"); + } + } else + out.set_b(match.s() == pattern.s()); + } + } + static void one_of_test_regex(const expr &match, boost::iterator_range &pattern, expr &out) { + if (! out.b()) { + if (match.type() != TYPE_STRING) + match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value"); + regex_op(match, pattern, '=', out); + } + } static void logical_op(expr &lhs, expr &rhs, char op) { @@ -1101,6 +1130,7 @@ namespace client { "multiplicative_expression", "Expecting an expression." }, { "unary_expression", "Expecting an expression." }, { "optional_parameter", "Expecting a closing brace or an optional parameter." }, + { "one_of_list", "Expecting a list of string patterns (simple text or rexep)" }, { "variable_reference", "Expecting a variable reference."}, { "is_nil_test", "Expecting a scalar variable reference."}, { "variable", "Expecting a variable name."}, @@ -1221,6 +1251,7 @@ namespace client qi::_a_type _a; qi::_b_type _b; qi::_r1_type _r1; + qi::_r2_type _r2; // Starting symbol of the grammer. // The leading eps is required by the "expectation point" operator ">". @@ -1395,7 +1426,8 @@ namespace client [ px::bind(&expr::template digits, _val, _2, _3) ] | (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] | (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ] - | (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [_val = _1] + | (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [ _val = _1 ] + | (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] @@ -1404,6 +1436,20 @@ namespace client ); unary_expression.name("unary_expression"); + one_of = (unary_expression(_r1)[_a = _1] > one_of_list(_r1, _a))[_val = _2]; + one_of.name("one_of"); + one_of_list = + eps[px::bind(&expr::one_of_test_init, _val)] > + ( ',' > *( + ( + unary_expression(_r1)[px::bind(&expr::template one_of_test, _r2, _1, _val)] + | (lit('~') > unary_expression(_r1))[px::bind(&expr::template one_of_test, _r2, _1, _val)] + | regular_expression[px::bind(&expr::one_of_test_regex, _r2, _1, _val)] + ) >> -lit(',')) + | eps + ); + one_of_list.name("one_of_list"); + optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( lit(')') [ px::bind(&FactorActions::noexpr, _val) ] | (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ] @@ -1445,6 +1491,7 @@ namespace client ("random") ("round") ("not") + ("one_of") ("or") ("true"); @@ -1466,6 +1513,8 @@ namespace client debug(additive_expression); debug(multiplicative_expression); debug(unary_expression); + debug(one_of); + debug(one_of_list); debug(optional_parameter); debug(variable_reference); debug(variable); @@ -1517,6 +1566,9 @@ namespace client qi::rule(const MyContext*), spirit_encoding::space_type> variable; // Evaluating whether a nullable variable is nil. qi::rule(const MyContext*), spirit_encoding::space_type> is_nil_test; + // Evaluating "one of" list of patterns. + qi::rule(const MyContext*), qi::locals>, spirit_encoding::space_type> one_of; + qi::rule(const MyContext*, const expr ¶m), spirit_encoding::space_type> one_of_list; qi::rule, spirit_encoding::space_type> if_else_output; qi::rule, int>, spirit_encoding::space_type> assignment_statement; diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 5248e089a8..e40657d160 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -113,6 +113,18 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("boolean expression parser: greater than or equal - false") { REQUIRE(! boolean_expression("12 >= 22")); } SECTION("boolean expression parser: lower than or equal (same values) - true") { REQUIRE(boolean_expression("12 <= 12")); } SECTION("boolean expression parser: greater than or equal (same values) - true") { REQUIRE(boolean_expression("12 >= 12")); } + SECTION("boolean expression parser: one_of(\"a\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"b\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"b\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"c\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"c\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"d\", \"a\", \"b\", \"c\")") { REQUIRE(! boolean_expression("one_of(\"d\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"a\")") { REQUIRE(! boolean_expression("one_of(\"a\")")); } + SECTION("boolean expression parser: one_of(\"a\", \"a\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\")")); } + SECTION("boolean expression parser: one_of(\"b\", \"a\")") { REQUIRE(! boolean_expression("one_of(\"b\", \"a\")")); } + SECTION("boolean expression parser: one_of(\"abcdef\", /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*c.*/)")); } + SECTION("boolean expression parser: one_of(\"abcdef\", /.*f.*/, /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*f.*/, /.*c.*/)")); } + SECTION("boolean expression parser: one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(boolean_expression("one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")")); } + SECTION("boolean expression parser: one_of(\"ghij\", /.*f.*/, /.*c.*/)") { REQUIRE(! boolean_expression("one_of(\"ghij\", /.*f.*/, /.*c.*/)")); } + SECTION("boolean expression parser: one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(! boolean_expression("one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")")); } SECTION("complex expression") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 and num_extruders>1")); } SECTION("complex expression2") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)")); } SECTION("complex expression3") { REQUIRE(! boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)")); } From d653cc6ab5d6ec8c1c209444fef21c73e6d7bed7 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 16:10:13 +0100 Subject: [PATCH 60/64] Fix crash made on MMU printers --- src/slic3r/Utils/RaycastManager.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 62722b0769..f8f4442411 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -313,11 +313,14 @@ namespace Slic3r::GUI{ RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes &condition) { - SceneRaycaster::EType type = SceneRaycaster::EType::Volume; - auto scene_casters = canvas.get_raycasters_for_picking(type); - const std::vector> &casters = *scene_casters; - const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; - const ModelObjectPtrs &objects = canvas.get_model()->objects; + SceneRaycaster::EType type = SceneRaycaster::EType::Volume; + auto scene_casters = canvas.get_raycasters_for_picking(type); + if (scene_casters == nullptr) + return {}; + const std::vector> &casters = *scene_casters; + + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + const ModelObjectPtrs &objects = canvas.get_model()->objects; RaycastManager::Meshes meshes; for (const std::shared_ptr &caster : casters) { @@ -327,9 +330,13 @@ RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::A auto index_ = static_cast(index); if(index_ >= gl_volumes.size()) continue; - const GLVolume *gl_volume = gl_volumes[index_]; - const ModelVolume *volume = get_model_volume(*gl_volume, objects); - size_t id = volume->id().id; + const GLVolume *gl_volume = gl_volumes[index_]; + if (gl_volume == nullptr) + continue; + const ModelVolume *volume = get_model_volume(*gl_volume, objects); + if (volume == nullptr) + continue; + size_t id = volume->id().id; if (condition.skip(id)) continue; auto mesh = std::make_unique(caster->get_raycaster()->get_aabb_mesh()); From 9c70ed01e5f3345f7468c64add674dbf7ba93590 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 17:41:51 +0100 Subject: [PATCH 61/64] Fix add emboss volume on reflected --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 7 ++----- src/slic3r/Utils/RaycastManager.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index bc0b5e8110..695399ab20 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3462,15 +3462,12 @@ bool priv::start_create_volume_on_surface_job( if (!hit.has_value()) return false; - // priv::reset_skew(hit_to_world); - Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); - // Create result volume transformation Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, priv::up_limit); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); - // new transformation in world coor is surface_trmat - Transform3d volume_trmat = instance.inverse() * 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); return true; } diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index f8f4442411..b2b7588555 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -153,6 +153,8 @@ std::optional RaycastManager::first_hit(const Vec3d& point, for (int i = 0; i < 3; ++i) pts[i] = tr * hit_mesh->vertices(tri[i]).cast(); Vec3d normal_world = (pts[1] - pts[0]).cross(pts[2] - pts[1]); + if (has_reflection(*hit_tramsformation)) + normal_world *= -1; normal_world.normalize(); SurfacePoint point_world{hit_world, normal_world}; @@ -298,8 +300,8 @@ RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &item template inline void erase(std::vector &vec, const std::vector &flags) { - assert(vec.size() == flags.size()); - if (flags.empty()) return; + if (vec.size() < flags.size() || flags.empty()) + return; // reverse iteration over flags to erase indices from back to front. for (int i = static_cast(flags.size()) - 1; i >= 0; --i) From ff5767b6f58e1464f5426536e1e4ce872fe5dc6e Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Mon, 20 Mar 2023 20:01:37 +0100 Subject: [PATCH 62/64] enable AABB tree over 3D lines --- src/libslic3r/AABBTreeLines.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index 21678bfcdf..2136e8edbd 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -165,9 +165,9 @@ inline std::vector> get_intersections_with_line(si // Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies // during tree traversal. template -inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector &lines) +inline AABBTreeIndirect::Tree build_aabb_tree_over_indexed_lines(const std::vector &lines) { - using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>; + using TreeType = AABBTreeIndirect::Tree; // using CoordType = typename TreeType::CoordType; using VectorType = typename TreeType::VectorType; using BoundingBox = typename TreeType::BoundingBox; From ae016aceb9aee77749e7aa6d74525e7e47ec0ee7 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 21 Mar 2023 12:11:09 +0100 Subject: [PATCH 63/64] Fix for selection of unknodn font --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 224 ++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 3 +- 2 files changed, 91 insertions(+), 136 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 695399ab20..dd18cb878a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -53,7 +53,6 @@ #define SHOW_IMGUI_ATLAS #define SHOW_ICONS_TEXTURE #define SHOW_FINE_POSITION // draw convex hull around volume -#define SHOW_WX_WEIGHT_INPUT #define DRAW_PLACE_TO_ADD_TEXT // Interactive draw of window position #define ALLOW_OPEN_NEAR_VOLUME #define EXECUTE_PROCESS_ON_MAIN_THREAD // debug execution on main thread @@ -531,6 +530,12 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) return; } + // Not known situation when could happend this is only for sure + if (!m_is_unknown_font && !m_style_manager.is_active_font()) + create_notification_not_valid_font("No active font in style. Select correct one."); + else if (!m_is_unknown_font && !m_style_manager.get_wx_font().IsOk()) + create_notification_not_valid_font("WxFont is not loaded properly."); + // Configuration creation double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); float main_toolbar_height = m_parent.get_main_toolbar_height(); @@ -1192,21 +1197,24 @@ void GLGizmoEmboss::draw_window() if (ImGui::Button("add svg")) choose_svg_file(); #endif // ALLOW_DEBUG_MODE - bool is_active_font = m_style_manager.is_active_font(); - if (!is_active_font) - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Warning: No font is selected. Select correct one.")); - + // Setter of indent must be befor disable !!! + ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, m_gui_cfg->indent); + ScopeGuard indent_sc([](){ ImGui::PopStyleVar(/*ImGuiStyleVar_IndentSpacing*/); }); + // Disable all except selection of font, when open text from 3mf with unknown font m_imgui->disabled_begin(m_is_unknown_font); - ScopeGuard unknown_font_sc([&]() { - m_imgui->disabled_end(); - }); - draw_text_input(); - m_imgui->disabled_begin(!is_active_font); + ScopeGuard unknown_font_sc([imgui = m_imgui]() { imgui->disabled_end(/*m_is_unknown_font*/); }); + + draw_text_input(); - ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, m_gui_cfg->indent); ImGui::Indent(); - draw_style_edit(); + // When unknown font is inside .3mf only font selection is allowed + m_imgui->disabled_end(/*m_is_unknown_font*/); + draw_font_list_line(); + m_imgui->disabled_begin(m_is_unknown_font); + bool use_inch = wxGetApp().app_config->get_bool("use_inches"); + draw_height(use_inch); + draw_depth(use_inch); ImGui::Unindent(); // close advanced style property when unknown font is selected @@ -1223,8 +1231,6 @@ void GLGizmoEmboss::draw_window() } else if (m_is_advanced_edit_style) set_minimal_window_size(false); - ImGui::PopStyleVar(); // ImGuiStyleVar_IndentSpacing - ImGui::Separator(); draw_style_list(); @@ -1234,8 +1240,6 @@ void GLGizmoEmboss::draw_window() ImGui::Separator(); draw_model_type(); } - - m_imgui->disabled_end(); // !is_active_font #ifdef SHOW_WX_FONT_DESCRIPTOR if (is_selected_style) @@ -1728,6 +1732,59 @@ bool GLGizmoEmboss::select_facename(const wxString &facename) return true; } +void GLGizmoEmboss::draw_font_list_line() +{ + bool exist_stored_style = m_style_manager.exist_stored_style(); + bool exist_change_in_font = m_style_manager.is_font_changed(); + const std::string& font_text = m_gui_cfg->translations.font; + if (exist_change_in_font || !exist_stored_style) + ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, font_text); + else + ImGuiWrapper::text(font_text); + + ImGui::SameLine(m_gui_cfg->input_offset); + + draw_font_list(); + + bool exist_change = false; + if (!m_is_unknown_font) { + ImGui::SameLine(); + if (draw_italic_button()) + exist_change = true; + ImGui::SameLine(); + if (draw_bold_button()) + exist_change = true; + } else { + // when exist unknown font add confirmation button + ImGui::SameLine(); + // Apply for actual selected font + if (ImGui::Button(_u8L("Apply").c_str())) + exist_change = true; + } + + EmbossStyle &style = m_style_manager.get_style(); + if (exist_change_in_font) { + ImGui::SameLine(ImGui::GetStyle().FramePadding.x); + if (draw_button(m_icons, IconType::undo)) { + const EmbossStyle *stored_style = m_style_manager.get_stored_style(); + + style.path = stored_style->path; + style.prop.boldness = stored_style->prop.boldness; + style.prop.skew = stored_style->prop.skew; + + wxFont new_wx_font = WxFontUtils::load_wxFont(style.path); + if (new_wx_font.IsOk() && m_style_manager.set_wx_font(new_wx_font)) + exist_change = true; + } else if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", _u8L("Revert font changes.").c_str()); + } + + if (exist_change) { + m_style_manager.clear_glyphs_cache(); + process(); + } +} + void GLGizmoEmboss::draw_font_list() { // Set partial @@ -1745,16 +1802,6 @@ void GLGizmoEmboss::draw_font_list() // When deletation of font appear this variable is set std::optional del_index; - // When is unknown font is inside .3mf only font selection is allowed - // Stop Imgui disable + Guard again start disabling - ScopeGuard unknown_font_sc; - if (m_is_unknown_font) { - m_imgui->disabled_end(); - unknown_font_sc.closure = [&]() { - m_imgui->disabled_begin(true); - }; - } - // Code const char *popup_id = "##font_list_popup"; const char *input_id = "##font_list_input"; @@ -1881,13 +1928,6 @@ void GLGizmoEmboss::draw_font_list() store(m_face_names); } - if (m_is_unknown_font) { - ImGui::SameLine(); - // Apply for actual selected font - if (ImGui::Button(_u8L("Apply").c_str())) - process(); - } - #ifdef ALLOW_ADD_FONT_BY_FILE ImGui::SameLine(); // select font file by file browser @@ -2580,89 +2620,6 @@ bool GLGizmoEmboss::rev_checkbox(const std::string &name, undo_offset, draw_offseted_input); } -void GLGizmoEmboss::draw_style_edit() -{ - { - // Check correct WxFont - const wxFont &wx_font = m_style_manager.get_wx_font(); - assert(wx_font.IsOk()); - if (!wx_font.IsOk()) { - ImGui::TextColored(ImGuiWrapper::COL_ORANGE_DARK, "%s", _u8L("WxFont is not loaded properly.").c_str()); - return; - } - } - - bool exist_stored_style = m_style_manager.exist_stored_style(); - bool exist_change_in_font = m_style_manager.is_font_changed(); - const GuiCfg::Translations &tr = m_gui_cfg->translations; - if (exist_change_in_font || !exist_stored_style) - ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr.font); - else - ImGuiWrapper::text(tr.font); - ImGui::SameLine(m_gui_cfg->input_offset); - draw_font_list(); - bool exist_change = false; - if (!m_is_unknown_font) { - ImGui::SameLine(); - if (draw_italic_button()) - exist_change = true; - ImGui::SameLine(); - if (draw_bold_button()) - exist_change = true; - } - EmbossStyle &style = m_style_manager.get_style(); - if (exist_change_in_font) { - ImGui::SameLine(ImGui::GetStyle().FramePadding.x); - if (draw_button(m_icons, IconType::undo)) { - const EmbossStyle *stored_style = m_style_manager.get_stored_style(); - style.path = stored_style->path; - style.prop.boldness = stored_style->prop.boldness; - style.prop.skew = stored_style->prop.skew; - - wxFont new_wx_font = WxFontUtils::load_wxFont(style.path); - if (new_wx_font.IsOk() && - m_style_manager.set_wx_font(new_wx_font)) - exist_change = true; - } else if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", _u8L("Revert font changes.").c_str()); - } - - if (exist_change) { - m_style_manager.clear_glyphs_cache(); - process(); - } - - bool use_inch = wxGetApp().app_config->get_bool("use_inches"); - draw_height(use_inch); - draw_depth(use_inch); - -#ifdef SHOW_WX_WEIGHT_INPUT - if (wx_font.has_value()) { - ImGui::Text("%s", "weight"); - ImGui::SameLine(m_gui_cfg->input_offset); - ImGui::SetNextItemWidth(m_gui_cfg->input_width); - int weight = wx_font->GetNumericWeight(); - int min_weight = 1, max_weight = 1000; - if (ImGui::SliderInt("##weight", &weight, min_weight, max_weight)) { - wx_font->SetNumericWeight(weight); - m_style_manager.wx_font_changed(); - process(); - } - - wxFont f = wx_font->Bold(); - bool disable = f == *wx_font; - ImGui::SameLine(); - if (draw_button(IconType::bold, disable)) { - *wx_font = f; - m_style_manager.wx_font_changed(); - process(); - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", _u8L("wx Make bold").c_str()); - } -#endif // SHOW_WX_WEIGHT_INPUT -} - bool GLGizmoEmboss::set_height() { float &value = m_style_manager.get_style().prop.size_in_mm; @@ -3254,39 +3211,36 @@ bool GLGizmoEmboss::choose_svg_file() void GLGizmoEmboss::create_notification_not_valid_font( const TextConfiguration &tc) { - // not neccessary, but for sure that old notification doesnt exist - if (m_is_unknown_font) remove_notification_not_valid_font(); - m_is_unknown_font = true; - - auto type = NotificationType::UnknownFont; - auto level = - NotificationManager::NotificationLevel::WarningNotificationLevel; - const EmbossStyle &es = m_style_manager.get_style(); const auto &face_name_opt = es.prop.face_name; - const auto &face_name_3mf_opt = tc.style.prop.face_name; + const std::string &face_name_3mf = tc.style.prop.face_name.value_or(tc.style.path); - const std::string &face_name_3mf = face_name_3mf_opt.has_value() ? - *face_name_3mf_opt : - tc.style.path; - - std::string face_name_by_wx; + std::optional face_name_by_wx; if (!face_name_opt.has_value()) { const wxFont& wx_font = m_style_manager.get_wx_font(); if (wx_font.IsOk()) { wxString wx_face_name = wx_font.GetFaceName(); - face_name_by_wx = std::string((const char *) wx_face_name.ToUTF8()); + if (!wx_face_name.empty()) + face_name_by_wx = std::string(wx_face_name.ToUTF8().data()); } } - - const std::string &face_name = face_name_opt.has_value() ? *face_name_opt : - (!face_name_by_wx.empty() ? face_name_by_wx : es.path); - + const std::string &face_name = face_name_opt.value_or(face_name_by_wx.value_or(es.path)); std::string text = GUI::format(_L("Can't load exactly same font(\"%1%\"), " "Aplication selected a similar one(\"%2%\"). " "You have to specify font for enable edit text."), face_name_3mf, face_name); + create_notification_not_valid_font(text); +} + +void GLGizmoEmboss::create_notification_not_valid_font(const std::string &text) { + // not neccessary, but for sure that old notification doesnt exist + if (m_is_unknown_font) + remove_notification_not_valid_font(); + m_is_unknown_font = true; + + auto type = NotificationType::UnknownFont; + auto level = NotificationManager::NotificationLevel::WarningNotificationLevel; auto notification_manager = wxGetApp().plater()->get_notification_manager(); notification_manager->push_notification(type, level, text); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 730b7354ca..ebbdf616cc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -102,8 +102,8 @@ private: void init_font_name_texture(); struct FaceName; void draw_font_preview(FaceName &face, bool is_visible); + void draw_font_list_line(); void draw_font_list(); - void draw_style_edit(); void draw_height(bool use_inch); void draw_depth(bool use_inch); @@ -154,6 +154,7 @@ private: // When open text loaded from .3mf it could be written with unknown font bool m_is_unknown_font; void create_notification_not_valid_font(const TextConfiguration& tc); + void create_notification_not_valid_font(const std::string& text); void remove_notification_not_valid_font(); // This configs holds GUI layout size given by translated texts. From 4326929960e4fab5d6516e8fd9a0a73f26b4580d Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Tue, 21 Mar 2023 12:36:23 +0100 Subject: [PATCH 64/64] Updated output filename format. --- resources/profiles/PrusaResearch.idx | 1 + resources/profiles/PrusaResearch.ini | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 05970389cf..77fdf5a83b 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,4 +1,5 @@ min_slic3r_version = 2.6.0-alpha5 +1.9.0-alpha0 Updated output filename format. 1.7.0-alpha2 Updated compatibility condition in some filament profiles (Prusa XL). 1.7.0-alpha1 Added profiles for Original Prusa XL. Added filament profile for Prusament PETG Tungsten 75%. min_slic3r_version = 2.6.0-alpha1 diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 3fb3aee3d9..f57a8110d7 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.7.0-alpha2 +config_version = 1.9.0-alpha0 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -192,7 +192,7 @@ notes = overhangs = 1 only_retract_when_crossing_perimeters = 0 ooze_prevention = 0 -output_filename_format = {input_filename_base}_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode perimeters = 2 perimeter_extruder = 1 perimeter_extrusion_width = 0.45 @@ -390,7 +390,7 @@ support_material_xy_spacing = 80% support_material_interface_spacing = 0.2 support_material_spacing = 2.2 raft_first_layer_expansion = 2 -output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode infill_anchor = 2 infill_anchor_max = 15 thick_bridges = 0