#include "libslic3r/libslic3r.h" #include "GLGizmosManager.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/3DScene.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_ObjectManipulation.hpp" #include #include namespace Slic3r { namespace GUI { #if ENABLE_SVG_ICONS const float GLGizmosManager::Default_Icons_Size = 64; #endif // ENABLE_SVG_ICONS GLGizmosManager::GLGizmosManager() : m_enabled(false) #if ENABLE_SVG_ICONS , m_icons_texture_dirty(true) #endif // ENABLE_SVG_ICONS , m_current(Undefined) #if ENABLE_SVG_ICONS , m_overlay_icons_size(Default_Icons_Size) , m_overlay_scale(1.0f) , m_overlay_border(5.0f) , m_overlay_gap_y(5.0f) , m_tooltip("") { } #else { set_overlay_scale(1.0); } #endif // ENABLE_SVG_ICONS GLGizmosManager::~GLGizmosManager() { reset(); } bool GLGizmosManager::init(GLCanvas3D& parent) { #if !ENABLE_SVG_ICONS m_icons_texture.metadata.filename = "gizmos.png"; m_icons_texture.metadata.icon_size = 64; if (!m_icons_texture.metadata.filename.empty()) { if (!m_icons_texture.texture.load_from_file(resources_dir() + "/icons/" + m_icons_texture.metadata.filename, false)) { reset(); return false; } } #endif // !ENABLE_SVG_ICONS m_background_texture.metadata.filename = "toolbar_background.png"; m_background_texture.metadata.left = 16; m_background_texture.metadata.top = 16; m_background_texture.metadata.right = 16; m_background_texture.metadata.bottom = 16; if (!m_background_texture.metadata.filename.empty()) { if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false)) { reset(); return false; } } #if ENABLE_SVG_ICONS GLGizmoBase* gizmo = new GLGizmoMove3D(parent, "move.svg", 0); #else GLGizmoBase* gizmo = new GLGizmoMove3D(parent, 0); #endif // ENABLE_SVG_ICONS if (gizmo == nullptr) return false; if (!gizmo->init()) return false; m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); #if ENABLE_SVG_ICONS gizmo = new GLGizmoScale3D(parent, "scale.svg", 1); #else gizmo = new GLGizmoScale3D(parent, 1); #endif // ENABLE_SVG_ICONS if (gizmo == nullptr) return false; if (!gizmo->init()) return false; m_gizmos.insert(GizmosMap::value_type(Scale, gizmo)); #if ENABLE_SVG_ICONS gizmo = new GLGizmoRotate3D(parent, "rotate.svg", 2); #else gizmo = new GLGizmoRotate3D(parent, 2); #endif // ENABLE_SVG_ICONS if (gizmo == nullptr) { reset(); return false; } if (!gizmo->init()) { reset(); return false; } m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); #if ENABLE_SVG_ICONS gizmo = new GLGizmoFlatten(parent, "place.svg", 3); #else gizmo = new GLGizmoFlatten(parent, 3); #endif // ENABLE_SVG_ICONS if (gizmo == nullptr) return false; if (!gizmo->init()) { reset(); return false; } m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo)); #if ENABLE_SVG_ICONS gizmo = new GLGizmoCut(parent, "cut.svg", 4); #else gizmo = new GLGizmoCut(parent, 4); #endif // ENABLE_SVG_ICONS if (gizmo == nullptr) return false; if (!gizmo->init()) { reset(); return false; } m_gizmos.insert(GizmosMap::value_type(Cut, gizmo)); #if ENABLE_SVG_ICONS gizmo = new GLGizmoSlaSupports(parent, "sla_supports.svg", 5); #else gizmo = new GLGizmoSlaSupports(parent, 5); #endif // ENABLE_SVG_ICONS if (gizmo == nullptr) return false; if (!gizmo->init()) { reset(); return false; } m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo)); return true; } #if ENABLE_SVG_ICONS void GLGizmosManager::set_overlay_icon_size(float size) { if (m_overlay_icons_size != size) { m_overlay_icons_size = size; m_icons_texture_dirty = true; } } #endif // ENABLE_SVG_ICONS void GLGizmosManager::set_overlay_scale(float scale) { #if ENABLE_SVG_ICONS if (m_overlay_scale != scale) { m_overlay_scale = scale; m_icons_texture_dirty = true; } #else m_overlay_icons_scale = scale; m_overlay_border = 5.0f * scale; m_overlay_gap_y = 5.0f * scale; #endif // ENABLE_SVG_ICONS } void GLGizmosManager::refresh_on_off_state(const Selection& selection) { GizmosMap::iterator it = m_gizmos.find(m_current); if ((it != m_gizmos.end()) && (it->second != nullptr)) { if (!it->second->is_activable(selection)) { it->second->set_state(GLGizmoBase::Off); m_current = Undefined; } } } void GLGizmosManager::reset_all_states() { if (!m_enabled) return; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if (it->second != nullptr) { it->second->set_state(GLGizmoBase::Off); it->second->set_hover_id(-1); } } m_current = Undefined; } void GLGizmosManager::set_hover_id(int id) { if (!m_enabled) return; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second != nullptr) && (it->second->get_state() == GLGizmoBase::On)) it->second->set_hover_id(id); } } void GLGizmosManager::enable_grabber(EType type, unsigned int id, bool enable) { if (!m_enabled) return; GizmosMap::const_iterator it = m_gizmos.find(type); if (it != m_gizmos.end()) { if (enable) it->second->enable_grabber(id); else it->second->disable_grabber(id); } } void GLGizmosManager::update(const Linef3& mouse_ray, const Selection& selection, bool shift_down, const Point* mouse_pos) { if (!m_enabled) return; GLGizmoBase* curr = get_current(); if (curr != nullptr) curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos, shift_down), selection); } void GLGizmosManager::update_data(GLCanvas3D& canvas) { if (!m_enabled) return; const Selection& selection = canvas.get_selection(); bool enable_move_z = !selection.is_wipe_tower(); enable_grabber(Move, 2, enable_move_z); bool enable_scale_xyz = selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier(); for (int i = 0; i < 6; ++i) { enable_grabber(Scale, i, enable_scale_xyz); } if (selection.is_single_full_instance()) { // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); set_scale(volume->get_instance_scaling_factor()); set_rotation(Vec3d::Zero()); ModelObject* model_object = canvas.get_model()->objects[selection.get_object_idx()]; set_flattening_data(model_object); set_sla_support_data(model_object, selection); } else if (selection.is_single_volume() || selection.is_single_modifier()) { const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); set_scale(volume->get_volume_scaling_factor()); set_rotation(Vec3d::Zero()); set_flattening_data(nullptr); set_sla_support_data(nullptr, selection); } else { set_scale(Vec3d::Ones()); set_rotation(Vec3d::Zero()); set_flattening_data(selection.is_from_single_object() ? canvas.get_model()->objects[selection.get_object_idx()] : nullptr); set_sla_support_data(nullptr, selection); } } bool GLGizmosManager::is_running() const { if (!m_enabled) return false; GLGizmoBase* curr = get_current(); return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false; } bool GLGizmosManager::handle_shortcut(int key, const Selection& selection) { if (!m_enabled || selection.is_empty()) return false; EType old_current = m_current; bool handled = false; for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; int it_key = it->second->get_shortcut_key(); if (it->second->is_activable(selection) && ((it_key == key - 64) || (it_key == key - 96))) { if ((it->second->get_state() == GLGizmoBase::On)) { it->second->set_state(GLGizmoBase::Off); m_current = Undefined; handled = true; } else if ((it->second->get_state() == GLGizmoBase::Off)) { it->second->set_state(GLGizmoBase::On); m_current = it->first; handled = true; } } } if (handled && (old_current != Undefined) && (old_current != m_current)) { GizmosMap::const_iterator it = m_gizmos.find(old_current); if (it != m_gizmos.end()) it->second->set_state(GLGizmoBase::Off); } return handled; } bool GLGizmosManager::is_dragging() const { if (!m_enabled) return false; GLGizmoBase* curr = get_current(); return (curr != nullptr) ? curr->is_dragging() : false; } void GLGizmosManager::start_dragging(const Selection& selection) { if (!m_enabled) return; GLGizmoBase* curr = get_current(); if (curr != nullptr) curr->start_dragging(selection); } void GLGizmosManager::stop_dragging() { if (!m_enabled) return; GLGizmoBase* curr = get_current(); if (curr != nullptr) curr->stop_dragging(); } Vec3d GLGizmosManager::get_displacement() 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_displacement() : Vec3d::Zero(); } Vec3d GLGizmosManager::get_scale() const { if (!m_enabled) return Vec3d::Ones(); GizmosMap::const_iterator it = m_gizmos.find(Scale); return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_scale() : Vec3d::Ones(); } void GLGizmosManager::set_scale(const Vec3d& scale) { if (!m_enabled) return; GizmosMap::const_iterator it = m_gizmos.find(Scale); if (it != m_gizmos.end()) reinterpret_cast(it->second)->set_scale(scale); } Vec3d GLGizmosManager::get_rotation() const { if (!m_enabled) return Vec3d::Zero(); GizmosMap::const_iterator it = m_gizmos.find(Rotate); return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_rotation() : Vec3d::Zero(); } void GLGizmosManager::set_rotation(const Vec3d& rotation) { if (!m_enabled) return; GizmosMap::const_iterator it = m_gizmos.find(Rotate); if (it != m_gizmos.end()) reinterpret_cast(it->second)->set_rotation(rotation); } Vec3d GLGizmosManager::get_flattening_normal() const { if (!m_enabled) return Vec3d::Zero(); GizmosMap::const_iterator it = m_gizmos.find(Flatten); return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_normal() : Vec3d::Zero(); } void GLGizmosManager::set_flattening_data(const ModelObject* model_object) { if (!m_enabled) return; GizmosMap::const_iterator it = m_gizmos.find(Flatten); if (it != m_gizmos.end()) reinterpret_cast(it->second)->set_flattening_data(model_object); } void GLGizmosManager::set_sla_support_data(ModelObject* model_object, const Selection& selection) { if (!m_enabled) return; GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); if (it != m_gizmos.end()) reinterpret_cast(it->second)->set_sla_support_data(model_object, selection); } // Returns true if the gizmo used the event to do something, false otherwise. bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down) { if (!m_enabled) return false; GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); if (it != m_gizmos.end()) return reinterpret_cast(it->second)->gizmo_event(action, mouse_position, shift_down); return false; } void GLGizmosManager::render_current_gizmo(const Selection& selection) const { if (!m_enabled) return; GLGizmoBase* curr = get_current(); if (curr != nullptr) curr->render(selection); } void GLGizmosManager::render_current_gizmo_for_picking_pass(const Selection& selection) const { if (!m_enabled) return; GLGizmoBase* curr = get_current(); if (curr != nullptr) curr->render_for_picking(selection); } void GLGizmosManager::render_overlay(const GLCanvas3D& canvas, const Selection& selection) const { if (!m_enabled) return; #if ENABLE_SVG_ICONS if (m_icons_texture_dirty) generate_icons_texture(); #endif // ENABLE_SVG_ICONS glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glPushMatrix()); glsafe(::glLoadIdentity()); do_render_overlay(canvas, selection); glsafe(::glPopMatrix()); } bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) { Point pos(evt.GetX(), evt.GetY()); Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY()); Selection& selection = canvas.get_selection(); int selected_object_idx = selection.get_object_idx(); bool processed = false; // mouse anywhere if (!evt.Dragging() && !evt.Leaving() && !evt.Entering() && (m_mouse_capture.parent != nullptr)) { if (m_mouse_capture.any() && (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())) // prevents loosing selection into the scene if mouse down was done inside the toolbar and mouse up was down outside it processed = true; m_mouse_capture.reset(); } // mouse anywhere if (evt.Moving()) m_tooltip = update_hover_state(canvas, mouse_pos); else if (evt.LeftUp()) m_mouse_capture.left = false; else if (evt.MiddleUp()) m_mouse_capture.middle = false; else if (evt.RightUp()) m_mouse_capture.right = false; else if (evt.Dragging() && m_mouse_capture.any()) // if the button down was done on this toolbar, prevent from dragging into the scene processed = true; if (!overlay_contains_mouse(canvas, mouse_pos)) { // mouse is outside the toolbar m_tooltip = ""; if (evt.LeftDown()) { if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown())) // the gizmo got the event and took some action, there is no need to do anything more processed = true; else if (!selection.is_empty() && grabber_contains_mouse()) { update_data(canvas); selection.start_dragging(); start_dragging(selection); if (m_current == Flatten) { // Rotate the object so the normal points downward: selection.flattening_rotate(get_flattening_normal()); canvas.do_flatten(); wxGetApp().obj_manipul()->update_settings_value(selection); } canvas.set_as_dirty(); processed = true; } } else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::RightDown)) // event was taken care of by the SlaSupports gizmo processed = true; else if (evt.Dragging() && (canvas.get_move_volume_id() != -1) && (m_current == SlaSupports)) // don't allow dragging objects with the Sla gizmo on processed = true; else if (evt.Dragging() && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown())) { // the gizmo got the event and took some action, no need to do anything more here canvas.set_as_dirty(); processed = true; } else if (evt.Dragging() && is_dragging()) { if (!canvas.get_wxglcanvas()->HasCapture()) canvas.get_wxglcanvas()->CaptureMouse(); canvas.set_mouse_as_dragging(); update(canvas.mouse_ray(pos), selection, evt.ShiftDown(), &pos); switch (m_current) { case Move: { // Apply new temporary offset selection.translate(get_displacement()); wxGetApp().obj_manipul()->update_settings_value(selection); break; } case Scale: { // Apply new temporary scale factors selection.scale(get_scale(), evt.AltDown()); wxGetApp().obj_manipul()->update_settings_value(selection); break; } case Rotate: { // Apply new temporary rotations TransformationType transformation_type(TransformationType::World_Relative_Joint); if (evt.AltDown()) transformation_type.set_independent(); selection.rotate(get_rotation(), transformation_type); wxGetApp().obj_manipul()->update_settings_value(selection); break; } default: break; } canvas.set_as_dirty(); processed = true; } else if (evt.LeftUp() && is_dragging()) { switch (m_current) { case Move: { canvas.disable_regenerate_volumes(); canvas.do_move(); break; } case Scale: { canvas.do_scale(); break; } case Rotate: { canvas.do_rotate(); break; } default: break; } stop_dragging(); update_data(canvas); wxGetApp().obj_manipul()->update_settings_value(selection); // Let the platter know that the dragging finished, so a delayed refresh // of the scene with the background processing data should be performed. canvas.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); // updates camera target constraints canvas.refresh_camera_scene_box(); processed = true; } else if (evt.LeftUp() && (m_current == SlaSupports) && !canvas.is_mouse_dragging()) { // in case SLA gizmo is selected, we just pass the LeftUp event and stop processing - neither // object moving or selecting is suppressed in that case gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown()); processed = true; } else if (evt.LeftUp() && (m_current == Flatten) && ((canvas.get_hover_volume_id() != -1) || grabber_contains_mouse())) { // to avoid to loose the selection when user clicks an object while the Flatten gizmo is active processed = true; } } else { // mouse inside toolbar if (evt.LeftDown() || evt.LeftDClick()) { m_mouse_capture.left = true; m_mouse_capture.parent = &canvas; processed = true; if (!selection.is_empty()) { update_on_off_state(canvas, mouse_pos, selection); update_data(canvas); canvas.set_as_dirty(); } } else if (evt.MiddleDown()) { m_mouse_capture.middle = true; m_mouse_capture.parent = &canvas; } else if (evt.RightDown()) { m_mouse_capture.right = true; m_mouse_capture.parent = &canvas; } else if (evt.LeftUp()) processed = true; } return processed; } bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas) { // see include/wx/defs.h enum wxKeyCode int keyCode = evt.GetKeyCode(); int ctrlMask = wxMOD_CONTROL; bool processed = false; if ((evt.GetModifiers() & ctrlMask) != 0) { switch (keyCode) { case WXK_CONTROL_A: { // Sla gizmo selects all support points if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::SelectAll)) processed = true; break; } } } else if (!evt.HasModifiers()) { switch (keyCode) { // key ESC case WXK_ESCAPE: { if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges)) reset_all_states(); processed = true; break; } case WXK_RETURN: { if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::ApplyChanges)) processed = true; break; } #ifdef __APPLE__ case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead. #else /* __APPLE__ */ case WXK_DELETE: #endif /* __APPLE__ */ { if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::Delete)) processed = true; break; } case 'A': case 'a': { if (m_current == SlaSupports) { gizmo_event(SLAGizmoEventType::AutomaticGeneration); // set as processed no matter what's returned by gizmo_event() to avoid the calling canvas to process 'A' as arrange processed = true; } break; } case 'M': case 'm': { if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::ManualEditing)) processed = true; break; } } } if (!processed) { if (handle_shortcut(keyCode, canvas.get_selection())) { update_data(canvas); processed = true; } } if (processed) canvas.set_as_dirty(); return processed; } bool GLGizmosManager::on_key(wxKeyEvent& evt, GLCanvas3D& canvas) { const int keyCode = evt.GetKeyCode(); bool processed = false; if (evt.GetEventType() == wxEVT_KEY_UP) { if ((m_current == SlaSupports) && (keyCode == WXK_SHIFT) && gizmo_event(SLAGizmoEventType::ShiftUp)) // shift has been just released - SLA gizmo might want to close rectangular selection. processed = true; } if (processed) canvas.set_as_dirty(); return processed; } void GLGizmosManager::reset() { for (GizmosMap::value_type& gizmo : m_gizmos) { delete gizmo.second; gizmo.second = nullptr; } m_gizmos.clear(); } void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selection& selection) const { if (m_gizmos.empty()) return; float cnv_w = (float)canvas.get_canvas_size().get_width(); float cnv_h = (float)canvas.get_canvas_size().get_height(); float zoom = canvas.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float height = get_total_overlay_height(); float width = get_total_overlay_width(); #if ENABLE_SVG_ICONS float scaled_border = m_overlay_border * m_overlay_scale * inv_zoom; #else float scaled_border = m_overlay_border * inv_zoom; #endif // ENABLE_SVG_ICONS float top_x = (-0.5f * cnv_w) * inv_zoom; float top_y = (0.5f * height) * inv_zoom; float left = top_x; float top = top_y; float right = left + width * inv_zoom; float bottom = top - height * inv_zoom; // renders background unsigned int bg_tex_id = m_background_texture.texture.get_id(); float bg_tex_width = (float)m_background_texture.texture.get_width(); float bg_tex_height = (float)m_background_texture.texture.get_height(); if ((bg_tex_id != 0) && (bg_tex_width > 0) && (bg_tex_height > 0)) { float inv_bg_tex_width = (bg_tex_width != 0.0f) ? 1.0f / bg_tex_width : 0.0f; float inv_bg_tex_height = (bg_tex_height != 0.0f) ? 1.0f / bg_tex_height : 0.0f; float bg_uv_left = 0.0f; float bg_uv_right = 1.0f; float bg_uv_top = 1.0f; float bg_uv_bottom = 0.0f; float bg_left = left; float bg_right = right; float bg_top = top; float bg_bottom = bottom; float bg_width = right - left; float bg_height = top - bottom; float bg_min_size = std::min(bg_width, bg_height); float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width; float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width; float bg_uv_i_top = 1.0f - (float)m_background_texture.metadata.top * inv_bg_tex_height; float bg_uv_i_bottom = (float)m_background_texture.metadata.bottom * inv_bg_tex_height; float bg_i_left = bg_left + scaled_border; float bg_i_right = bg_right - scaled_border; float bg_i_top = bg_top - scaled_border; float bg_i_bottom = bg_bottom + scaled_border; bg_uv_left = bg_uv_i_left; bg_i_left = bg_left; if ((m_overlay_border > 0) && (bg_uv_top != bg_uv_i_top)) { if (bg_uv_left != bg_uv_i_left) GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } }); GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_top, bg_top, { { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_top }, { bg_uv_i_left, bg_uv_top } }); if (bg_uv_right != bg_uv_i_right) GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } }); } if ((m_overlay_border > 0) && (bg_uv_left != bg_uv_i_left)) GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } }); GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } }); if ((m_overlay_border > 0) && (bg_uv_right != bg_uv_i_right)) GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } }); if ((m_overlay_border > 0) && (bg_uv_bottom != bg_uv_i_bottom)) { if (bg_uv_left != bg_uv_i_left) GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } }); GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_bottom, bg_i_bottom, { { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_right, bg_uv_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom } }); if (bg_uv_right != bg_uv_i_right) GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_bottom, bg_i_bottom, { { bg_uv_i_right, bg_uv_bottom }, { bg_uv_right, bg_uv_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom } }); } } #if ENABLE_SVG_ICONS top_x += scaled_border; top_y -= scaled_border; float scaled_gap_y = m_overlay_gap_y * m_overlay_scale * inv_zoom; float scaled_icons_size = m_overlay_icons_size * m_overlay_scale * inv_zoom; float scaled_stride_y = scaled_icons_size + scaled_gap_y; unsigned int icons_texture_id = m_icons_texture.get_id(); unsigned int tex_width = m_icons_texture.get_width(); unsigned int tex_height = m_icons_texture.get_height(); float inv_tex_width = (tex_width != 0) ? 1.0f / (float)tex_width : 0.0f; float inv_tex_height = (tex_height != 0) ? 1.0f / (float)tex_height : 0.0f; #else top_x += m_overlay_border * inv_zoom; top_y -= m_overlay_border * inv_zoom; float scaled_gap_y = m_overlay_gap_y * inv_zoom; float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale * inv_zoom; unsigned int icons_texture_id = m_icons_texture.texture.get_id(); unsigned int texture_size = m_icons_texture.texture.get_width(); float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f; #endif // ENABLE_SVG_ICONS #if ENABLE_SVG_ICONS if ((icons_texture_id == 0) || (tex_width <= 0) || (tex_height <= 0)) return; #endif // ENABLE_SVG_ICONS for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; unsigned int sprite_id = it->second->get_sprite_id(); GLGizmoBase::EState state = it->second->get_state(); #if ENABLE_SVG_ICONS float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width; float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height; float v_top = sprite_id * v_icon_size; float u_left = state * u_icon_size; float v_bottom = v_top + v_icon_size; float u_right = u_left + u_icon_size; #else float uv_icon_size = (float)m_icons_texture.metadata.icon_size * inv_texture_size; float v_top = sprite_id * uv_icon_size; float u_left = state * uv_icon_size; float v_bottom = v_top + uv_icon_size; float u_right = u_left + uv_icon_size; #endif // ENABLE_SVG_ICONS GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); if (it->second->get_state() == GLGizmoBase::On) { float toolbar_top = (float)cnv_h - canvas.get_view_toolbar_height(); #if ENABLE_SVG_ICONS it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection); #else it->second->render_input_window(2.0f * m_overlay_border + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection); #endif // ENABLE_SVG_ICONS } #if ENABLE_SVG_ICONS top_y -= scaled_stride_y; #else top_y -= (scaled_icons_size + scaled_gap_y); #endif // ENABLE_SVG_ICONS } } float GLGizmosManager::get_total_overlay_height() const { #if ENABLE_SVG_ICONS float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; float scaled_border = m_overlay_border * m_overlay_scale; float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; float scaled_stride_y = scaled_icons_size + scaled_gap_y; float height = 2.0f * scaled_border; #else float height = 2.0f * m_overlay_border; float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; #endif // ENABLE_SVG_ICONS for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; #if ENABLE_SVG_ICONS height += scaled_stride_y; #else height += (scaled_icons_size + m_overlay_gap_y); #endif // ENABLE_SVG_ICONS } #if ENABLE_SVG_ICONS return height - scaled_gap_y; #else return height - m_overlay_gap_y; #endif // ENABLE_SVG_ICONS } float GLGizmosManager::get_total_overlay_width() const { #if ENABLE_SVG_ICONS return (2.0f * m_overlay_border + m_overlay_icons_size) * m_overlay_scale; #else return (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale + 2.0f * m_overlay_border; #endif // ENABLE_SVG_ICONS } GLGizmoBase* GLGizmosManager::get_current() const { GizmosMap::const_iterator it = m_gizmos.find(m_current); return (it != m_gizmos.end()) ? it->second : nullptr; } #if ENABLE_SVG_ICONS bool GLGizmosManager::generate_icons_texture() const { std::string path = resources_dir() + "/icons/"; std::vector filenames; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if (it->second != nullptr) { const std::string& icon_filename = it->second->get_icon_filename(); if (!icon_filename.empty()) filenames.push_back(path + icon_filename); } } std::vector> states; states.push_back(std::make_pair(1, false)); states.push_back(std::make_pair(0, false)); states.push_back(std::make_pair(0, true)); bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, (unsigned int)(m_overlay_icons_size * m_overlay_scale)); if (res) m_icons_texture_dirty = false; return res; } #endif // ENABLE_SVG_ICONS void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection) { if (!m_enabled) return; float cnv_h = (float)canvas.get_canvas_size().get_height(); float height = get_total_overlay_height(); #if ENABLE_SVG_ICONS float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; float scaled_border = m_overlay_border * m_overlay_scale; float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; float scaled_stride_y = scaled_icons_size + scaled_gap_y; float top_y = 0.5f * (cnv_h - height) + scaled_border; #else float top_y = 0.5f * (cnv_h - height) + m_overlay_border; float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; #endif // ENABLE_SVG_ICONS for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; #if ENABLE_SVG_ICONS bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); #else bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); #endif // ENABLE_SVG_ICONS if (it->second->is_activable(selection) && inside) { if ((it->second->get_state() == GLGizmoBase::On)) { it->second->set_state(GLGizmoBase::Hover); m_current = Undefined; } else if ((it->second->get_state() == GLGizmoBase::Hover)) { it->second->set_state(GLGizmoBase::On); m_current = it->first; } } else it->second->set_state(GLGizmoBase::Off); #if ENABLE_SVG_ICONS top_y += scaled_stride_y; #else top_y += (scaled_icons_size + m_overlay_gap_y); #endif // ENABLE_SVG_ICONS } GizmosMap::iterator it = m_gizmos.find(m_current); if ((it != m_gizmos.end()) && (it->second != nullptr) && (it->second->get_state() != GLGizmoBase::On)) it->second->set_state(GLGizmoBase::On); } std::string GLGizmosManager::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos) { std::string name = ""; if (!m_enabled) return name; const Selection& selection = canvas.get_selection(); float cnv_h = (float)canvas.get_canvas_size().get_height(); float height = get_total_overlay_height(); #if ENABLE_SVG_ICONS float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; float scaled_border = m_overlay_border * m_overlay_scale; float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; float scaled_stride_y = scaled_icons_size + scaled_gap_y; float top_y = 0.5f * (cnv_h - height) + scaled_border; #else float top_y = 0.5f * (cnv_h - height) + m_overlay_border; float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; #endif // ENABLE_SVG_ICONS for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; #if ENABLE_SVG_ICONS bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); #else bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); #endif // ENABLE_SVG_ICONS if (inside) name = it->second->get_name(); if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); #if ENABLE_SVG_ICONS top_y += scaled_stride_y; #else top_y += (scaled_icons_size + m_overlay_gap_y); #endif // ENABLE_SVG_ICONS } return name; } bool GLGizmosManager::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const { if (!m_enabled) return false; float cnv_h = (float)canvas.get_canvas_size().get_height(); float height = get_total_overlay_height(); #if ENABLE_SVG_ICONS float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; float scaled_border = m_overlay_border * m_overlay_scale; float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; float scaled_stride_y = scaled_icons_size + scaled_gap_y; float top_y = 0.5f * (cnv_h - height) + scaled_border; #else float top_y = 0.5f * (cnv_h - height) + m_overlay_border; float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; #endif // ENABLE_SVG_ICONS for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; #if ENABLE_SVG_ICONS if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) #else if ((m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) #endif // ENABLE_SVG_ICONS return true; #if ENABLE_SVG_ICONS top_y += scaled_stride_y; #else top_y += (scaled_icons_size + m_overlay_gap_y); #endif // ENABLE_SVG_ICONS } return false; } bool GLGizmosManager::grabber_contains_mouse() const { if (!m_enabled) return false; GLGizmoBase* curr = get_current(); return (curr != nullptr) ? (curr->get_hover_id() != -1) : false; } } // namespace GUI } // namespace Slic3r