From 587effbedf754855134c52bb4aa1c6fb447ebb61 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Thu, 3 Oct 2019 11:38:31 +0200
Subject: [PATCH] ENABLE_3DCONNEXION_DEVICES -> Refactored Mouse3DController to
 be unaware of current active GLCanvas3D

---
 src/slic3r/GUI/Camera.cpp            | 20 ++++++++++++++++
 src/slic3r/GUI/Camera.hpp            |  5 ++++
 src/slic3r/GUI/GLCanvas3D.cpp        | 36 ++++++++++++++++++++++++++--
 src/slic3r/GUI/GLCanvas3D.hpp        |  5 ++++
 src/slic3r/GUI/MainFrame.cpp         |  4 ----
 src/slic3r/GUI/Mouse3DController.cpp | 19 ++++++---------
 src/slic3r/GUI/Mouse3DController.hpp | 20 ++++------------
 src/slic3r/GUI/Plater.cpp            |  8 -------
 8 files changed, 75 insertions(+), 42 deletions(-)

diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp
index 8e3a6d1f1..4bb3e6ad5 100644
--- a/src/slic3r/GUI/Camera.cpp
+++ b/src/slic3r/GUI/Camera.cpp
@@ -103,6 +103,25 @@ void Camera::set_theta(float theta, bool apply_limit)
     }
 }
 
+#if ENABLE_3DCONNEXION_DEVICES
+void Camera::update_zoom(double delta_zoom)
+{
+    set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1));
+}
+
+void Camera::set_zoom(double zoom)
+{
+    // Don't allow to zoom too far outside the scene.
+    double zoom_min = calc_zoom_to_bounding_box_factor(m_scene_box, (int)m_viewport[2], (int)m_viewport[3]);
+    if (zoom_min > 0.0)
+        zoom = std::max(zoom, zoom_min * 0.7);
+
+    // Don't allow to zoom too close to the scene.
+    zoom = std::min(zoom, 100.0);
+
+    m_zoom = zoom;
+}
+#else
 void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h)
 {
     zoom = std::max(std::min(zoom, 4.0), -4.0) / 10.0;
@@ -118,6 +137,7 @@ void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, i
 
     m_zoom = zoom;
 }
+#endif // ENABLE_3DCONNEXION_DEVICES
 
 bool Camera::select_view(const std::string& direction)
 {
diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp
index 839d0d6cf..bf452833c 100644
--- a/src/slic3r/GUI/Camera.hpp
+++ b/src/slic3r/GUI/Camera.hpp
@@ -63,8 +63,13 @@ public:
     void set_theta(float theta, bool apply_limit);
 
     double get_zoom() const { return m_zoom; }
+#if ENABLE_3DCONNEXION_DEVICES
+    void update_zoom(double delta_zoom);
+    void set_zoom(double zoom);
+#else
     void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h);
     void set_zoom(double zoom) { m_zoom = zoom; }
+#endif // ENABLE_3DCONNEXION_DEVICES
 
     const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
     void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; }
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 562a939fb..6f94647ac 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1394,7 +1394,11 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const
 BoundingBoxf3 GLCanvas3D::scene_bounding_box() const
 {
     BoundingBoxf3 bb = volumes_bounding_box();
+#if ENABLE_3DCONNEXION_DEVICES
+    bb.merge(m_bed.get_bounding_box(true));
+#else
     bb.merge(m_bed.get_bounding_box(false));
+#endif // ENABLE_3DCONNEXION_DEVICES
 
     if (m_config != nullptr)
     {
@@ -1542,10 +1546,16 @@ void GLCanvas3D::render()
         return;
     }
 
+#if ENABLE_3DCONNEXION_DEVICES
+    const Size& cnv_size = get_canvas_size();
+#endif // ENABLE_3DCONNEXION_DEVICES
+
     if (m_camera.requires_zoom_to_bed)
     {
         zoom_to_bed();
+#if !ENABLE_3DCONNEXION_DEVICES
         const Size& cnv_size = get_canvas_size();
+#endif // !ENABLE_3DCONNEXION_DEVICES
         _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
         m_camera.requires_zoom_to_bed = false;
     }
@@ -1637,7 +1647,7 @@ void GLCanvas3D::render()
 #endif // ENABLE_CAMERA_STATISTICS
 
 #if ENABLE_3DCONNEXION_DEVICES
-    wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog();
+    wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
 #endif // ENABLE_3DCONNEXION_DEVICES
 
     wxGetApp().imgui()->render();
@@ -2313,7 +2323,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
     m_dirty |= m_undoredo_toolbar.update_items_state();
     m_dirty |= m_view_toolbar.update_items_state();
 #if ENABLE_3DCONNEXION_DEVICES
-    bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply();
+    bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera);
     m_dirty |= mouse3d_controller_applied;
 #endif // ENABLE_3DCONNEXION_DEVICES
 
@@ -2460,11 +2470,19 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
         case 'B':
         case 'b': { zoom_to_bed(); break; }
         case 'I':
+#if ENABLE_3DCONNEXION_DEVICES
+        case 'i': { _update_camera_zoom(1.0); break; }
+#else
         case 'i': { set_camera_zoom(1.0); break; }
+#endif // ENABLE_3DCONNEXION_DEVICES
         case 'K':
         case 'k': { m_camera.select_next_type(); m_dirty = true; break; }
         case 'O':
+#if ENABLE_3DCONNEXION_DEVICES
+        case 'o': { _update_camera_zoom(-1.0); break; }
+#else
         case 'o': { set_camera_zoom(-1.0); break; }
+#endif // ENABLE_3DCONNEXION_DEVICES
 #if ENABLE_RENDER_PICKING_PASS
         case 'T':
         case 't': {
@@ -2618,7 +2636,11 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
         return;
 
     // Calculate the zoom delta and apply it to the current zoom factor
+#if ENABLE_3DCONNEXION_DEVICES
+    _update_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta());
+#else
     set_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta());
+#endif // ENABLE_3DCONNEXION_DEVICES
 }
 
 void GLCanvas3D::on_timer(wxTimerEvent& evt)
@@ -3413,12 +3435,14 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
     m_dirty = true;
 }
 
+#if !ENABLE_3DCONNEXION_DEVICES
 void GLCanvas3D::set_camera_zoom(double zoom)
 {
     const Size& cnv_size = get_canvas_size();
     m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height());
     m_dirty = true;
 }
+#endif // !ENABLE_3DCONNEXION_DEVICES
 
 void GLCanvas3D::update_gizmos_on_off_state()
 {
@@ -3887,6 +3911,14 @@ void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box)
     m_dirty = true;
 }
 
+#if ENABLE_3DCONNEXION_DEVICES
+void GLCanvas3D::_update_camera_zoom(double zoom)
+{
+    m_camera.update_zoom(zoom);
+    m_dirty = true;
+}
+#endif // ENABLE_3DCONNEXION_DEVICES
+
 void GLCanvas3D::_refresh_if_shown_on_screen()
 {
     if (_is_shown_on_screen())
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index cce6b0f2b..3051ae653 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -567,7 +567,9 @@ public:
     void do_flatten(const Vec3d& normal, const std::string& snapshot_type);
     void do_mirror(const std::string& snapshot_type);
 
+#if !ENABLE_3DCONNEXION_DEVICES
     void set_camera_zoom(double zoom);
+#endif // !ENABLE_3DCONNEXION_DEVICES
 
     void update_gizmos_on_off_state();
     void reset_all_gizmos() { m_gizmos.reset_all_states(); }
@@ -640,6 +642,9 @@ private:
     BoundingBoxf3 _max_bounding_box(bool include_gizmos, bool include_bed_model) const;
 
     void _zoom_to_box(const BoundingBoxf3& box);
+#if ENABLE_3DCONNEXION_DEVICES
+    void _update_camera_zoom(double zoom);
+#endif // ENABLE_3DCONNEXION_DEVICES
 
     void _refresh_if_shown_on_screen();
 
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 615d7d8a9..eaf6944b6 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -111,10 +111,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
         }
         
         if(m_plater) m_plater->stop_jobs();
-#if ENABLE_3DCONNEXION_DEVICES
-        if (m_plater != nullptr)
-            m_plater->get_mouse3d_controller().set_canvas(nullptr);
-#endif // ENABLE_3DCONNEXION_DEVICES
 
         // Weird things happen as the Paint messages are floating around the windows being destructed.
         // Avoid the Paint messages by hiding the main window.
diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp
index c7bf89b4d..ccf1adfa2 100644
--- a/src/slic3r/GUI/Mouse3DController.cpp
+++ b/src/slic3r/GUI/Mouse3DController.cpp
@@ -3,7 +3,7 @@
 
 #if ENABLE_3DCONNEXION_DEVICES
 
-#include "GLCanvas3D.hpp"
+#include "Camera.hpp"
 #include "GUI_App.hpp"
 #include "PresetBundle.hpp"
 #include "AppConfig.hpp"
@@ -115,13 +115,12 @@ bool Mouse3DController::State::has_any_button() const
     return !m_buttons.empty();
 }
 
-bool Mouse3DController::State::apply(GLCanvas3D& canvas)
+bool Mouse3DController::State::apply(Camera& camera)
 {
     if (!wxGetApp().IsActive())
         return false;
 
     bool ret = false;
-    Camera& camera = canvas.get_camera();
 
     if (has_translation())
     {
@@ -150,8 +149,8 @@ bool Mouse3DController::State::apply(GLCanvas3D& canvas)
         {
             switch (i)
             {
-            case 0: { canvas.set_camera_zoom(1.0); break; }
-            case 1: { canvas.set_camera_zoom(-1.0); break; }
+            case 0: { camera.update_zoom(1.0); break; }
+            case 1: { camera.update_zoom(-1.0); break; }
             default: { break; }
             }
         }
@@ -165,7 +164,6 @@ bool Mouse3DController::State::apply(GLCanvas3D& canvas)
 
 Mouse3DController::Mouse3DController()
     : m_initialized(false)
-    , m_canvas(nullptr)
     , m_device(nullptr)
     , m_device_str("")
     , m_running(false)
@@ -206,17 +204,14 @@ void Mouse3DController::shutdown()
     m_initialized = false;
 }
 
-void Mouse3DController::render_settings_dialog() const
+void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const
 {
-    if ((m_canvas == nullptr) || !m_running || !m_settings_dialog)
+    if (!m_running || !m_settings_dialog)
         return;
 
     ImGuiWrapper& imgui = *wxGetApp().imgui();
 
-    std::lock_guard<std::mutex> lock(m_mutex);
-    Size cnv_size = m_canvas->get_canvas_size();
-
-    imgui.set_next_window_pos(0.5f * (float)cnv_size.get_width(), 0.5f * (float)cnv_size.get_height(), ImGuiCond_Always, 0.5f, 0.5f);
+    imgui.set_next_window_pos(0.5f * (float)canvas_width, 0.5f * (float)canvas_height, ImGuiCond_Always, 0.5f, 0.5f);
     imgui.set_next_window_bg_alpha(0.5f);
 
     ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp
index 55deaf118..bfad1b3b2 100644
--- a/src/slic3r/GUI/Mouse3DController.hpp
+++ b/src/slic3r/GUI/Mouse3DController.hpp
@@ -13,7 +13,7 @@
 namespace Slic3r {
 namespace GUI {
 
-class GLCanvas3D;
+struct Camera;
 
 class Mouse3DController
 {
@@ -57,14 +57,12 @@ class Mouse3DController
         void set_rotation_scale(float scale) { m_rotation_scale = scale; }
 
         // return true if any change to the camera took place
-        bool apply(GLCanvas3D& canvas);
+        bool apply(Camera& camera);
     };
 
     bool m_initialized;
     mutable State m_state;
     std::thread m_thread;
-    GLCanvas3D* m_canvas;
-    mutable std::mutex m_mutex;
     hid_device* m_device;
     std::string m_device_str;
     bool m_running;
@@ -79,26 +77,16 @@ public:
     bool is_device_connected() const { return m_device != nullptr; }
     bool is_running() const { return m_running; }
 
-    void set_canvas(GLCanvas3D* canvas)
-    {
-        std::lock_guard<std::mutex> lock(m_mutex);
-        m_canvas = canvas;
-    }
-
     bool has_translation() const { return m_state.has_translation(); }
     bool has_rotation() const { return m_state.has_rotation(); }
     bool has_translation_or_rotation() const { return m_state.has_translation_or_rotation(); }
     bool has_any_button() const { return m_state.has_any_button(); }
 
-    bool apply()
-    {
-        std::lock_guard<std::mutex> lock(m_mutex);
-        return (m_canvas != nullptr) ? m_state.apply(*m_canvas) : false;
-    }
+    bool apply(Camera& camera) { return m_state.apply(camera); }
 
     bool is_settings_dialog_shown() const { return m_settings_dialog; }
     void show_settings_dialog(bool show) { m_settings_dialog = show; }
-    void render_settings_dialog() const;
+    void render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const;
 
 private:
     void connect_device();
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index c3214336b..b85da3373 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -3263,10 +3263,6 @@ void Plater::priv::set_current_panel(wxPanel* panel)
                 view3D->reload_scene(true);
         }
 
-#if ENABLE_3DCONNEXION_DEVICES
-        mouse3d_controller.set_canvas(view3D->get_canvas3d());
-#endif // ENABLE_3DCONNEXION_DEVICES
-
         // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
         view3D->set_as_dirty();
         view_toolbar.select_item("3D");
@@ -3282,10 +3278,6 @@ void Plater::priv::set_current_panel(wxPanel* panel)
         // keeps current gcode preview, if any
         preview->reload_print(true);
 
-#if ENABLE_3DCONNEXION_DEVICES
-        mouse3d_controller.set_canvas(preview->get_canvas3d());
-#endif // ENABLE_3DCONNEXION_DEVICES
-
         preview->set_canvas_as_dirty();
         view_toolbar.select_item("Preview");
     }