diff --git a/resources/icons/overlay/move_hover.png b/resources/icons/overlay/move_hover.png new file mode 100644 index 000000000..99dc4cf8d Binary files /dev/null and b/resources/icons/overlay/move_hover.png differ diff --git a/resources/icons/overlay/move_off.png b/resources/icons/overlay/move_off.png new file mode 100644 index 000000000..cd4f130e1 Binary files /dev/null and b/resources/icons/overlay/move_off.png differ diff --git a/resources/icons/overlay/move_on.png b/resources/icons/overlay/move_on.png new file mode 100644 index 000000000..7e78e0e6a Binary files /dev/null and b/resources/icons/overlay/move_on.png differ diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index dc2f5dd5a..ed2f8690d 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -313,6 +313,14 @@ void GLVolume::set_select_group_id(const std::string& select_by) select_group_id = composite_id; } +void GLVolume::set_drag_group_id(const std::string& drag_by) +{ + if (drag_by == "object") + drag_group_id = object_idx() * 1000; + else if (drag_by == "instance") + drag_group_id = object_idx() * 1000 + instance_idx(); +} + const Transform3f& GLVolume::world_matrix() const { if (m_world_matrix_dirty) @@ -666,11 +674,7 @@ std::vector GLVolumeCollection::load_object( v.indexed_vertex_array.finalize_geometry(use_VBOs); v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx; v.set_select_group_id(select_by); - if (drag_by == "object") - v.drag_group_id = obj_idx * 1000; - else if (drag_by == "instance") - v.drag_group_id = obj_idx * 1000 + instance_idx; - + v.set_drag_group_id(drag_by); if (!model_volume->modifier) { v.set_convex_hull(model_volume->get_convex_hull()); @@ -963,6 +967,15 @@ void GLVolumeCollection::set_select_by(const std::string& select_by) } } +void GLVolumeCollection::set_drag_by(const std::string& drag_by) +{ + for (GLVolume *vol : this->volumes) + { + if (vol != nullptr) + vol->set_drag_group_id(drag_by); + } +} + std::vector GLVolumeCollection::get_current_print_zs(bool active_only) const { // Collect layer top positions of all volumes. diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index df749d1bb..6db58aa16 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -338,6 +338,7 @@ public: void set_convex_hull(const TriangleMesh& convex_hull); void set_select_group_id(const std::string& select_by); + void set_drag_group_id(const std::string& drag_by); int object_idx() const { return this->composite_id / 1000000; } int volume_idx() const { return (this->composite_id / 1000) % 1000; } @@ -449,6 +450,7 @@ public: void update_colors_by_extruder(const DynamicPrintConfig* config); void set_select_by(const std::string& select_by); + void set_drag_by(const std::string& drag_by); // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection std::vector get_current_print_zs(bool active_only) const; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 94de895b4..b98826a80 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1123,7 +1123,6 @@ const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale; GLCanvas3D::Gizmos::Gizmos() : m_enabled(false) , m_current(Undefined) - , m_dragging(false) { } @@ -1134,7 +1133,16 @@ GLCanvas3D::Gizmos::~Gizmos() bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) { - GLGizmoBase* gizmo = new GLGizmoScale3D(parent); + GLGizmoBase* gizmo = new GLGizmoMove3D(parent); + if (gizmo == nullptr) + return false; + + if (!gizmo->init()) + return false; + + m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); + + gizmo = new GLGizmoScale3D(parent); if (gizmo == nullptr) return false; @@ -1336,12 +1344,12 @@ bool GLCanvas3D::Gizmos::is_running() const bool GLCanvas3D::Gizmos::is_dragging() const { - return m_dragging; + GLGizmoBase* curr = _get_current(); + return (curr != nullptr) ? curr->is_dragging() : false; } void GLCanvas3D::Gizmos::start_dragging(const BoundingBoxf3& box) { - m_dragging = true; GLGizmoBase* curr = _get_current(); if (curr != nullptr) curr->start_dragging(box); @@ -1349,12 +1357,30 @@ void GLCanvas3D::Gizmos::start_dragging(const BoundingBoxf3& box) void GLCanvas3D::Gizmos::stop_dragging() { - m_dragging = false; GLGizmoBase* curr = _get_current(); if (curr != nullptr) curr->stop_dragging(); } +Vec3d GLCanvas3D::Gizmos::get_position() const +{ + if (!m_enabled) + return Vec3d::Zero(); + + GizmosMap::const_iterator it = m_gizmos.find(Move); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_position() : Vec3d::Zero(); +} + +void GLCanvas3D::Gizmos::set_position(const Vec3d& position) +{ + if (!m_enabled) + return; + + GizmosMap::const_iterator it = m_gizmos.find(Move); + if (it != m_gizmos.end()) + reinterpret_cast(it->second)->set_position(position); +} + float GLCanvas3D::Gizmos::get_scale() const { if (!m_enabled) @@ -2143,6 +2169,7 @@ void GLCanvas3D::set_select_by(const std::string& value) void GLCanvas3D::set_drag_by(const std::string& value) { m_drag_by = value; + m_volumes.set_drag_by(value); } const std::string& GLCanvas3D::get_select_by() const @@ -2150,6 +2177,11 @@ const std::string& GLCanvas3D::get_select_by() const return m_select_by; } +const std::string& GLCanvas3D::get_drag_by() const +{ + return m_drag_by; +} + float GLCanvas3D::get_camera_zoom() const { return m_camera.zoom; @@ -2326,6 +2358,7 @@ void GLCanvas3D::update_gizmos_data() ModelInstance* model_instance = model_object->instances[0]; if (model_instance != nullptr) { + m_gizmos.set_position(Vec3d(model_instance->offset(0), model_instance->offset(1), 0.0)); m_gizmos.set_scale(model_instance->scaling_factor); m_gizmos.set_angle_z(model_instance->rotation); m_gizmos.set_flattening_data(model_object); @@ -2334,6 +2367,7 @@ void GLCanvas3D::update_gizmos_data() } else { + m_gizmos.set_position(Vec3d::Zero()); m_gizmos.set_scale(1.0f); m_gizmos.set_angle_z(0.0f); m_gizmos.set_flattening_data(nullptr); @@ -3181,6 +3215,18 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) switch (m_gizmos.get_current_type()) { + case Gizmos::Move: + { + // Apply new temporary offset + GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx]; + Vec3d offset = m_gizmos.get_position() - volume->get_offset(); + for (GLVolume* v : volumes) + { + v->set_offset(v->get_offset() + offset); + } + update_position_values(volume->get_offset()); + break; + } case Gizmos::Scale: { // Apply new temporary scale factor @@ -3188,8 +3234,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) for (GLVolume* v : volumes) { v->set_scaling_factor((double)scale_factor); - update_scale_values((double)scale_factor); } + update_scale_values((double)scale_factor); break; } case Gizmos::Rotate: @@ -3199,8 +3245,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) for (GLVolume* v : volumes) { v->set_rotation((double)angle_z); - update_rotation_value((double)angle_z, Z); } + update_rotation_value((double)angle_z, Z); break; } default: @@ -3310,6 +3356,27 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { switch (m_gizmos.get_current_type()) { + case Gizmos::Move: + { + // get all volumes belonging to the same group, if any + std::vector volume_idxs; + int vol_id = m_mouse.drag.gizmo_volume_idx; + int group_id = m_volumes.volumes[vol_id]->select_group_id; + if (group_id == -1) + volume_idxs.push_back(vol_id); + else + { + for (int i = 0; i < (int)m_volumes.volumes.size(); ++i) + { + if (m_volumes.volumes[i]->select_group_id == group_id) + volume_idxs.push_back(i); + } + } + + _on_move(volume_idxs); + + break; + } case Gizmos::Scale: { m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale()); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 55a3075ce..a200e7fdb 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -336,6 +336,7 @@ public: enum EType : unsigned char { Undefined, + Move, Scale, Rotate, Flatten, @@ -347,7 +348,6 @@ public: typedef std::map GizmosMap; GizmosMap m_gizmos; EType m_current; - bool m_dragging; public: Gizmos(); @@ -376,6 +376,9 @@ public: void start_dragging(const BoundingBoxf3& box); void stop_dragging(); + Vec3d get_position() const; + void set_position(const Vec3d& position); + float get_scale() const; void set_scale(float scale); @@ -558,6 +561,7 @@ public: void set_drag_by(const std::string& value); const std::string& get_select_by() const; + const std::string& get_drag_by() const; float get_camera_zoom() const; diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 1d02e1752..17c69749b 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -989,6 +989,157 @@ double GLGizmoScale3D::calc_ratio(unsigned int preferred_plane_id, const Linef3& return ratio; } +const double GLGizmoMove3D::Offset = 10.0; + +GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent) + : GLGizmoBase(parent) + , m_position(Vec3d::Zero()) + , m_starting_drag_position(Vec3d::Zero()) + , m_starting_box_center(Vec3d::Zero()) +{ +} + +bool GLGizmoMove3D::on_init() +{ + std::string path = resources_dir() + "/icons/overlay/"; + + std::string filename = path + "move_off.png"; + if (!m_textures[Off].load_from_file(filename, false)) + return false; + + filename = path + "move_hover.png"; + if (!m_textures[Hover].load_from_file(filename, false)) + return false; + + filename = path + "move_on.png"; + if (!m_textures[On].load_from_file(filename, false)) + return false; + + for (int i = 0; i < 3; ++i) + { + m_grabbers.push_back(Grabber()); + } + + return true; +} + +void GLGizmoMove3D::on_start_dragging(const BoundingBoxf3& box) +{ + if (m_hover_id != -1) + { + m_starting_drag_position = m_grabbers[m_hover_id].center; + m_starting_box_center = box.center(); + } +} + +void GLGizmoMove3D::on_update(const Linef3& mouse_ray) +{ + if (m_hover_id == 0) + m_position(0) = 2.0 * m_starting_box_center(0) + calc_displacement(1, mouse_ray) - m_starting_drag_position(0); + else if (m_hover_id == 1) + m_position(1) = 2.0 * m_starting_box_center(1) + calc_displacement(2, mouse_ray) - m_starting_drag_position(1); + else if (m_hover_id == 2) + m_position(2) = 2.0 * m_starting_box_center(2) + calc_displacement(1, mouse_ray) - m_starting_drag_position(2); +} + +void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const +{ + if (m_grabbers[0].dragging) + set_tooltip("X: " + format(m_position(0), 2)); + else if (m_grabbers[1].dragging) + set_tooltip("Y: " + format(m_position(1), 2)); + else if (m_grabbers[2].dragging) + set_tooltip("Z: " + format(m_position(2), 2)); + + ::glEnable(GL_DEPTH_TEST); + + const Vec3d& center = box.center(); + + // x axis + m_grabbers[0].center = Vec3d(box.max(0) + Offset, center(1), center(2)); + ::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float)); + + // y axis + m_grabbers[1].center = Vec3d(center(0), box.max(1) + Offset, center(2)); + ::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float)); + + // z axis + m_grabbers[2].center = Vec3d(center(0), center(1), box.max(2) + Offset); + ::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float)); + + ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f); + + if (m_hover_id == -1) + { + // draw axes + for (unsigned int i = 0; i < 3; ++i) + { + ::glColor3fv(AXES_COLOR[i]); + ::glBegin(GL_LINES); + ::glVertex3f(center(0), center(1), center(2)); + ::glVertex3f((GLfloat)m_grabbers[i].center(0), (GLfloat)m_grabbers[i].center(1), (GLfloat)m_grabbers[i].center(2)); + ::glEnd(); + } + + // draw grabbers + render_grabbers(); + } + else + { + // draw axis + ::glColor3fv(AXES_COLOR[m_hover_id]); + ::glBegin(GL_LINES); + ::glVertex3f(center(0), center(1), center(2)); + ::glVertex3f((GLfloat)m_grabbers[m_hover_id].center(0), (GLfloat)m_grabbers[m_hover_id].center(1), (GLfloat)m_grabbers[m_hover_id].center(2)); + ::glEnd(); + + // draw grabber + m_grabbers[m_hover_id].render(true); + } +} + +void GLGizmoMove3D::on_render_for_picking(const BoundingBoxf3& box) const +{ + ::glDisable(GL_DEPTH_TEST); + + render_grabbers_for_picking(); +} + +double GLGizmoMove3D::calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const +{ + double displacement = 0.0; + + Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; + double len_starting_vec = starting_vec.norm(); + if (len_starting_vec == 0.0) + return displacement; + + Vec3d starting_vec_dir = starting_vec.normalized(); + Vec3d mouse_dir = mouse_ray.unit_vector(); + + unsigned int plane_id = select_best_plane(mouse_dir, preferred_plane_id); + + switch (plane_id) + { + case 0: + { + displacement = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, m_starting_box_center)); + break; + } + case 1: + { + displacement = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, m_starting_box_center)); + break; + } + case 2: + { + displacement = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, m_starting_box_center)); + break; + } + } + + return displacement; +} GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent) : GLGizmoBase(parent) diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index 0599955ed..a7c688531 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -262,6 +262,31 @@ private: double calc_ratio(unsigned int preferred_plane_id, const Linef3& mouse_ray, const Vec3d& center) const; }; +class GLGizmoMove3D : public GLGizmoBase +{ + static const double Offset; + + Vec3d m_position; + Vec3d m_starting_drag_position; + Vec3d m_starting_box_center; + +public: + explicit GLGizmoMove3D(GLCanvas3D& parent); + + const Vec3d& get_position() const { return m_position; } + void set_position(const Vec3d& position) { m_position = position; } + +protected: + virtual bool on_init(); + virtual void on_start_dragging(const BoundingBoxf3& box); + virtual void on_update(const Linef3& mouse_ray); + virtual void on_render(const BoundingBoxf3& box) const; + virtual void on_render_for_picking(const BoundingBoxf3& box) const; + +private: + double calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const; +}; + class GLGizmoFlatten : public GLGizmoBase { // This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself.