1 // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
2 #include "GLGizmoMove.hpp"
3 #include "slic3r/GUI/GLCanvas3D.hpp"
4 
5 #include <GL/glew.h>
6 
7 #include <wx/utils.h>
8 
9 namespace Slic3r {
10 namespace GUI {
11 
12 const double GLGizmoMove3D::Offset = 10.0;
13 
GLGizmoMove3D(GLCanvas3D & parent,const std::string & icon_filename,unsigned int sprite_id)14 GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
15     : GLGizmoBase(parent, icon_filename, sprite_id)
16     , m_displacement(Vec3d::Zero())
17     , m_snap_step(1.0)
18     , m_starting_drag_position(Vec3d::Zero())
19     , m_starting_box_center(Vec3d::Zero())
20     , m_starting_box_bottom_center(Vec3d::Zero())
21     , m_quadric(nullptr)
22 {
23     m_quadric = ::gluNewQuadric();
24     if (m_quadric != nullptr)
25         ::gluQuadricDrawStyle(m_quadric, GLU_FILL);
26 }
27 
~GLGizmoMove3D()28 GLGizmoMove3D::~GLGizmoMove3D()
29 {
30     if (m_quadric != nullptr)
31         ::gluDeleteQuadric(m_quadric);
32 }
33 
get_tooltip() const34 std::string GLGizmoMove3D::get_tooltip() const
35 {
36     const Selection& selection = m_parent.get_selection();
37     bool show_position = selection.is_single_full_instance();
38     const Vec3d& position = selection.get_bounding_box().center();
39 
40     if (m_hover_id == 0 || m_grabbers[0].dragging)
41         return "X: " + format(show_position ? position(0) : m_displacement(0), 2);
42     else if (m_hover_id == 1 || m_grabbers[1].dragging)
43         return "Y: " + format(show_position ? position(1) : m_displacement(1), 2);
44     else if (m_hover_id == 2 || m_grabbers[2].dragging)
45         return "Z: " + format(show_position ? position(2) : m_displacement(2), 2);
46     else
47         return "";
48 }
49 
on_init()50 bool GLGizmoMove3D::on_init()
51 {
52     for (int i = 0; i < 3; ++i)
53     {
54         m_grabbers.push_back(Grabber());
55     }
56 
57     m_shortcut_key = WXK_CONTROL_M;
58 
59     return true;
60 }
61 
on_get_name() const62 std::string GLGizmoMove3D::on_get_name() const
63 {
64     return (_(L("Move")) + " [M]").ToUTF8().data();
65 }
66 
on_is_activable() const67 bool GLGizmoMove3D::on_is_activable() const
68 {
69     return !m_parent.get_selection().is_empty();
70 }
71 
on_start_dragging()72 void GLGizmoMove3D::on_start_dragging()
73 {
74     if (m_hover_id != -1)
75     {
76         m_displacement = Vec3d::Zero();
77         const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
78         m_starting_drag_position = m_grabbers[m_hover_id].center;
79         m_starting_box_center = box.center();
80         m_starting_box_bottom_center = box.center();
81         m_starting_box_bottom_center(2) = box.min(2);
82     }
83 }
84 
on_stop_dragging()85 void GLGizmoMove3D::on_stop_dragging()
86 {
87     m_displacement = Vec3d::Zero();
88 }
89 
on_update(const UpdateData & data)90 void GLGizmoMove3D::on_update(const UpdateData& data)
91 {
92     if (m_hover_id == 0)
93         m_displacement(0) = calc_projection(data);
94     else if (m_hover_id == 1)
95         m_displacement(1) = calc_projection(data);
96     else if (m_hover_id == 2)
97         m_displacement(2) = calc_projection(data);
98 }
99 
on_render() const100 void GLGizmoMove3D::on_render() const
101 {
102     const Selection& selection = m_parent.get_selection();
103 
104     glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
105     glsafe(::glEnable(GL_DEPTH_TEST));
106 
107     const BoundingBoxf3& box = selection.get_bounding_box();
108     const Vec3d& center = box.center();
109 
110     // x axis
111     m_grabbers[0].center = Vec3d(box.max(0) + Offset, center(1), center(2));
112     ::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 4 * sizeof(float));
113 
114     // y axis
115     m_grabbers[1].center = Vec3d(center(0), box.max(1) + Offset, center(2));
116     ::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[1], 4 * sizeof(float));
117 
118     // z axis
119     m_grabbers[2].center = Vec3d(center(0), center(1), box.max(2) + Offset);
120     ::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[2], 4 * sizeof(float));
121 
122     glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
123 
124     if (m_hover_id == -1)
125     {
126         // draw axes
127         for (unsigned int i = 0; i < 3; ++i)
128         {
129             if (m_grabbers[i].enabled)
130             {
131                 glsafe(::glColor4fv(AXES_COLOR[i]));
132                 ::glBegin(GL_LINES);
133                 ::glVertex3dv(center.data());
134                 ::glVertex3dv(m_grabbers[i].center.data());
135                 glsafe(::glEnd());
136             }
137         }
138 
139         // draw grabbers
140         render_grabbers(box);
141         for (unsigned int i = 0; i < 3; ++i)
142         {
143             if (m_grabbers[i].enabled)
144                 render_grabber_extension((Axis)i, box, false);
145         }
146     }
147     else
148     {
149         // draw axis
150         glsafe(::glColor4fv(AXES_COLOR[m_hover_id]));
151         ::glBegin(GL_LINES);
152         ::glVertex3dv(center.data());
153         ::glVertex3dv(m_grabbers[m_hover_id].center.data());
154         glsafe(::glEnd());
155 
156         // draw grabber
157         float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
158         m_grabbers[m_hover_id].render(true, mean_size);
159         render_grabber_extension((Axis)m_hover_id, box, false);
160     }
161 }
162 
on_render_for_picking() const163 void GLGizmoMove3D::on_render_for_picking() const
164 {
165     glsafe(::glDisable(GL_DEPTH_TEST));
166 
167     const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
168     render_grabbers_for_picking(box);
169     render_grabber_extension(X, box, true);
170     render_grabber_extension(Y, box, true);
171     render_grabber_extension(Z, box, true);
172 }
173 
calc_projection(const UpdateData & data) const174 double GLGizmoMove3D::calc_projection(const UpdateData& data) const
175 {
176     double projection = 0.0;
177 
178     Vec3d starting_vec = m_starting_drag_position - m_starting_box_center;
179     double len_starting_vec = starting_vec.norm();
180     if (len_starting_vec != 0.0)
181     {
182         Vec3d mouse_dir = data.mouse_ray.unit_vector();
183         // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
184         // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
185         // in our case plane normal and ray direction are the same (orthogonal view)
186         // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
187         Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
188         // vector from the starting position to the found intersection
189         Vec3d inters_vec = inters - m_starting_drag_position;
190 
191         // finds projection of the vector along the staring direction
192         projection = inters_vec.dot(starting_vec.normalized());
193     }
194 
195     if (wxGetKeyState(WXK_SHIFT))
196         projection = m_snap_step * (double)std::round(projection / m_snap_step);
197 
198     return projection;
199 }
200 
render_grabber_extension(Axis axis,const BoundingBoxf3 & box,bool picking) const201 void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const
202 {
203     if (m_quadric == nullptr)
204         return;
205 
206     float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
207     double size = m_dragging ? (double)m_grabbers[axis].get_dragging_half_size(mean_size) : (double)m_grabbers[axis].get_half_size(mean_size);
208 
209     float color[4];
210     ::memcpy((void*)color, (const void*)m_grabbers[axis].color, 4 * sizeof(float));
211     if (!picking && (m_hover_id != -1))
212     {
213         color[0] = 1.0f - color[0];
214         color[1] = 1.0f - color[1];
215         color[2] = 1.0f - color[2];
216         color[3] = color[3];
217     }
218 
219     if (!picking)
220         glsafe(::glEnable(GL_LIGHTING));
221 
222     glsafe(::glColor4fv(color));
223     glsafe(::glPushMatrix());
224     glsafe(::glTranslated(m_grabbers[axis].center(0), m_grabbers[axis].center(1), m_grabbers[axis].center(2)));
225     if (axis == X)
226         glsafe(::glRotated(90.0, 0.0, 1.0, 0.0));
227     else if (axis == Y)
228         glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0));
229 
230     glsafe(::glTranslated(0.0, 0.0, 2.0 * size));
231     ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
232     ::gluCylinder(m_quadric, 0.75 * size, 0.0, 3.0 * size, 36, 1);
233     ::gluQuadricOrientation(m_quadric, GLU_INSIDE);
234     ::gluDisk(m_quadric, 0.0, 0.75 * size, 36, 1);
235     glsafe(::glPopMatrix());
236 
237     if (!picking)
238         glsafe(::glDisable(GL_LIGHTING));
239 }
240 
241 
242 
243 } // namespace GUI
244 } // namespace Slic3r
245