From 00b10c7f9d99976929d9c67d1abfc8f90f6ed055 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Tue, 11 Sep 2018 14:48:17 +0200
Subject: [PATCH] 3D gizmos' grabber size as a function of selected objects
 size

---
 xs/src/slic3r/GUI/GLGizmo.cpp | 206 +++++++++++++++++-----------------
 xs/src/slic3r/GUI/GLGizmo.hpp |  15 +--
 2 files changed, 113 insertions(+), 108 deletions(-)

diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
index 4aa5ab32f..50c93d72c 100644
--- a/xs/src/slic3r/GUI/GLGizmo.cpp
+++ b/xs/src/slic3r/GUI/GLGizmo.cpp
@@ -20,90 +20,91 @@ static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f
 namespace Slic3r {
 namespace GUI {
 
-    // returns the intersection of the given ray with the plane parallel to plane XY and passing through the given center
-    // coordinates are local to the plane
-    Vec3d intersection_on_plane_xy(const Linef3& ray, const Vec3d& center)
+// returns the intersection of the given ray with the plane parallel to plane XY and passing through the given center
+// coordinates are local to the plane
+Vec3d intersection_on_plane_xy(const Linef3& ray, const Vec3d& center)
+{
+    Transform3d m = Transform3d::Identity();
+    m.translate(-center);
+    Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
+    return Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0);
+}
+
+// returns the intersection of the given ray with the plane parallel to plane XZ and passing through the given center
+// coordinates are local to the plane
+Vec3d intersection_on_plane_xz(const Linef3& ray, const Vec3d& center)
+{
+    Transform3d m = Transform3d::Identity();
+    m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX()));
+    m.translate(-center);
+    Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
+    return Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1));
+}
+
+// returns the intersection of the given ray with the plane parallel to plane YZ and passing through the given center
+// coordinates are local to the plane
+Vec3d intersection_on_plane_yz(const Linef3& ray, const Vec3d& center)
+{
+    Transform3d m = Transform3d::Identity();
+    m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY()));
+    m.translate(-center);
+    Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
+
+    return Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0));
+}
+
+// return an index:
+// 0 for plane XY
+// 1 for plane XZ
+// 2 for plane YZ
+// which indicates which plane is best suited for intersecting the given unit vector
+// giving precedence to the plane with the given index
+unsigned int select_best_plane(const Vec3d& unit_vector, unsigned int preferred_plane)
+{
+    unsigned int ret = preferred_plane;
+
+    // 1st checks if the given vector is not parallel to the given preferred plane
+    double dot_to_normal = 0.0;
+    switch (ret)
     {
-        Transform3d m = Transform3d::Identity();
-        m.translate(-center);
-        Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
-        return Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0);
+    case 0: // plane xy
+    {
+        dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitZ()));
+        break;
+    }
+    case 1: // plane xz
+    {
+        dot_to_normal = std::abs(unit_vector.dot(-Vec3d::UnitY()));
+        break;
+    }
+    case 2: // plane yz
+    {
+        dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitX()));
+        break;
+    }
+    default:
+    {
+        break;
+    }
     }
 
-    // returns the intersection of the given ray with the plane parallel to plane XZ and passing through the given center
-    // coordinates are local to the plane
-    Vec3d intersection_on_plane_xz(const Linef3& ray, const Vec3d& center)
+    // if almost parallel, select the plane whose normal direction is closest to the given vector direction,
+    // otherwise return the given preferred plane index
+    if (dot_to_normal < 0.1)
     {
-        Transform3d m = Transform3d::Identity();
-        m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX()));
-        m.translate(-center);
-        Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
-        return Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1));
+        typedef std::map<double, unsigned int> ProjsMap;
+        ProjsMap projs_map;
+        projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitZ())), 0));  // plane xy
+        projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(-Vec3d::UnitY())), 1)); // plane xz
+        projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitX())), 2));  // plane yz
+        ret = projs_map.rbegin()->second;
     }
 
-    // returns the intersection of the given ray with the plane parallel to plane YZ and passing through the given center
-    // coordinates are local to the plane
-    Vec3d intersection_on_plane_yz(const Linef3& ray, const Vec3d& center)
-    {
-        Transform3d m = Transform3d::Identity();
-        m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY()));
-        m.translate(-center);
-        Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
-
-        return Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0));
-    }
-
-    // return an index:
-    // 0 for plane XY
-    // 1 for plane XZ
-    // 2 for plane YZ
-    // which indicates which plane is best suited for intersecting the given unit vector
-    // giving precedence to the plane with the given index
-    unsigned int select_best_plane(const Vec3d& unit_vector, unsigned int preferred_plane)
-    {
-        unsigned int ret = preferred_plane;
-
-        // 1st checks if the given vector is not parallel to the given preferred plane
-        double dot_to_normal = 0.0;
-        switch (ret)
-        {
-        case 0: // plane xy
-        {
-            dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitZ()));
-            break;
-        }
-        case 1: // plane xz
-        {
-            dot_to_normal = std::abs(unit_vector.dot(-Vec3d::UnitY()));
-            break;
-        }
-        case 2: // plane yz
-        {
-            dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitX()));
-            break;
-        }
-        default:
-        {
-            break;
-        }
-        }
-
-        // if almost parallel, select the plane whose normal direction is closest to the given vector direction,
-        // otherwise return the given preferred plane index
-        if (dot_to_normal < 0.1)
-        {
-            typedef std::map<double, unsigned int> ProjsMap;
-            ProjsMap projs_map;
-            projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitZ())), 0));  // plane xy
-            projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(-Vec3d::UnitY())), 1)); // plane xz
-            projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitX())), 2));  // plane yz
-            ret = projs_map.rbegin()->second;
-        }
-
-        return ret;
-    }
+    return ret;
+}
     
-    const float GLGizmoBase::Grabber::HalfSize = 2.0f;
+const float GLGizmoBase::Grabber::SizeFactor = 0.025f;
+const float GLGizmoBase::Grabber::MinHalfSize = 1.5f;
 const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f;
 
 GLGizmoBase::Grabber::Grabber()
@@ -117,7 +118,7 @@ GLGizmoBase::Grabber::Grabber()
     color[2] = 1.0f;
 }
 
-void GLGizmoBase::Grabber::render(bool hover) const
+void GLGizmoBase::Grabber::render(bool hover, const BoundingBoxf3& box) const
 {
     float render_color[3];
     if (hover)
@@ -129,12 +130,15 @@ void GLGizmoBase::Grabber::render(bool hover) const
     else
         ::memcpy((void*)render_color, (const void*)color, 3 * sizeof(float));
 
-    render(render_color, true);
+    render(box, render_color, true);
 }
 
-void GLGizmoBase::Grabber::render(const float* render_color, bool use_lighting) const
+void GLGizmoBase::Grabber::render(const BoundingBoxf3& box, const float* render_color, bool use_lighting) const
 {
-    float half_size = dragging ? HalfSize * DraggingScaleFactor : HalfSize;
+    float max_size = (float)box.max_size();
+    float half_size = dragging ? max_size * SizeFactor * DraggingScaleFactor : max_size * SizeFactor;
+    half_size = std::max(half_size, MinHalfSize);
+
     if (use_lighting)
         ::glEnable(GL_LIGHTING);
 
@@ -291,16 +295,16 @@ float GLGizmoBase::picking_color_component(unsigned int id) const
     return (float)color / 255.0f;
 }
 
-void GLGizmoBase::render_grabbers() const
+void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
 {
     for (int i = 0; i < (int)m_grabbers.size(); ++i)
     {
         if (m_grabbers[i].enabled)
-            m_grabbers[i].render(m_hover_id == i);
+            m_grabbers[i].render((m_hover_id == i), box);
     }
 }
 
-void GLGizmoBase::render_grabbers_for_picking() const
+void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
 {
     for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
     {
@@ -309,7 +313,7 @@ void GLGizmoBase::render_grabbers_for_picking() const
             m_grabbers[i].color[0] = 1.0f;
             m_grabbers[i].color[1] = 1.0f;
             m_grabbers[i].color[2] = picking_color_component(i);
-            m_grabbers[i].render_for_picking();
+            m_grabbers[i].render_for_picking(box);
         }
     }
 }
@@ -440,7 +444,7 @@ void GLGizmoRotate::on_render(const BoundingBoxf3& box) const
     if (m_hover_id != -1)
         render_angle();
 
-    render_grabber();
+    render_grabber(box);
 
     ::glPopMatrix();
 }
@@ -452,7 +456,7 @@ void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const
     ::glPushMatrix();
 
     transform_to_local();
-    render_grabbers_for_picking();
+    render_grabbers_for_picking(box);
 
     ::glPopMatrix();
 }
@@ -544,7 +548,7 @@ void GLGizmoRotate::render_angle() const
     ::glEnd();
 }
 
-void GLGizmoRotate::render_grabber() const
+void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
 {
     double grabber_radius = (double)(m_radius + GrabberOffset);
     m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0);
@@ -558,7 +562,7 @@ void GLGizmoRotate::render_grabber() const
     ::glEnd();
 
     ::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float));
-    render_grabbers();
+    render_grabbers(box);
 }
 
 void GLGizmoRotate::transform_to_local() const
@@ -829,7 +833,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
             render_grabbers_connection(4, 5);
         }
         // draw grabbers
-        render_grabbers();
+        render_grabbers(m_box);
     }
     else if ((m_hover_id == 0) || (m_hover_id == 1))
     {
@@ -846,8 +850,8 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
         ::glColor3fv(m_grabbers[0].color);
         render_grabbers_connection(0, 1);
         // draw grabbers
-        m_grabbers[0].render(true);
-        m_grabbers[1].render(true);
+        m_grabbers[0].render(true, m_box);
+        m_grabbers[1].render(true, m_box);
     }
     else if ((m_hover_id == 2) || (m_hover_id == 3))
     {
@@ -864,8 +868,8 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
         ::glColor3fv(m_grabbers[2].color);
         render_grabbers_connection(2, 3);
         // draw grabbers
-        m_grabbers[2].render(true);
-        m_grabbers[3].render(true);
+        m_grabbers[2].render(true, m_box);
+        m_grabbers[3].render(true, m_box);
     }
     else if ((m_hover_id == 4) || (m_hover_id == 5))
     {
@@ -882,8 +886,8 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
         ::glColor3fv(m_grabbers[4].color);
         render_grabbers_connection(4, 5);
         // draw grabbers
-        m_grabbers[4].render(true);
-        m_grabbers[5].render(true);
+        m_grabbers[4].render(true, m_box);
+        m_grabbers[5].render(true, m_box);
     }
     else if (m_hover_id >= 6)
     {
@@ -899,7 +903,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
         // draw grabbers
         for (int i = 6; i < 10; ++i)
         {
-            m_grabbers[i].render(true);
+            m_grabbers[i].render(true, m_box);
         }
     }
 }
@@ -908,7 +912,7 @@ void GLGizmoScale3D::on_render_for_picking(const BoundingBoxf3& box) const
 {
     ::glDisable(GL_DEPTH_TEST);
 
-    render_grabbers_for_picking();
+    render_grabbers_for_picking(box);
 }
 
 void GLGizmoScale3D::render_box(const BoundingBoxf3& box) const
@@ -1118,7 +1122,7 @@ void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const
         }
 
         // draw grabbers
-        render_grabbers();
+        render_grabbers(box);
     }
     else
     {
@@ -1130,7 +1134,7 @@ void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const
         ::glEnd();
 
         // draw grabber
-        m_grabbers[m_hover_id].render(true);
+        m_grabbers[m_hover_id].render(true, box);
     }
 }
 
@@ -1138,7 +1142,7 @@ void GLGizmoMove3D::on_render_for_picking(const BoundingBoxf3& box) const
 {
     ::glDisable(GL_DEPTH_TEST);
 
-    render_grabbers_for_picking();
+    render_grabbers_for_picking(box);
 }
 
 double GLGizmoMove3D::calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const
diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp
index 8760b1c7d..d3cad05c6 100644
--- a/xs/src/slic3r/GUI/GLGizmo.hpp
+++ b/xs/src/slic3r/GUI/GLGizmo.hpp
@@ -22,7 +22,8 @@ class GLGizmoBase
 protected:
     struct Grabber
     {
-        static const float HalfSize;
+        static const float SizeFactor;
+        static const float MinHalfSize;
         static const float DraggingScaleFactor;
 
         Vec3d center;
@@ -33,11 +34,11 @@ protected:
 
         Grabber();
 
-        void render(bool hover) const;
-        void render_for_picking() const { render(color, false); }
+        void render(bool hover, const BoundingBoxf3& box) const;
+        void render_for_picking(const BoundingBoxf3& box) const { render(box, color, false); }
 
     private:
-        void render(const float* render_color, bool use_lighting) const;
+        void render(const BoundingBoxf3& box, const float* render_color, bool use_lighting) const;
         void render_face(float half_size) const;
     };
 
@@ -109,8 +110,8 @@ protected:
     virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0;
 
     float picking_color_component(unsigned int id) const;
-    void render_grabbers() const;
-    void render_grabbers_for_picking() const;
+    void render_grabbers(const BoundingBoxf3& box) const;
+    void render_grabbers_for_picking(const BoundingBoxf3& box) const;
 
     void set_tooltip(const std::string& tooltip) const;
     std::string format(float value, unsigned int decimals) const;
@@ -163,7 +164,7 @@ private:
     void render_snap_radii() const;
     void render_reference_radius() const;
     void render_angle() const;
-    void render_grabber() const;
+    void render_grabber(const BoundingBoxf3& box) const;
 
     void transform_to_local() const;
     // returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate