diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 431a62e377..58d6aa6018 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -544,12 +544,9 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) if (m_volume == nullptr) return false; - std::optional wanted_up_limit; - if (m_keep_up) - wanted_up_limit = 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, up_limit); + 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? @@ -569,13 +566,14 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) calculate_scale(); // Recalculate angle for GUI - if (!m_keep_up) { - const GLVolume *gl_volume = get_selected_gl_volume(m_parent.get_selection()); + if (!m_keep_up) { + const Selection &selection = m_parent.get_selection(); + const GLVolume *gl_volume = get_selected_gl_volume(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().angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit); + m_style_manager.get_style().angle = calc_angle(selection); } volume_transformation_changing(); @@ -994,11 +992,12 @@ void GLGizmoEmboss::on_stop_dragging() 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()); + const Selection &selection = m_parent.get_selection(); + const GLVolume *gl_volume = get_selected_gl_volume(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_style().angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit); + m_style_manager.get_style().angle = calc_angle(selection); m_rotate_start_angle.reset(); @@ -1235,7 +1234,7 @@ void GLGizmoEmboss::set_volume_by_selection() StyleManager::Style style_{style}; // copy style_.projection = volume->emboss_shape->projection; - style_.angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit); + style_.angle = calc_angle(selection); style_.distance = calc_distance(*gl_volume, m_raycast_manager, m_parent); if (auto it = std::find_if(styles.begin(), styles.end(), has_same_name); @@ -1283,7 +1282,7 @@ void GLGizmoEmboss::set_volume_by_selection() // Calculate current angle of up vector assert(m_style_manager.is_active_font()); if (m_style_manager.is_active_font()) - m_style_manager.get_style().angle = calc_up(gl_volume->world_matrix(), up_limit); + m_style_manager.get_style().angle = calc_angle(selection); // calculate scale for height and depth inside of scaled object instance calculate_scale(); @@ -2890,11 +2889,12 @@ void GLGizmoEmboss::draw_advanced() do_local_z_rotate(m_parent, diff_angle); // calc angle after rotation - const GLVolume *gl_volume = get_selected_gl_volume(m_parent.get_selection()); + const Selection &selection = m_parent.get_selection(); + const GLVolume *gl_volume = get_selected_gl_volume(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_style().angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit); + m_style_manager.get_style().angle = calc_angle(selection); if (font_prop.per_glyph) reinit_text_lines(m_text_lines.get_lines().size()); @@ -2955,13 +2955,27 @@ void GLGizmoEmboss::draw_advanced() if (ImGui::Button(_u8L("Set text to face camera").c_str())) { assert(get_selected_volume(m_parent.get_selection()) == m_volume); const Camera &cam = wxGetApp().plater()->get_camera(); - FontProp& fp = m_style_manager.get_font_prop(); - if (face_selected_volume_to_camera(cam, m_parent) && - (use_surface || fp.per_glyph)) { - if (fp.per_glyph) - reinit_text_lines(); - process(); - } + + auto wanted_up_limit = (m_keep_up) ? std::optional(UP_LIMIT) : std::optional{}; + if (face_selected_volume_to_camera(cam, m_parent, wanted_up_limit)) { + if (!m_keep_up) { + // update current style + m_style_manager.get_style().angle = calc_angle(m_parent.get_selection()); + } else { + // after set face to camera, angle should be the same + assert(is_approx(m_style_manager.get_style().angle, calc_angle(m_parent.get_selection()))); + } + + FontProp &fp = m_style_manager.get_font_prop(); + if (use_surface || fp.per_glyph) { + if (fp.per_glyph) + reinit_text_lines(); + process(); + } else { + // Check outside bed + m_parent.requires_check_outside_state(); + } + } } else if (ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", _u8L("Orient the text towards the camera.").c_str()); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp index d72984386d..b8fccceeee 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp @@ -279,38 +279,13 @@ bool GLGizmoSVG::on_mouse_for_rotation(const wxMouseEvent &mouse_event) return used; } -namespace{ -std::optional calculate_angle(const Selection& selection) -{ - const GLVolume *gl_volume = selection.get_first_volume(); - assert(gl_volume != nullptr); - if (gl_volume == nullptr) - return {}; - - Transform3d to_world = gl_volume->world_matrix(); - const ModelVolume* volume = get_model_volume(*gl_volume, selection.get_model()->objects); - assert(volume != nullptr); - assert(volume->emboss_shape.has_value()); - if (volume == nullptr || - !volume->emboss_shape.has_value() || - !volume->emboss_shape->fix_3mf_tr) - return calc_up(to_world, Slic3r::GUI::up_limit); - - // exist fix matrix and must be applied before calculation - to_world = to_world * volume->emboss_shape->fix_3mf_tr->inverse(); - return calc_up(to_world, Slic3r::GUI::up_limit); -} -} - bool GLGizmoSVG::on_mouse_for_translate(const wxMouseEvent &mouse_event) { // exist selected volume? if (m_volume == nullptr) return false; - std::optional up_limit; - if (m_keep_up) - up_limit = Slic3r::GUI::up_limit; + auto up_limit = m_keep_up ? std::optional(UP_LIMIT) : std::optional{}; const Camera &camera = wxGetApp().plater()->get_camera(); bool was_dragging = m_surface_drag.has_value(); @@ -346,7 +321,7 @@ bool GLGizmoSVG::on_mouse_for_translate(const wxMouseEvent &mouse_event) // Recalculate angle for GUI if (!m_keep_up) - m_angle = calculate_angle(m_parent.get_selection()); + m_angle = calc_angle(m_parent.get_selection()); } return res; } @@ -1213,7 +1188,7 @@ void GLGizmoSVG::set_volume_by_selection() m_shape_warnings = create_shape_warnings(es, get_scale_for_tolerance()); // Calculate current angle of up vector - m_angle = calculate_angle(selection); + m_angle = calc_angle(selection); m_distance = calc_distance(*gl_volume, m_raycast_manager, m_parent); m_shape_bb = get_extents(m_volume_shape.shapes_with_ids); @@ -1351,9 +1326,22 @@ void GLGizmoSVG::draw_window() if (ImGui::Button(_u8L("Face the camera").c_str())) { const Camera &cam = wxGetApp().plater()->get_camera(); - if (face_selected_volume_to_camera(cam, m_parent) && - m_volume->emboss_shape->projection.use_surface) - process(); + auto wanted_up_limit = (m_keep_up) ? std::optional(UP_LIMIT) : std::optional{}; + if (face_selected_volume_to_camera(cam, m_parent, wanted_up_limit)) { + if (!m_keep_up) { + m_angle = calc_angle(m_parent.get_selection()); + } else { + // after set face to camera, angle should be the same + assert(is_approx(m_angle, calc_angle(m_parent.get_selection()))); + } + + if (m_volume->emboss_shape->projection.use_surface) { + process(); + } else { + // Check outside bed + m_parent.requires_check_outside_state(); + } + } } ImGui::Unindent(m_gui_cfg->icon_width); @@ -1895,7 +1883,7 @@ void GLGizmoSVG::draw_rotation() do_local_z_rotate(m_parent, diff_angle); // calc angle after rotation - m_angle = calculate_angle(m_parent.get_selection()); + m_angle = calc_angle(m_parent.get_selection()); // recalculate for surface cut if (m_volume->emboss_shape->projection.use_surface) diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index 3c6902dfbb..d303bf0a07 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -1561,7 +1561,7 @@ bool start_create_volume_on_surface_job(CreateVolumeParams &input, DataBasePtr d return on_bad_state(std::move(data), object); // Create result volume transformation - Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, Slic3r::GUI::up_limit); + Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, UP_LIMIT); apply_transformation(input.angle, input.distance, surface_trmat); Transform3d transform = instance->get_matrix().inverse() * surface_trmat; auto gizmo_type = static_cast(input.gizmo); diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index 0730811c65..fa282bd817 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -56,7 +56,7 @@ bool start_dragging(const Vec2d &mouse_pos, /// bool dragging(const Vec2d &mouse_pos, const Camera &camera, - std::optional &surface_drag, + SurfaceDrag &surface_drag, // need to write whether exist hit GLCanvas3D &canvas, const RaycastManager &raycast_manager, const std::optional &up_limit); @@ -80,7 +80,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, std::optional &surface_drag, GLCanvas3D &canvas, RaycastManager &raycast_manager, - std::optional up_limit) + const std::optional&up_limit) { // Fix when leave window during dragging // Fix when click right button @@ -109,7 +109,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, return false; if (mouse_event.Dragging()) - return dragging(mouse_position(mouse_event), camera, surface_drag, canvas, raycast_manager, up_limit); + return dragging(mouse_position(mouse_event), camera, *surface_drag, canvas, raycast_manager, up_limit); return false; } @@ -228,6 +228,25 @@ std::optional calc_distance(const GLVolume &gl_volume, const RaycastManag return sign * static_cast(sqrt(distance_sq)); } +std::optional calc_angle(const Selection &selection) +{ + const GLVolume *gl_volume = selection.get_first_volume(); + assert(gl_volume != nullptr); + if (gl_volume == nullptr) + return {}; + + Transform3d to_world = gl_volume->world_matrix(); + const ModelVolume *volume = get_model_volume(*gl_volume, selection.get_model()->objects); + assert(volume != nullptr); + assert(volume->emboss_shape.has_value()); + if (volume == nullptr || !volume->emboss_shape.has_value() || !volume->emboss_shape->fix_3mf_tr) + return Emboss::calc_up(to_world, UP_LIMIT); + + // exist fix matrix and must be applied before calculation + to_world = to_world * volume->emboss_shape->fix_3mf_tr->inverse(); + return Emboss::calc_up(to_world, UP_LIMIT); +} + Transform3d world_matrix_fixed(const GLVolume &gl_volume, const ModelObjectPtrs &objects) { Transform3d res = gl_volume.world_matrix(); @@ -287,16 +306,41 @@ void selection_transform(Selection &selection, const std::function &sele selection.setup_cache(); } -bool face_selected_volume_to_camera(const Camera &camera, GLCanvas3D &canvas) +bool face_selected_volume_to_camera(const Camera &camera, GLCanvas3D &canvas, const std::optional& wanted_up_limit) { - const Vec3d &cam_dir = camera.get_dir_forward(); - Selection &sel = canvas.get_selection(); - if (sel.is_empty()) + const Selection &selection = canvas.get_selection(); + if (selection.is_empty()) return false; + GLVolume *gl_volume_ptr = get_selected_gl_volume(canvas); + if (gl_volume_ptr == nullptr) + return false; + const GLVolume &gl_volume = *gl_volume_ptr; + + const ModelObjectPtrs &objects = canvas.get_model()->objects; + const ModelObject *object_ptr = get_model_object(gl_volume, objects); + assert(object_ptr != nullptr); + if (object_ptr == nullptr) + return false; + const ModelObject &object = *object_ptr; + + const ModelInstance *instance_ptr = get_model_instance(gl_volume, object); + assert(instance_ptr != nullptr); + if (instance_ptr == nullptr) + return false; + const ModelInstance &instance = *instance_ptr; + + const ModelVolume *volume_ptr = get_model_volume(gl_volume, object); + assert(volume_ptr != nullptr); + if (volume_ptr == nullptr) + return false; + const ModelVolume &volume = *volume_ptr; + + const Vec3d &cam_dir = camera.get_dir_forward(); + // camera direction transformed into volume coordinate system - Transform3d to_world = world_matrix_fixed(sel); - Vec3d cam_dir_tr = to_world.inverse().linear() * cam_dir; + Transform3d to_world = world_matrix_fixed(gl_volume, objects); + Vec3d cam_dir_tr = to_world.inverse().linear() * cam_dir; cam_dir_tr.normalize(); Vec3d emboss_dir(0., 0., -1.); @@ -305,11 +349,12 @@ bool face_selected_volume_to_camera(const Camera &camera, GLCanvas3D &canvas) 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()); + assert(selection.get_volume_idxs().size() == 1); + if (selection.get_volume_idxs().size() != 1) + return false; Transform3d vol_rot; - Transform3d vol_tr = gl_volume->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 @@ -322,12 +367,27 @@ bool face_selected_volume_to_camera(const Camera &camera, GLCanvas3D &canvas) 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); + 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); + + // Need modifiable gl volume + GLVolume *gl_volume_ptr_ = canvas.get_selection().get_volume(*selection.get_volume_idxs().begin()); + assert(gl_volume_ptr_ != nullptr); + if (gl_volume_ptr_ == nullptr) + return false; + + // Need modifiable model volume + ModelVolume *model_volume_ptr_ = get_model_volume(gl_volume, objects); + assert(model_volume_ptr_ != nullptr); + if (model_volume_ptr_ == nullptr) + return false; + + // write result transformation + gl_volume_ptr_->set_volume_transformation(Geometry::Transformation(res)); + model_volume_ptr_->set_transformation(res); return true; } @@ -501,25 +561,19 @@ bool start_dragging(const Vec2d &mouse_pos, return true; } -bool dragging(const Vec2d &mouse_pos, - const Camera &camera, - std::optional &surface_drag, - GLCanvas3D &canvas, - const RaycastManager &raycast_manager, - const std::optional &up_limit) +Transform3d get_volume_transformation( + Transform3d world, // from volume + const Vec3d& world_dir, // wanted new direction + const Vec3d& world_position, // wanted new position + const std::optional& fix, // [optional] fix matrix + // Invers transformation of text volume instance + // Help convert world transformation to instance space + const Transform3d& instance_inv, + // initial rotation in Z axis + std::optional current_angle = {}, + const std::optional &up_limit = {}) { - Vec2d offseted_mouse = mouse_pos + surface_drag->mouse_offset_without_sla_shift; - 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(); + auto world_linear = world.linear(); // Calculate offset: transformation to wanted position { // Reset skew of the text Z axis: @@ -530,8 +584,8 @@ bool dragging(const Vec2d &mouse_pos, } 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 z_rotation = Eigen::Quaternion::FromTwoVectors(text_z_world, world_dir); + Transform3d world_new = z_rotation * world; auto world_new_linear = world_new.linear(); // Fix direction of up vector to zero initial rotation @@ -546,34 +600,61 @@ bool dragging(const Vec2d &mouse_pos, 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; + Transform3d volume_new{Eigen::Translation(instance_inv * world_position)}; + volume_new.linear() = 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; + return Transform3d::Identity(); // 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); // fix baked transformation from .3mf store process - if (volume != nullptr && volume->emboss_shape.has_value()) { - const std::optional &fix = volume->emboss_shape->fix_3mf_tr; - if (fix.has_value()) - volume_new = volume_new * (*fix); + if (fix.has_value()) + volume_new = volume_new * (*fix); - // apply move in Z direction and rotation by up vector - Emboss::apply_transformation(surface_drag->start_angle, surface_drag->start_distance, volume_new); + // apply move in Z direction and rotation by up vector + Emboss::apply_transformation(current_angle, {}, volume_new); + + return volume_new; +} + +bool dragging(const Vec2d &mouse_pos, + const Camera &camera, + SurfaceDrag &surface_drag, + GLCanvas3D &canvas, + const RaycastManager &raycast_manager, + const std::optional &up_limit) +{ + Vec2d offseted_mouse = mouse_pos + surface_drag.mouse_offset_without_sla_shift; + 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; } + const ModelVolume *volume = get_model_volume(*surface_drag.gl_volume, canvas.get_model()->objects); + std::optional fix; + if (volume !=nullptr && + volume->emboss_shape.has_value() && + volume->emboss_shape->fix_3mf_tr.has_value()) + fix = volume->emboss_shape->fix_3mf_tr; + Transform3d volume_new = get_volume_transformation(surface_drag.world, hit->normal, hit->position, + fix, surface_drag.instance_inv, surface_drag.start_angle, up_limit); + // 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()) + 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); } diff --git a/src/slic3r/GUI/SurfaceDrag.hpp b/src/slic3r/GUI/SurfaceDrag.hpp index d1b154a945..6bc3c523ed 100644 --- a/src/slic3r/GUI/SurfaceDrag.hpp +++ b/src/slic3r/GUI/SurfaceDrag.hpp @@ -51,7 +51,7 @@ struct SurfaceDrag // Limit direction of up vector on model // Between side and top surface -constexpr double up_limit = 0.9; +constexpr double UP_LIMIT = 0.9; /// /// Mouse event handler, when move(drag&drop) volume over model surface @@ -70,7 +70,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, std::optional &surface_drag, GLCanvas3D &canvas, RaycastManager &raycast_manager, - std::optional up_limit = {}); + const std::optional&up_limit = {}); /// /// Calculate translation of volume onto surface of model @@ -90,6 +90,13 @@ std::optional calc_surface_offset(const Selection &selection, RaycastMana std::optional calc_distance(const GLVolume &gl_volume, RaycastManager &raycaster, GLCanvas3D &canvas); std::optional calc_distance(const GLVolume &gl_volume, const RaycastManager &raycaster, const RaycastManager::ISkip *condition); +/// +/// Calculate up vector angle +/// +/// Calculation of angle is for selected one volume +/// +std::optional calc_angle(const Selection &selection); + /// /// Get transformation to world /// - use fix after store to 3mf when exists @@ -123,8 +130,9 @@ void selection_transform(Selection &selection, const std::function& sele /// /// Define view vector /// Containe Selected ModelVolume to modify orientation +/// [Optional]Limit for direction of up vector /// True when apply change otherwise false -bool face_selected_volume_to_camera(const Camera &camera, GLCanvas3D &canvas); +bool face_selected_volume_to_camera(const Camera &camera, GLCanvas3D &canvas, const std::optional &wanted_up_limit = {}); /// /// Rotation around z Axis(emboss direction)