From 2057d0ce9a9007f03f9c0519f56ef893600338e5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 30 Mar 2023 13:05:57 +0200 Subject: [PATCH] SPE-1610 - Modified SceneRaycaster to keep the current selection when the user clicks and drag a selected volume --- src/slic3r/GUI/SceneRaycaster.cpp | 55 ++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index 1e8bb0c82b..1f44a07d6d 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -3,6 +3,8 @@ #include "Camera.hpp" #include "GUI_App.hpp" +#include "Selection.hpp" +#include "Plater.hpp" namespace Slic3r { namespace GUI { @@ -88,10 +90,48 @@ void SceneRaycaster::remove_raycaster(std::shared_ptr item) SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) const { + // helper class used to return currently selected volume as hit when overlapping with other volumes + // to allow the user to click and drag on a selected volume + class VolumeKeeper + { + std::optional m_selected_volume_id; + Vec3f m_closest_hit_pos{ std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; + bool m_selected_volume_already_found{ false }; + + public: + VolumeKeeper() { + const Selection& selection = wxGetApp().plater()->get_selection(); + if (selection.is_single_volume() || selection.is_single_modifier()) { + const GLVolume* volume = selection.get_first_volume(); + if (!volume->is_wipe_tower && !volume->is_sla_pad() && !volume->is_sla_support()) + m_selected_volume_id = *selection.get_volume_idxs().begin(); + } + } + + bool is_active() const { return m_selected_volume_id.has_value(); } + const Vec3f& get_closest_hit_pos() const { return m_closest_hit_pos; } + bool check_hit_result(const HitResult& hit) { + assert(is_active()); + + if (m_selected_volume_already_found && hit.type == SceneRaycaster::EType::Volume && hit.position.isApprox(m_closest_hit_pos)) + return false; + + if (hit.type == SceneRaycaster::EType::Volume) + m_selected_volume_already_found = *m_selected_volume_id == decode_id(hit.type, hit.raycaster_id); + + m_closest_hit_pos = hit.position; + return true; + } + }; + + VolumeKeeper volume_keeper; + double closest_hit_squared_distance = std::numeric_limits::max(); - auto is_closest = [&closest_hit_squared_distance](const Camera& camera, const Vec3f& hit) { + auto is_closest = [&closest_hit_squared_distance, &volume_keeper](const Camera& camera, const Vec3f& hit) { const double hit_squared_distance = (camera.get_position() - hit.cast()).squaredNorm(); - const bool ret = hit_squared_distance < closest_hit_squared_distance; + bool ret = hit_squared_distance < closest_hit_squared_distance; + if (volume_keeper.is_active()) + ret |= hit.isApprox(volume_keeper.get_closest_hit_pos()); if (ret) closest_hit_squared_distance = hit_squared_distance; return ret; @@ -103,7 +143,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came HitResult ret; - auto test_raycasters = [this, is_closest, clipping_plane](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) { + auto test_raycasters = [this, is_closest, clipping_plane, &volume_keeper](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) { const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr; const std::vector>* raycasters = get_raycasters(type); const Vec3f camera_forward = camera.get_dir_forward().cast(); @@ -117,9 +157,14 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came if (item->get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane)) { current_hit.position = (trafo * current_hit.position.cast()).cast(); current_hit.normal = (trafo.matrix().block(0, 0, 3, 3).inverse().transpose() * current_hit.normal.cast()).normalized().cast(); - if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f){ + if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f) { if (is_closest(camera, current_hit.position)) { - ret = current_hit; + if (volume_keeper.is_active()) { + if (volume_keeper.check_hit_result(current_hit)) + ret = current_hit; + } + else + ret = current_hit; } } }