diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 80f2a5b81..a89a7c8b0 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -79,6 +79,8 @@ #define ENABLE_PROCESS_G2_G3_LINES (1 && ENABLE_2_5_0_ALPHA1) // Enable fix of used filament data exported to gcode file #define ENABLE_USED_FILAMENT_POST_PROCESS (1 && ENABLE_2_5_0_ALPHA1) +// Enable gizmo grabbers to share common models +#define ENABLE_GIZMO_GRABBER_REFACTOR (1 && ENABLE_2_5_0_ALPHA1) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index aacecb6ef..1e0ff6c9e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -1,277 +1,398 @@ -#include "GLGizmoBase.hpp" -#include "slic3r/GUI/GLCanvas3D.hpp" - -#include - -#include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/GUI_ObjectManipulation.hpp" -#if ENABLE_GL_SHADERS_ATTRIBUTES -#include "slic3r/GUI/Plater.hpp" -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - -// TODO: Display tooltips quicker on Linux - -namespace Slic3r { -namespace GUI { - -const float GLGizmoBase::Grabber::SizeFactor = 0.05f; -const float GLGizmoBase::Grabber::MinHalfSize = 1.5f; -const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f; - -void GLGizmoBase::Grabber::render(bool hover, float size) -{ - render(size, hover ? complementary(color) : color, false); -} - -float GLGizmoBase::Grabber::get_half_size(float size) const -{ - return std::max(size * SizeFactor, MinHalfSize); -} - -float GLGizmoBase::Grabber::get_dragging_half_size(float size) const -{ - return get_half_size(size) * DraggingScaleFactor; -} - -void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color, bool picking) -{ -#if ENABLE_GL_SHADERS_ATTRIBUTES - GLShaderProgram* shader = wxGetApp().get_current_shader(); - if (shader == nullptr) - return; -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - - if (!m_cube.is_initialized()) { - // This cannot be done in constructor, OpenGL is not yet - // initialized at that point (on Linux at least). - indexed_triangle_set its = its_make_cube(1., 1., 1.); - its_translate(its, -0.5f * Vec3f::Ones()); -#if ENABLE_LEGACY_OPENGL_REMOVAL - m_cube.init_from(its); -#else - m_cube.init_from(its, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } }); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - } - - const float fullsize = 2.0f * (dragging ? get_dragging_half_size(size) : get_half_size(size)); - -#if ENABLE_LEGACY_OPENGL_REMOVAL - m_cube.set_color(render_color); -#else - m_cube.set_color(-1, render_color); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - -#if ENABLE_GL_SHADERS_ATTRIBUTES - const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d view_model_matrix = camera.get_view_matrix() * matrix * Geometry::assemble_transform(center, angles, fullsize * Vec3d::Ones()); - const Transform3d& projection_matrix = camera.get_projection_matrix(); - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("projection_matrix", projection_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); -#else - glsafe(::glPushMatrix()); - glsafe(::glTranslated(center.x(), center.y(), center.z())); - glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); - glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); - glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); - glsafe(::glScaled(fullsize, fullsize, fullsize)); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - m_cube.render(); -#if !ENABLE_GL_SHADERS_ATTRIBUTES - glsafe(::glPopMatrix()); -#endif // !ENABLE_GL_SHADERS_ATTRIBUTES -} - -GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : m_parent(parent) - , m_group_id(-1) - , m_state(Off) - , m_shortcut_key(0) - , m_icon_filename(icon_filename) - , m_sprite_id(sprite_id) - , m_hover_id(-1) - , m_dragging(false) - , m_imgui(wxGetApp().imgui()) - , m_first_input_window_render(true) - , m_dirty(false) -{ -} - -void GLGizmoBase::set_hover_id(int id) -{ - // do not change hover id during dragging - assert(!m_dragging); - - // allow empty grabbers when not using grabbers but use hover_id - flatten, rotate - if (!m_grabbers.empty() && id >= (int) m_grabbers.size()) - return; - - m_hover_id = id; - on_set_hover_id(); -} - -bool GLGizmoBase::update_items_state() -{ - bool res = m_dirty; - m_dirty = false; - return res; -} - -ColorRGBA GLGizmoBase::picking_color_component(unsigned int id) const -{ - id = BASE_ID - id; - if (m_group_id > -1) - id -= m_group_id; - - return picking_decode(id); -} - -void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const -{ - render_grabbers((float)((box.size().x() + box.size().y() + box.size().z()) / 3.0)); -} - -void GLGizmoBase::render_grabbers(float size) const -{ - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) - return; - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); - for (int i = 0; i < (int)m_grabbers.size(); ++i) { - if (m_grabbers[i].enabled) - m_grabbers[i].render(m_hover_id == i, size); - } - shader->stop_using(); -} - -void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const -{ -#if ENABLE_LEGACY_OPENGL_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader("flat"); - if (shader != nullptr) { - shader->start_using(); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - const float mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 3.0); - - for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) { - if (m_grabbers[i].enabled) { - m_grabbers[i].color = picking_color_component(i); - m_grabbers[i].render_for_picking(mean_size); - } - } -#if ENABLE_LEGACY_OPENGL_REMOVAL - shader->stop_using(); - } -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -} - -// help function to process grabbers -// call start_dragging, stop_dragging, on_dragging -bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { - bool is_dragging_finished = false; - if (mouse_event.Moving()) { - // it should not happen but for sure - assert(!m_dragging); - if (m_dragging) is_dragging_finished = true; - else return false; - } - - if (mouse_event.LeftDown()) { - Selection &selection = m_parent.get_selection(); - if (!selection.is_empty() && m_hover_id != -1 && - (m_grabbers.empty() || m_hover_id < static_cast(m_grabbers.size()))) { - selection.setup_cache(); - - m_dragging = true; - for (auto &grabber : m_grabbers) grabber.dragging = false; - if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size())) - m_grabbers[m_hover_id].dragging = true; - - // prevent change of hover_id during dragging - m_parent.set_mouse_as_dragging(); - on_start_dragging(); - - // Let the plater know that the dragging started - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED)); - m_parent.set_as_dirty(); - return true; - } - } else if (m_dragging) { - // when mouse cursor leave window than finish actual dragging operation - bool is_leaving = mouse_event.Leaving(); - if (mouse_event.Dragging()) { - m_parent.set_mouse_as_dragging(); - Point mouse_coord(mouse_event.GetX(), mouse_event.GetY()); - auto ray = m_parent.mouse_ray(mouse_coord); - UpdateData data(ray, mouse_coord); - - on_dragging(data); - - wxGetApp().obj_manipul()->set_dirty(); - m_parent.set_as_dirty(); - return true; - } else if (mouse_event.LeftUp() || is_leaving || is_dragging_finished) { - for (auto &grabber : m_grabbers) grabber.dragging = false; - m_dragging = false; - - // NOTE: This should be part of GLCanvas3D - // Reset hover_id when leave window - if (is_leaving) m_parent.mouse_up_cleanup(); - - on_stop_dragging(); - - // There is prediction that after draggign, data are changed - // Data are updated twice also by canvas3D::reload_scene. - // Should be fixed. - m_parent.get_gizmos_manager().update_data(); - - wxGetApp().obj_manipul()->set_dirty(); - - // Let the plater know that the dragging finished, so a delayed - // refresh of the scene with the background processing data should - // be performed. - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); - // updates camera target constraints - m_parent.refresh_camera_scene_box(); - return true; - } - } - return false; -} - -std::string GLGizmoBase::format(float value, unsigned int decimals) const -{ - return Slic3r::string_printf("%.*f", decimals, value); -} - -void GLGizmoBase::set_dirty() { - m_dirty = true; -} - -void GLGizmoBase::render_input_window(float x, float y, float bottom_limit) -{ - on_render_input_window(x, y, bottom_limit); - if (m_first_input_window_render) { - // for some reason, the imgui dialogs are not shown on screen in the 1st frame where they are rendered, but show up only with the 2nd rendered frame - // so, we forces another frame rendering the first time the imgui window is shown - m_parent.set_as_dirty(); - m_first_input_window_render = false; - } -} - - - -std::string GLGizmoBase::get_name(bool include_shortcut) const -{ - int key = get_shortcut_key(); - std::string out = on_get_name(); - if (include_shortcut && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z) - out += std::string(" [") + char(int('A') + key - int(WXK_CONTROL_A)) + "]"; - return out; -} - - -} // namespace GUI -} // namespace Slic3r +#include "GLGizmoBase.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" + +#include + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" +#if ENABLE_GL_SHADERS_ATTRIBUTES +#include "slic3r/GUI/Plater.hpp" +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + +// TODO: Display tooltips quicker on Linux + +namespace Slic3r { +namespace GUI { + +const float GLGizmoBase::Grabber::SizeFactor = 0.05f; +const float GLGizmoBase::Grabber::MinHalfSize = 1.5f; +const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f; + +#if ENABLE_GIZMO_GRABBER_REFACTOR +GLModel GLGizmoBase::Grabber::s_cube; +GLModel GLGizmoBase::Grabber::s_cone; + +GLGizmoBase::Grabber::~Grabber() +{ + if (s_cube.is_initialized()) + s_cube.reset(); + + if (s_cone.is_initialized()) + s_cone.reset(); +} +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + +float GLGizmoBase::Grabber::get_half_size(float size) const +{ + return std::max(size * SizeFactor, MinHalfSize); +} + +float GLGizmoBase::Grabber::get_dragging_half_size(float size) const +{ + return get_half_size(size) * DraggingScaleFactor; +} + +void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color, bool picking) +{ +#if ENABLE_GL_SHADERS_ATTRIBUTES + GLShaderProgram* shader = wxGetApp().get_current_shader(); + if (shader == nullptr) + return; +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + +#if ENABLE_GIZMO_GRABBER_REFACTOR + if (!s_cube.is_initialized()) { +#else + if (!m_cube.is_initialized()) { +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + // This cannot be done in constructor, OpenGL is not yet + // initialized at that point (on Linux at least). + indexed_triangle_set its = its_make_cube(1.0, 1.0, 1.0); + its_translate(its, -0.5f * Vec3f::Ones()); +#if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_GIZMO_GRABBER_REFACTOR + s_cube.init_from(its); +#else + m_cube.init_from(its); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#else +#if ENABLE_GIZMO_GRABBER_REFACTOR + s_cube.init_from(its, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } }); +#else + m_cube.init_from(its, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } }); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + } + +#if ENABLE_GIZMO_GRABBER_REFACTOR + if (!s_cone.is_initialized()) + s_cone.init_from(its_make_cone(0.375, 1.5, double(PI) / 18.0)); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + +#if ENABLE_GIZMO_GRABBER_REFACTOR + const float half_size = dragging ? get_dragging_half_size(size) : get_half_size(size); +#else + const float fullsize = 2.0f * (dragging ? get_dragging_half_size(size) : get_half_size(size)); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + +#if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_GIZMO_GRABBER_REFACTOR + s_cube.set_color(render_color); + s_cone.set_color(render_color); +#else + m_cube.set_color(render_color); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#else +#if ENABLE_GIZMO_GRABBER_REFACTOR + s_cube.set_color(-1, render_color); + s_cone.set_color(-1, render_color); +#else + m_cube.set_color(-1, render_color); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); +#if ENABLE_GIZMO_GRABBER_REFACTOR + const Transform3d view_model_matrix = camera.get_view_matrix() * matrix * Geometry::assemble_transform(center, angles, 2.0 * half_size * Vec3d::Ones()); +#else + const Transform3d view_model_matrix = camera.get_view_matrix() * matrix * Geometry::assemble_transform(center, angles, fullsize * Vec3d::Ones()); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + const Transform3d& projection_matrix = camera.get_projection_matrix(); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", projection_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); +#else + glsafe(::glPushMatrix()); + glsafe(::glTranslated(center.x(), center.y(), center.z())); + glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); + glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); + glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); +#if ENABLE_GIZMO_GRABBER_REFACTOR + glsafe(::glScaled(2.0 * half_size, 2.0 * half_size, 2.0 * half_size)); +#else + glsafe(::glScaled(fullsize, fullsize, fullsize)); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#endif // ENABLE_GL_SHADERS_ATTRIBUTES +#if ENABLE_GIZMO_GRABBER_REFACTOR + s_cube.render(); + +#if ENABLE_GL_SHADERS_ATTRIBUTES + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosX)) != 0) { + shader->set_uniform("view_model_matrix", view_model_matrix * Geometry::assemble_transform(Vec3d::UnitX(), Vec3d(0.0, 0.5 * double(PI), 0.0))); + s_cone.render(); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegX)) != 0) { + shader->set_uniform("view_model_matrix", view_model_matrix * Geometry::assemble_transform(-Vec3d::UnitX(), Vec3d(0.0, -0.5 * double(PI), 0.0))); + s_cone.render(); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosY)) != 0) { + shader->set_uniform("view_model_matrix", view_model_matrix * Geometry::assemble_transform(Vec3d::UnitY(), Vec3d(-0.5 * double(PI), 0.0, 0.0))); + s_cone.render(); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegY)) != 0) { + shader->set_uniform("view_model_matrix", view_model_matrix * Geometry::assemble_transform(-Vec3d::UnitY(), Vec3d(0.5 * double(PI), 0.0, 0.0))); + s_cone.render(); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosZ)) != 0) { + shader->set_uniform("view_model_matrix", view_model_matrix * Geometry::assemble_transform(Vec3d::UnitZ())); + s_cone.render(); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegZ)) != 0) { + shader->set_uniform("view_model_matrix", view_model_matrix * Geometry::assemble_transform(-Vec3d::UnitZ(), Vec3d(double(PI), 0.0, 0.0))); + s_cone.render(); + } +#else + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosX)) != 0) { + glsafe(::glPushMatrix()); + glsafe(::glTranslated(1.0, 0.0, 0.0)); + glsafe(::glRotated(0.5 * Geometry::rad2deg(double(PI)), 0.0, 1.0, 0.0)); + s_cone.render(); + glsafe(::glPopMatrix()); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegX)) != 0) { + glsafe(::glPushMatrix()); + glsafe(::glTranslated(-1.0, 0.0, 0.0)); + glsafe(::glRotated(-0.5 * Geometry::rad2deg(double(PI)), 0.0, 1.0, 0.0)); + s_cone.render(); + glsafe(::glPopMatrix()); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosY)) != 0) { + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0.0, 1.0, 0.0)); + glsafe(::glRotated(-0.5 * Geometry::rad2deg(double(PI)), 1.0, 0.0, 0.0)); + s_cone.render(); + glsafe(::glPopMatrix()); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegY)) != 0) { + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0.0, -1.0, 0.0)); + glsafe(::glRotated(0.5 * Geometry::rad2deg(double(PI)), 1.0, 0.0, 0.0)); + s_cone.render(); + glsafe(::glPopMatrix()); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosZ)) != 0) { + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0.0, 0.0, 1.0)); + s_cone.render(); + glsafe(::glPopMatrix()); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegZ)) != 0) { + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0.0, 0.0, -1.0)); + glsafe(::glRotated(Geometry::rad2deg(double(PI)), 1.0, 0.0, 0.0)); + s_cone.render(); + glsafe(::glPopMatrix()); + } +#endif // ENABLE_GL_SHADERS_ATTRIBUTES +#else + m_cube.render(); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#if !ENABLE_GL_SHADERS_ATTRIBUTES + glsafe(::glPopMatrix()); +#endif // !ENABLE_GL_SHADERS_ATTRIBUTES +} + +GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : m_parent(parent) + , m_group_id(-1) + , m_state(Off) + , m_shortcut_key(0) + , m_icon_filename(icon_filename) + , m_sprite_id(sprite_id) + , m_hover_id(-1) + , m_dragging(false) + , m_imgui(wxGetApp().imgui()) + , m_first_input_window_render(true) + , m_dirty(false) +{ +} + +void GLGizmoBase::set_hover_id(int id) +{ + // do not change hover id during dragging + assert(!m_dragging); + + // allow empty grabbers when not using grabbers but use hover_id - flatten, rotate + if (!m_grabbers.empty() && id >= (int) m_grabbers.size()) + return; + + m_hover_id = id; + on_set_hover_id(); +} + +bool GLGizmoBase::update_items_state() +{ + bool res = m_dirty; + m_dirty = false; + return res; +} + +ColorRGBA GLGizmoBase::picking_color_component(unsigned int id) const +{ + id = BASE_ID - id; + if (m_group_id > -1) + id -= m_group_id; + + return picking_decode(id); +} + +void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const +{ + render_grabbers((float)((box.size().x() + box.size().y() + box.size().z()) / 3.0)); +} + +void GLGizmoBase::render_grabbers(float size) const +{ + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + shader->start_using(); + shader->set_uniform("emission_factor", 0.1f); + for (int i = 0; i < (int)m_grabbers.size(); ++i) { + if (m_grabbers[i].enabled) + m_grabbers[i].render(m_hover_id == i, size); + } + shader->stop_using(); +} + +void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const +{ +#if ENABLE_LEGACY_OPENGL_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + const float mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 3.0); + + for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) { + if (m_grabbers[i].enabled) { + m_grabbers[i].color = picking_color_component(i); + m_grabbers[i].render_for_picking(mean_size); + } + } +#if ENABLE_LEGACY_OPENGL_REMOVAL + shader->stop_using(); + } +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +} + +// help function to process grabbers +// call start_dragging, stop_dragging, on_dragging +bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { + bool is_dragging_finished = false; + if (mouse_event.Moving()) { + // it should not happen but for sure + assert(!m_dragging); + if (m_dragging) is_dragging_finished = true; + else return false; + } + + if (mouse_event.LeftDown()) { + Selection &selection = m_parent.get_selection(); + if (!selection.is_empty() && m_hover_id != -1 && + (m_grabbers.empty() || m_hover_id < static_cast(m_grabbers.size()))) { + selection.setup_cache(); + + m_dragging = true; + for (auto &grabber : m_grabbers) grabber.dragging = false; + if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size())) + m_grabbers[m_hover_id].dragging = true; + + // prevent change of hover_id during dragging + m_parent.set_mouse_as_dragging(); + on_start_dragging(); + + // Let the plater know that the dragging started + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED)); + m_parent.set_as_dirty(); + return true; + } + } else if (m_dragging) { + // when mouse cursor leave window than finish actual dragging operation + bool is_leaving = mouse_event.Leaving(); + if (mouse_event.Dragging()) { + m_parent.set_mouse_as_dragging(); + Point mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + auto ray = m_parent.mouse_ray(mouse_coord); + UpdateData data(ray, mouse_coord); + + on_dragging(data); + + wxGetApp().obj_manipul()->set_dirty(); + m_parent.set_as_dirty(); + return true; + } else if (mouse_event.LeftUp() || is_leaving || is_dragging_finished) { + for (auto &grabber : m_grabbers) grabber.dragging = false; + m_dragging = false; + + // NOTE: This should be part of GLCanvas3D + // Reset hover_id when leave window + if (is_leaving) m_parent.mouse_up_cleanup(); + + on_stop_dragging(); + + // There is prediction that after draggign, data are changed + // Data are updated twice also by canvas3D::reload_scene. + // Should be fixed. + m_parent.get_gizmos_manager().update_data(); + + wxGetApp().obj_manipul()->set_dirty(); + + // Let the plater know that the dragging finished, so a delayed + // refresh of the scene with the background processing data should + // be performed. + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); + // updates camera target constraints + m_parent.refresh_camera_scene_box(); + return true; + } + } + return false; +} + +std::string GLGizmoBase::format(float value, unsigned int decimals) const +{ + return Slic3r::string_printf("%.*f", decimals, value); +} + +void GLGizmoBase::set_dirty() { + m_dirty = true; +} + +void GLGizmoBase::render_input_window(float x, float y, float bottom_limit) +{ + on_render_input_window(x, y, bottom_limit); + if (m_first_input_window_render) { + // for some reason, the imgui dialogs are not shown on screen in the 1st frame where they are rendered, but show up only with the 2nd rendered frame + // so, we forces another frame rendering the first time the imgui window is shown + m_parent.set_as_dirty(); + m_first_input_window_render = false; + } +} + + + +std::string GLGizmoBase::get_name(bool include_shortcut) const +{ + int key = get_shortcut_key(); + std::string out = on_get_name(); + if (include_shortcut && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z) + out += std::string(" [") + char(int('A') + key - int(WXK_CONTROL_A)) + "]"; + return out; +} + + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 145320462..f61654183 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -1,215 +1,239 @@ -#ifndef slic3r_GLGizmoBase_hpp_ -#define slic3r_GLGizmoBase_hpp_ - -#include "libslic3r/Point.hpp" -#include "libslic3r/Color.hpp" - -#include "slic3r/GUI/I18N.hpp" -#include "slic3r/GUI/GLModel.hpp" - -#include - -class wxWindow; -class wxMouseEvent; - -namespace Slic3r { - -class BoundingBoxf3; -class Linef3; -class ModelObject; - -namespace GUI { - -static const ColorRGBA DEFAULT_BASE_COLOR = { 0.625f, 0.625f, 0.625f, 1.0f }; -static const ColorRGBA DEFAULT_DRAG_COLOR = ColorRGBA::WHITE(); -static const ColorRGBA DEFAULT_HIGHLIGHT_COLOR = ColorRGBA::ORANGE(); -static const std::array AXES_COLOR = {{ ColorRGBA::X(), ColorRGBA::Y(), ColorRGBA::Z() }}; -static const ColorRGBA CONSTRAINED_COLOR = ColorRGBA::GRAY(); - -class ImGuiWrapper; -class GLCanvas3D; -enum class CommonGizmosDataID; -class CommonGizmosDataPool; - -class GLGizmoBase -{ -public: - // Starting value for ids to avoid clashing with ids used by GLVolumes - // (254 is choosen to leave some space for forward compatibility) - static const unsigned int BASE_ID = 255 * 255 * 254; - -protected: - struct Grabber - { - static const float SizeFactor; - static const float MinHalfSize; - static const float DraggingScaleFactor; - - bool enabled{ true }; - bool dragging{ false }; - Vec3d center{ Vec3d::Zero() }; - Vec3d angles{ Vec3d::Zero() }; -#if ENABLE_GL_SHADERS_ATTRIBUTES - Transform3d matrix{ Transform3d::Identity() }; -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - ColorRGBA color{ ColorRGBA::WHITE() }; - - Grabber() = default; - - void render(bool hover, float size); - void render_for_picking(float size) { render(size, color, true); } - - float get_half_size(float size) const; - float get_dragging_half_size(float size) const; - - private: - void render(float size, const ColorRGBA& render_color, bool picking); - - GLModel m_cube; - }; - -public: - enum EState - { - Off, - On, - Num_States - }; - - struct UpdateData - { - const Linef3& mouse_ray; - const Point& mouse_pos; - - UpdateData(const Linef3& mouse_ray, const Point& mouse_pos) - : mouse_ray(mouse_ray), mouse_pos(mouse_pos) - {} - }; - -protected: - GLCanvas3D& m_parent; - int m_group_id; // TODO: remove only for rotate - EState m_state; - int m_shortcut_key; - std::string m_icon_filename; - unsigned int m_sprite_id; - int m_hover_id; - bool m_dragging; - mutable std::vector m_grabbers; - ImGuiWrapper* m_imgui; - bool m_first_input_window_render; - CommonGizmosDataPool* m_c; -public: - GLGizmoBase(GLCanvas3D& parent, - const std::string& icon_filename, - unsigned int sprite_id); - virtual ~GLGizmoBase() = default; - - bool init() { return on_init(); } - - void load(cereal::BinaryInputArchive& ar) { m_state = On; on_load(ar); } - void save(cereal::BinaryOutputArchive& ar) const { on_save(ar); } - - std::string get_name(bool include_shortcut = true) const; - - EState get_state() const { return m_state; } - void set_state(EState state) { m_state = state; on_set_state(); } - - int get_shortcut_key() const { return m_shortcut_key; } - - const std::string& get_icon_filename() const { return m_icon_filename; } - - bool is_activable() const { return on_is_activable(); } - bool is_selectable() const { return on_is_selectable(); } - CommonGizmosDataID get_requirements() const { return on_get_requirements(); } - virtual bool wants_enter_leave_snapshots() const { return false; } - virtual std::string get_gizmo_entering_text() const { assert(false); return ""; } - virtual std::string get_gizmo_leaving_text() const { assert(false); return ""; } - virtual std::string get_action_snapshot_name() { return _u8L("Gizmo action"); } - void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; } - - unsigned int get_sprite_id() const { return m_sprite_id; } - - int get_hover_id() const { return m_hover_id; } - void set_hover_id(int id); - - bool is_dragging() const { return m_dragging; } - - // returns True when Gizmo changed its state - bool update_items_state(); - - void render() { on_render(); } - void render_for_picking() { on_render_for_picking(); } - void render_input_window(float x, float y, float bottom_limit); - - /// - /// Mouse tooltip text - /// - /// Text to be visible in mouse tooltip - virtual std::string get_tooltip() const { return ""; } - - /// - /// Is called when data (Selection) is changed - /// - virtual void data_changed(){}; - - /// - /// Implement when want to process mouse events in gizmo - /// Click, Right click, move, drag, ... - /// - /// Keep information about mouse click - /// Return True when use the information and don't want to propagate it otherwise False. - virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; } -protected: - virtual bool on_init() = 0; - virtual void on_load(cereal::BinaryInputArchive& ar) {} - virtual void on_save(cereal::BinaryOutputArchive& ar) const {} - virtual std::string on_get_name() const = 0; - virtual void on_set_state() {} - virtual void on_set_hover_id() {} - virtual bool on_is_activable() const { return true; } - virtual bool on_is_selectable() const { return true; } - virtual CommonGizmosDataID on_get_requirements() const { return CommonGizmosDataID(0); } - virtual void on_enable_grabber(unsigned int id) {} - virtual void on_disable_grabber(unsigned int id) {} - - // called inside use_grabbers - virtual void on_start_dragging() {} - virtual void on_stop_dragging() {} - virtual void on_dragging(const UpdateData& data) {} - - virtual void on_render() = 0; - virtual void on_render_for_picking() = 0; - virtual void on_render_input_window(float x, float y, float bottom_limit) {} - - // Returns the picking color for the given id, based on the BASE_ID constant - // No check is made for clashing with other picking color (i.e. GLVolumes) - ColorRGBA picking_color_component(unsigned int id) const; - - void render_grabbers(const BoundingBoxf3& box) const; - void render_grabbers(float size) const; - void render_grabbers_for_picking(const BoundingBoxf3& box) const; - - std::string format(float value, unsigned int decimals) const; - - // Mark gizmo as dirty to Re-Render when idle() - void set_dirty(); - - /// - /// function which - /// Set up m_dragging and call functions - /// on_start_dragging / on_dragging / on_stop_dragging - /// - /// Keep information about mouse click - /// same as on_mouse - bool use_grabbers(const wxMouseEvent &mouse_event); -private: - // Flag for dirty visible state of Gizmo - // When True then need new rendering - bool m_dirty; -}; - -} // namespace GUI -} // namespace Slic3r - -#endif // slic3r_GLGizmoBase_hpp_ +#ifndef slic3r_GLGizmoBase_hpp_ +#define slic3r_GLGizmoBase_hpp_ + +#include "libslic3r/Point.hpp" +#include "libslic3r/Color.hpp" + +#include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/GLModel.hpp" + +#include + +class wxWindow; +class wxMouseEvent; + +namespace Slic3r { + +class BoundingBoxf3; +class Linef3; +class ModelObject; + +namespace GUI { + +static const ColorRGBA DEFAULT_BASE_COLOR = { 0.625f, 0.625f, 0.625f, 1.0f }; +static const ColorRGBA DEFAULT_DRAG_COLOR = ColorRGBA::WHITE(); +static const ColorRGBA DEFAULT_HIGHLIGHT_COLOR = ColorRGBA::ORANGE(); +static const std::array AXES_COLOR = {{ ColorRGBA::X(), ColorRGBA::Y(), ColorRGBA::Z() }}; +static const ColorRGBA CONSTRAINED_COLOR = ColorRGBA::GRAY(); + +class ImGuiWrapper; +class GLCanvas3D; +enum class CommonGizmosDataID; +class CommonGizmosDataPool; + +class GLGizmoBase +{ +public: + // Starting value for ids to avoid clashing with ids used by GLVolumes + // (254 is choosen to leave some space for forward compatibility) + static const unsigned int BASE_ID = 255 * 255 * 254; + +#if ENABLE_GIZMO_GRABBER_REFACTOR + enum class EGrabberExtension + { + None = 0, + PosX = 1 << 0, + NegX = 1 << 1, + PosY = 1 << 2, + NegY = 1 << 3, + PosZ = 1 << 4, + NegZ = 1 << 5, + }; +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + +protected: + struct Grabber + { + static const float SizeFactor; + static const float MinHalfSize; + static const float DraggingScaleFactor; + + bool enabled{ true }; + bool dragging{ false }; + Vec3d center{ Vec3d::Zero() }; + Vec3d angles{ Vec3d::Zero() }; +#if ENABLE_GL_SHADERS_ATTRIBUTES + Transform3d matrix{ Transform3d::Identity() }; +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + ColorRGBA color{ ColorRGBA::WHITE() }; +#if ENABLE_GIZMO_GRABBER_REFACTOR + EGrabberExtension extensions{ EGrabberExtension::None }; +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + + Grabber() = default; +#if ENABLE_GIZMO_GRABBER_REFACTOR + ~Grabber(); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + + void render(bool hover, float size) { render(size, hover ? complementary(color) : color, false); } + void render_for_picking(float size) { render(size, color, true); } + + float get_half_size(float size) const; + float get_dragging_half_size(float size) const; + + private: + void render(float size, const ColorRGBA& render_color, bool picking); + +#if ENABLE_GIZMO_GRABBER_REFACTOR + static GLModel s_cube; + static GLModel s_cone; +#else + GLModel m_cube; +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + }; + +public: + enum EState + { + Off, + On, + Num_States + }; + + struct UpdateData + { + const Linef3& mouse_ray; + const Point& mouse_pos; + + UpdateData(const Linef3& mouse_ray, const Point& mouse_pos) + : mouse_ray(mouse_ray), mouse_pos(mouse_pos) + {} + }; + +protected: + GLCanvas3D& m_parent; + int m_group_id; // TODO: remove only for rotate + EState m_state; + int m_shortcut_key; + std::string m_icon_filename; + unsigned int m_sprite_id; + int m_hover_id; + bool m_dragging; + mutable std::vector m_grabbers; + ImGuiWrapper* m_imgui; + bool m_first_input_window_render; + CommonGizmosDataPool* m_c; +public: + GLGizmoBase(GLCanvas3D& parent, + const std::string& icon_filename, + unsigned int sprite_id); + virtual ~GLGizmoBase() = default; + + bool init() { return on_init(); } + + void load(cereal::BinaryInputArchive& ar) { m_state = On; on_load(ar); } + void save(cereal::BinaryOutputArchive& ar) const { on_save(ar); } + + std::string get_name(bool include_shortcut = true) const; + + EState get_state() const { return m_state; } + void set_state(EState state) { m_state = state; on_set_state(); } + + int get_shortcut_key() const { return m_shortcut_key; } + + const std::string& get_icon_filename() const { return m_icon_filename; } + + bool is_activable() const { return on_is_activable(); } + bool is_selectable() const { return on_is_selectable(); } + CommonGizmosDataID get_requirements() const { return on_get_requirements(); } + virtual bool wants_enter_leave_snapshots() const { return false; } + virtual std::string get_gizmo_entering_text() const { assert(false); return ""; } + virtual std::string get_gizmo_leaving_text() const { assert(false); return ""; } + virtual std::string get_action_snapshot_name() { return _u8L("Gizmo action"); } + void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; } + + unsigned int get_sprite_id() const { return m_sprite_id; } + + int get_hover_id() const { return m_hover_id; } + void set_hover_id(int id); + + bool is_dragging() const { return m_dragging; } + + // returns True when Gizmo changed its state + bool update_items_state(); + + void render() { on_render(); } + void render_for_picking() { on_render_for_picking(); } + void render_input_window(float x, float y, float bottom_limit); + + /// + /// Mouse tooltip text + /// + /// Text to be visible in mouse tooltip + virtual std::string get_tooltip() const { return ""; } + + /// + /// Is called when data (Selection) is changed + /// + virtual void data_changed(){}; + + /// + /// Implement when want to process mouse events in gizmo + /// Click, Right click, move, drag, ... + /// + /// Keep information about mouse click + /// Return True when use the information and don't want to propagate it otherwise False. + virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; } +protected: + virtual bool on_init() = 0; + virtual void on_load(cereal::BinaryInputArchive& ar) {} + virtual void on_save(cereal::BinaryOutputArchive& ar) const {} + virtual std::string on_get_name() const = 0; + virtual void on_set_state() {} + virtual void on_set_hover_id() {} + virtual bool on_is_activable() const { return true; } + virtual bool on_is_selectable() const { return true; } + virtual CommonGizmosDataID on_get_requirements() const { return CommonGizmosDataID(0); } + virtual void on_enable_grabber(unsigned int id) {} + virtual void on_disable_grabber(unsigned int id) {} + + // called inside use_grabbers + virtual void on_start_dragging() {} + virtual void on_stop_dragging() {} + virtual void on_dragging(const UpdateData& data) {} + + virtual void on_render() = 0; + virtual void on_render_for_picking() = 0; + virtual void on_render_input_window(float x, float y, float bottom_limit) {} + + // Returns the picking color for the given id, based on the BASE_ID constant + // No check is made for clashing with other picking color (i.e. GLVolumes) + ColorRGBA picking_color_component(unsigned int id) const; + + void render_grabbers(const BoundingBoxf3& box) const; + void render_grabbers(float size) const; + void render_grabbers_for_picking(const BoundingBoxf3& box) const; + + std::string format(float value, unsigned int decimals) const; + + // Mark gizmo as dirty to Re-Render when idle() + void set_dirty(); + + /// + /// function which + /// Set up m_dragging and call functions + /// on_start_dragging / on_dragging / on_stop_dragging + /// + /// Keep information about mouse click + /// same as on_mouse + bool use_grabbers(const wxMouseEvent &mouse_event); +private: + // Flag for dirty visible state of Gizmo + // When True then need new rendering + bool m_dirty; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoBase_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index a798122ec..9dfb8bc9e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -1,331 +1,349 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. -#include "GLGizmoMove.hpp" -#include "slic3r/GUI/GLCanvas3D.hpp" -#include "slic3r/GUI/GUI_App.hpp" -#if ENABLE_GL_SHADERS_ATTRIBUTES -#include "slic3r/GUI/Plater.hpp" -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - -#include - -#include - -namespace Slic3r { -namespace GUI { - -const double GLGizmoMove3D::Offset = 10.0; - -GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) -{} - -std::string GLGizmoMove3D::get_tooltip() const -{ - const Selection& selection = m_parent.get_selection(); - bool show_position = selection.is_single_full_instance(); - const Vec3d& position = selection.get_bounding_box().center(); - - if (m_hover_id == 0 || m_grabbers[0].dragging) - return "X: " + format(show_position ? position(0) : m_displacement(0), 2); - else if (m_hover_id == 1 || m_grabbers[1].dragging) - return "Y: " + format(show_position ? position(1) : m_displacement(1), 2); - else if (m_hover_id == 2 || m_grabbers[2].dragging) - return "Z: " + format(show_position ? position(2) : m_displacement(2), 2); - else - return ""; -} - -bool GLGizmoMove3D::on_mouse(const wxMouseEvent &mouse_event) { - return use_grabbers(mouse_event); -} - -void GLGizmoMove3D::data_changed() { - const Selection &selection = m_parent.get_selection(); - bool is_wipe_tower = selection.is_wipe_tower(); - m_grabbers[2].enabled = !is_wipe_tower; -} - -bool GLGizmoMove3D::on_init() -{ - for (int i = 0; i < 3; ++i) { - m_grabbers.push_back(Grabber()); - } - - m_shortcut_key = WXK_CONTROL_M; - - return true; -} - -std::string GLGizmoMove3D::on_get_name() const -{ - return _u8L("Move"); -} - -bool GLGizmoMove3D::on_is_activable() const -{ - return !m_parent.get_selection().is_empty(); -} - -void GLGizmoMove3D::on_start_dragging() -{ - assert(m_hover_id != -1); - - m_displacement = Vec3d::Zero(); - const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); - m_starting_drag_position = m_grabbers[m_hover_id].center; - m_starting_box_center = box.center(); - m_starting_box_bottom_center = box.center(); - m_starting_box_bottom_center(2) = box.min(2); -} - -void GLGizmoMove3D::on_stop_dragging() -{ - m_parent.do_move(L("Gizmo-Move")); - m_displacement = Vec3d::Zero(); -} - -void GLGizmoMove3D::on_dragging(const UpdateData& data) -{ - if (m_hover_id == 0) - m_displacement.x() = calc_projection(data); - else if (m_hover_id == 1) - m_displacement.y() = calc_projection(data); - else if (m_hover_id == 2) - m_displacement.z() = calc_projection(data); - - Selection &selection = m_parent.get_selection(); - selection.translate(m_displacement); -} - -void GLGizmoMove3D::on_render() -{ - if (!m_cone.is_initialized()) - m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 18.0)); - - const Selection& selection = m_parent.get_selection(); - - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - glsafe(::glEnable(GL_DEPTH_TEST)); - - const BoundingBoxf3& box = selection.get_bounding_box(); - const Vec3d& center = box.center(); - - // x axis - m_grabbers[0].center = { box.max.x() + Offset, center.y(), center.z() }; - m_grabbers[0].color = AXES_COLOR[0]; - - // y axis - m_grabbers[1].center = { center.x(), box.max.y() + Offset, center.z() }; - m_grabbers[1].color = AXES_COLOR[1]; - - // z axis - m_grabbers[2].center = { center.x(), center.y(), box.max.z() + Offset }; - m_grabbers[2].color = AXES_COLOR[2]; - - glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); - -#if ENABLE_LEGACY_OPENGL_REMOVAL - auto render_grabber_connection = [this, ¢er](unsigned int id) { - if (m_grabbers[id].enabled) { - if (!m_grabber_connections[id].model.is_initialized() || !m_grabber_connections[id].old_center.isApprox(center)) { - m_grabber_connections[id].old_center = center; - m_grabber_connections[id].model.reset(); - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = AXES_COLOR[id]; - init_data.reserve_vertices(2); - init_data.reserve_indices(2); - - // vertices - init_data.add_vertex((Vec3f)center.cast()); - init_data.add_vertex((Vec3f)m_grabbers[id].center.cast()); - - // indices - init_data.add_line(0, 1); - - m_grabber_connections[id].model.init_from(std::move(init_data)); - } - - m_grabber_connections[id].model.render(); - } - }; -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - if (m_hover_id == -1) { -#if ENABLE_LEGACY_OPENGL_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader("flat"); - if (shader != nullptr) { - shader->start_using(); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - -#if ENABLE_GL_SHADERS_ATTRIBUTES - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix()); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - - // draw axes - for (unsigned int i = 0; i < 3; ++i) { -#if ENABLE_LEGACY_OPENGL_REMOVAL - render_grabber_connection(i); -#else - if (m_grabbers[i].enabled) { - glsafe(::glColor4fv(AXES_COLOR[i].data())); - ::glBegin(GL_LINES); - ::glVertex3dv(center.data()); - ::glVertex3dv(m_grabbers[i].center.data()); - glsafe(::glEnd()); - } -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - } - -#if ENABLE_LEGACY_OPENGL_REMOVAL - shader->stop_using(); - } -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - // draw grabbers - render_grabbers(box); - for (unsigned int i = 0; i < 3; ++i) { - if (m_grabbers[i].enabled) - render_grabber_extension((Axis)i, box, false); - } - } - else { - // draw axis -#if ENABLE_LEGACY_OPENGL_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader("flat"); - if (shader != nullptr) { - shader->start_using(); - -#if ENABLE_GL_SHADERS_ATTRIBUTES - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix()); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - - render_grabber_connection(m_hover_id); - shader->stop_using(); - } - - shader = wxGetApp().get_shader("gouraud_light"); -#else - glsafe(::glColor4fv(AXES_COLOR[m_hover_id].data())); - ::glBegin(GL_LINES); - ::glVertex3dv(center.data()); - ::glVertex3dv(m_grabbers[m_hover_id].center.data()); - glsafe(::glEnd()); - - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - if (shader != nullptr) { - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); - // draw grabber - const float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); - m_grabbers[m_hover_id].render(true, mean_size); - shader->stop_using(); - } - render_grabber_extension((Axis)m_hover_id, box, false); - } -} - -void GLGizmoMove3D::on_render_for_picking() -{ - glsafe(::glDisable(GL_DEPTH_TEST)); - - const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); - render_grabbers_for_picking(box); - render_grabber_extension(X, box, true); - render_grabber_extension(Y, box, true); - render_grabber_extension(Z, box, true); -} - -double GLGizmoMove3D::calc_projection(const UpdateData& data) const -{ - double projection = 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) { - Vec3d mouse_dir = data.mouse_ray.unit_vector(); - // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position - // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form - // in our case plane normal and ray direction are the same (orthogonal view) - // 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 - Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; - // vector from the starting position to the found intersection - Vec3d inters_vec = inters - m_starting_drag_position; - - // finds projection of the vector along the staring direction - projection = inters_vec.dot(starting_vec.normalized()); - } - - if (wxGetKeyState(WXK_SHIFT)) - projection = m_snap_step * (double)std::round(projection / m_snap_step); - - return projection; -} - -void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) -{ - const float mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 3.0); - const double size = m_dragging ? double(m_grabbers[axis].get_dragging_half_size(mean_size)) : double(m_grabbers[axis].get_half_size(mean_size)); - -#if ENABLE_LEGACY_OPENGL_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader(picking ? "flat" : "gouraud_light"); -#else - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - if (shader == nullptr) - return; - -#if ENABLE_LEGACY_OPENGL_REMOVAL - m_cone.set_color((!picking && m_hover_id != -1) ? complementary(m_grabbers[axis].color) : m_grabbers[axis].color); - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); -#else - m_cone.set_color(-1, (!picking && m_hover_id != -1) ? complementary(m_grabbers[axis].color) : m_grabbers[axis].color); - if (!picking) { - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); - } -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - -#if ENABLE_GL_SHADERS_ATTRIBUTES - const Camera& camera = wxGetApp().plater()->get_camera(); - Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform(m_grabbers[axis].center); - if (axis == X) - view_model_matrix = view_model_matrix * Geometry::assemble_transform(Vec3d::Zero(), 0.5 * PI * Vec3d::UnitY()); - else if (axis == Y) - view_model_matrix = view_model_matrix * Geometry::assemble_transform(Vec3d::Zero(), -0.5 * PI * Vec3d::UnitX()); - view_model_matrix = view_model_matrix * Geometry::assemble_transform(2.0 * size * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(0.75 * size, 0.75 * size, 3.0 * size)); - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); -#else - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_grabbers[axis].center.x(), m_grabbers[axis].center.y(), m_grabbers[axis].center.z())); - if (axis == X) - glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); - else if (axis == Y) - glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); - - glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); - glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - m_cone.render(); -#if !ENABLE_GL_SHADERS_ATTRIBUTES - glsafe(::glPopMatrix()); -#endif // !ENABLE_GL_SHADERS_ATTRIBUTES - -#if !ENABLE_LEGACY_OPENGL_REMOVAL - if (! picking) -#endif // !ENABLE_LEGACY_OPENGL_REMOVAL - shader->stop_using(); -} - -} // namespace GUI -} // namespace Slic3r +// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +#include "GLGizmoMove.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#if ENABLE_GL_SHADERS_ATTRIBUTES +#include "slic3r/GUI/Plater.hpp" +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + +#include + +#include + +namespace Slic3r { +namespace GUI { + +const double GLGizmoMove3D::Offset = 10.0; + +GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoBase(parent, icon_filename, sprite_id) +{} + +std::string GLGizmoMove3D::get_tooltip() const +{ + const Selection& selection = m_parent.get_selection(); + bool show_position = selection.is_single_full_instance(); + const Vec3d& position = selection.get_bounding_box().center(); + + if (m_hover_id == 0 || m_grabbers[0].dragging) + return "X: " + format(show_position ? position(0) : m_displacement(0), 2); + else if (m_hover_id == 1 || m_grabbers[1].dragging) + return "Y: " + format(show_position ? position(1) : m_displacement(1), 2); + else if (m_hover_id == 2 || m_grabbers[2].dragging) + return "Z: " + format(show_position ? position(2) : m_displacement(2), 2); + else + return ""; +} + +bool GLGizmoMove3D::on_mouse(const wxMouseEvent &mouse_event) { + return use_grabbers(mouse_event); +} + +void GLGizmoMove3D::data_changed() { + const Selection &selection = m_parent.get_selection(); + bool is_wipe_tower = selection.is_wipe_tower(); + m_grabbers[2].enabled = !is_wipe_tower; +} + +bool GLGizmoMove3D::on_init() +{ + for (int i = 0; i < 3; ++i) { + m_grabbers.push_back(Grabber()); +#if ENABLE_GIZMO_GRABBER_REFACTOR + m_grabbers.back().extensions = GLGizmoBase::EGrabberExtension::PosZ; +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + } + +#if ENABLE_GIZMO_GRABBER_REFACTOR + m_grabbers[0].angles = { 0.0, 0.5 * double(PI), 0.0 }; + m_grabbers[1].angles = { -0.5 * double(PI), 0.0, 0.0 }; +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + + m_shortcut_key = WXK_CONTROL_M; + + return true; +} + +std::string GLGizmoMove3D::on_get_name() const +{ + return _u8L("Move"); +} + +bool GLGizmoMove3D::on_is_activable() const +{ + return !m_parent.get_selection().is_empty(); +} + +void GLGizmoMove3D::on_start_dragging() +{ + assert(m_hover_id != -1); + + m_displacement = Vec3d::Zero(); + const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); + m_starting_drag_position = m_grabbers[m_hover_id].center; + m_starting_box_center = box.center(); + m_starting_box_bottom_center = box.center(); + m_starting_box_bottom_center(2) = box.min(2); +} + +void GLGizmoMove3D::on_stop_dragging() +{ + m_parent.do_move(L("Gizmo-Move")); + m_displacement = Vec3d::Zero(); +} + +void GLGizmoMove3D::on_dragging(const UpdateData& data) +{ + if (m_hover_id == 0) + m_displacement.x() = calc_projection(data); + else if (m_hover_id == 1) + m_displacement.y() = calc_projection(data); + else if (m_hover_id == 2) + m_displacement.z() = calc_projection(data); + + Selection &selection = m_parent.get_selection(); + selection.translate(m_displacement); +} + +void GLGizmoMove3D::on_render() +{ +#if !ENABLE_GIZMO_GRABBER_REFACTOR + if (!m_cone.is_initialized()) + m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 18.0)); +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR + + const Selection& selection = m_parent.get_selection(); + + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + const BoundingBoxf3& box = selection.get_bounding_box(); + const Vec3d& center = box.center(); + + // x axis + m_grabbers[0].center = { box.max.x() + Offset, center.y(), center.z() }; + m_grabbers[0].color = AXES_COLOR[0]; + + // y axis + m_grabbers[1].center = { center.x(), box.max.y() + Offset, center.z() }; + m_grabbers[1].color = AXES_COLOR[1]; + + // z axis + m_grabbers[2].center = { center.x(), center.y(), box.max.z() + Offset }; + m_grabbers[2].color = AXES_COLOR[2]; + + glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); + +#if ENABLE_LEGACY_OPENGL_REMOVAL + auto render_grabber_connection = [this, ¢er](unsigned int id) { + if (m_grabbers[id].enabled) { + if (!m_grabber_connections[id].model.is_initialized() || !m_grabber_connections[id].old_center.isApprox(center)) { + m_grabber_connections[id].old_center = center; + m_grabber_connections[id].model.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = AXES_COLOR[id]; + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex((Vec3f)center.cast()); + init_data.add_vertex((Vec3f)m_grabbers[id].center.cast()); + + // indices + init_data.add_line(0, 1); + + m_grabber_connections[id].model.init_from(std::move(init_data)); + } + + m_grabber_connections[id].model.render(); + } + }; +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + + if (m_hover_id == -1) { +#if ENABLE_LEGACY_OPENGL_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + + // draw axes + for (unsigned int i = 0; i < 3; ++i) { +#if ENABLE_LEGACY_OPENGL_REMOVAL + render_grabber_connection(i); +#else + if (m_grabbers[i].enabled) { + glsafe(::glColor4fv(AXES_COLOR[i].data())); + ::glBegin(GL_LINES); + ::glVertex3dv(center.data()); + ::glVertex3dv(m_grabbers[i].center.data()); + glsafe(::glEnd()); + } +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + } + +#if ENABLE_LEGACY_OPENGL_REMOVAL + shader->stop_using(); + } +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + + // draw grabbers + render_grabbers(box); +#if !ENABLE_GIZMO_GRABBER_REFACTOR + for (unsigned int i = 0; i < 3; ++i) { + if (m_grabbers[i].enabled) + render_grabber_extension((Axis)i, box, false); + } +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR + } + else { + // draw axis +#if ENABLE_LEGACY_OPENGL_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + + render_grabber_connection(m_hover_id); + shader->stop_using(); + } + + shader = wxGetApp().get_shader("gouraud_light"); +#else + glsafe(::glColor4fv(AXES_COLOR[m_hover_id].data())); + ::glBegin(GL_LINES); + ::glVertex3dv(center.data()); + ::glVertex3dv(m_grabbers[m_hover_id].center.data()); + glsafe(::glEnd()); + + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + if (shader != nullptr) { + shader->start_using(); + shader->set_uniform("emission_factor", 0.1f); + // draw grabber + const float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); + m_grabbers[m_hover_id].render(true, mean_size); + shader->stop_using(); + } +#if !ENABLE_GIZMO_GRABBER_REFACTOR + render_grabber_extension((Axis)m_hover_id, box, false); +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR + } +} + +void GLGizmoMove3D::on_render_for_picking() +{ + glsafe(::glDisable(GL_DEPTH_TEST)); + + const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); + render_grabbers_for_picking(box); +#if !ENABLE_GIZMO_GRABBER_REFACTOR + render_grabber_extension(X, box, true); + render_grabber_extension(Y, box, true); + render_grabber_extension(Z, box, true); +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR +} + +double GLGizmoMove3D::calc_projection(const UpdateData& data) const +{ + double projection = 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) { + Vec3d mouse_dir = data.mouse_ray.unit_vector(); + // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position + // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form + // in our case plane normal and ray direction are the same (orthogonal view) + // 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 + Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + // vector from the starting position to the found intersection + Vec3d inters_vec = inters - m_starting_drag_position; + + // finds projection of the vector along the staring direction + projection = inters_vec.dot(starting_vec.normalized()); + } + + if (wxGetKeyState(WXK_SHIFT)) + projection = m_snap_step * (double)std::round(projection / m_snap_step); + + return projection; +} + +#if !ENABLE_GIZMO_GRABBER_REFACTOR +void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) +{ + const float mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 3.0); + const double size = m_dragging ? double(m_grabbers[axis].get_dragging_half_size(mean_size)) : double(m_grabbers[axis].get_half_size(mean_size)); + +#if ENABLE_LEGACY_OPENGL_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader(picking ? "flat" : "gouraud_light"); +#else + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + if (shader == nullptr) + return; + +#if ENABLE_LEGACY_OPENGL_REMOVAL + m_cone.set_color((!picking && m_hover_id != -1) ? complementary(m_grabbers[axis].color) : m_grabbers[axis].color); + shader->start_using(); + shader->set_uniform("emission_factor", 0.1f); +#else + m_cone.set_color(-1, (!picking && m_hover_id != -1) ? complementary(m_grabbers[axis].color) : m_grabbers[axis].color); + if (!picking) { + shader->start_using(); + shader->set_uniform("emission_factor", 0.1f); + } +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); + Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform(m_grabbers[axis].center); + if (axis == X) + view_model_matrix = view_model_matrix * Geometry::assemble_transform(Vec3d::Zero(), 0.5 * PI * Vec3d::UnitY()); + else if (axis == Y) + view_model_matrix = view_model_matrix * Geometry::assemble_transform(Vec3d::Zero(), -0.5 * PI * Vec3d::UnitX()); + view_model_matrix = view_model_matrix * Geometry::assemble_transform(2.0 * size * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(0.75 * size, 0.75 * size, 3.0 * size)); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); +#else + glsafe(::glPushMatrix()); + glsafe(::glTranslated(m_grabbers[axis].center.x(), m_grabbers[axis].center.y(), m_grabbers[axis].center.z())); + if (axis == X) + glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); + else if (axis == Y) + glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); + + glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); + glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + m_cone.render(); +#if !ENABLE_GL_SHADERS_ATTRIBUTES + glsafe(::glPopMatrix()); +#endif // !ENABLE_GL_SHADERS_ATTRIBUTES + +#if !ENABLE_LEGACY_OPENGL_REMOVAL + if (! picking) +#endif // !ENABLE_LEGACY_OPENGL_REMOVAL + shader->stop_using(); +} +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 92729c199..6a618c3e4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -1,70 +1,73 @@ -#ifndef slic3r_GLGizmoMove_hpp_ -#define slic3r_GLGizmoMove_hpp_ - -#include "GLGizmoBase.hpp" - - -namespace Slic3r { -namespace GUI { - -class GLGizmoMove3D : public GLGizmoBase -{ - static const double Offset; - - Vec3d m_displacement{ Vec3d::Zero() }; - double m_snap_step{ 1.0 }; - Vec3d m_starting_drag_position{ Vec3d::Zero() }; - Vec3d m_starting_box_center{ Vec3d::Zero() }; - Vec3d m_starting_box_bottom_center{ Vec3d::Zero() }; - - GLModel m_cone; -#if ENABLE_LEGACY_OPENGL_REMOVAL - struct GrabberConnection - { - GLModel model; - Vec3d old_center{ Vec3d::Zero() }; - }; - std::array m_grabber_connections; -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - -public: - GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - virtual ~GLGizmoMove3D() = default; - - double get_snap_step(double step) const { return m_snap_step; } - void set_snap_step(double step) { m_snap_step = step; } - - std::string get_tooltip() const override; - - /// - /// Postpone to Grabber for move - /// - /// Keep information about mouse click - /// Return True when use the information otherwise False. - bool on_mouse(const wxMouseEvent &mouse_event) override; - - /// - /// Detect reduction of move for wipetover on selection change - /// - void data_changed() override; -protected: - bool on_init() override; - std::string on_get_name() const override; - bool on_is_activable() const override; - void on_start_dragging() override; - void on_stop_dragging() override; - void on_dragging(const UpdateData& data) override; - void on_render() override; - void on_render_for_picking() override; - -private: - double calc_projection(const UpdateData& data) const; - void render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking); -}; - - - -} // namespace GUI -} // namespace Slic3r - -#endif // slic3r_GLGizmoMove_hpp_ +#ifndef slic3r_GLGizmoMove_hpp_ +#define slic3r_GLGizmoMove_hpp_ + +#include "GLGizmoBase.hpp" + + +namespace Slic3r { +namespace GUI { + +class GLGizmoMove3D : public GLGizmoBase +{ + static const double Offset; + + Vec3d m_displacement{ Vec3d::Zero() }; + double m_snap_step{ 1.0 }; + Vec3d m_starting_drag_position{ Vec3d::Zero() }; + Vec3d m_starting_box_center{ Vec3d::Zero() }; + Vec3d m_starting_box_bottom_center{ Vec3d::Zero() }; + +#if !ENABLE_GIZMO_GRABBER_REFACTOR + GLModel m_cone; +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR +#if ENABLE_LEGACY_OPENGL_REMOVAL + struct GrabberConnection + { + GLModel model; + Vec3d old_center{ Vec3d::Zero() }; + }; + std::array m_grabber_connections; +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + +public: + GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + virtual ~GLGizmoMove3D() = default; + + double get_snap_step(double step) const { return m_snap_step; } + void set_snap_step(double step) { m_snap_step = step; } + + std::string get_tooltip() const override; + + /// + /// Postpone to Grabber for move + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + + /// + /// Detect reduction of move for wipetover on selection change + /// + void data_changed() override; + +protected: + bool on_init() override; + std::string on_get_name() const override; + bool on_is_activable() const override; + void on_start_dragging() override; + void on_stop_dragging() override; + void on_dragging(const UpdateData& data) override; + void on_render() override; + void on_render_for_picking() override; + +private: + double calc_projection(const UpdateData& data) const; +#if !ENABLE_GIZMO_GRABBER_REFACTOR + void render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking); +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoMove_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 6bec8be50..b4b9e0777 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -1,913 +1,924 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. -#include "GLGizmoRotate.hpp" -#include "slic3r/GUI/GLCanvas3D.hpp" -#include "slic3r/GUI/ImGuiWrapper.hpp" - -#include - -#include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/GUI.hpp" -#include "slic3r/GUI/Plater.hpp" -#include "libslic3r/PresetBundle.hpp" - -#include "slic3r/GUI/Jobs/RotoptimizeJob.hpp" - -namespace Slic3r { -namespace GUI { - - -const float GLGizmoRotate::Offset = 5.0f; -const unsigned int GLGizmoRotate::AngleResolution = 64; -const unsigned int GLGizmoRotate::ScaleStepsCount = 72; -const float GLGizmoRotate::ScaleStepRad = 2.0f * float(PI) / GLGizmoRotate::ScaleStepsCount; -const unsigned int GLGizmoRotate::ScaleLongEvery = 2; -const float GLGizmoRotate::ScaleLongTooth = 0.1f; // in percent of radius -const unsigned int GLGizmoRotate::SnapRegionsCount = 8; -const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius - -GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) - : GLGizmoBase(parent, "", -1) - , m_axis(axis) - , m_angle(0.0) - , m_center(0.0, 0.0, 0.0) - , m_radius(0.0f) - , m_snap_coarse_in_radius(0.0f) - , m_snap_coarse_out_radius(0.0f) - , m_snap_fine_in_radius(0.0f) - , m_snap_fine_out_radius(0.0f) - , m_drag_color(DEFAULT_DRAG_COLOR) - , m_highlight_color(DEFAULT_HIGHLIGHT_COLOR) -{ - m_group_id = static_cast(axis); -} - -void GLGizmoRotate::set_highlight_color(const ColorRGBA &color) -{ - m_highlight_color = color; -} - -void GLGizmoRotate::set_angle(double angle) -{ - if (std::abs(angle - 2.0 * double(PI)) < EPSILON) - angle = 0.0; - - m_angle = angle; -} - -std::string GLGizmoRotate::get_tooltip() const -{ - std::string axis; - switch (m_axis) - { - case X: { axis = "X"; break; } - case Y: { axis = "Y"; break; } - case Z: { axis = "Z"; break; } - } - return (m_hover_id == 0 || m_grabbers.front().dragging) ? axis + ": " + format(float(Geometry::rad2deg(m_angle)), 4) : ""; -} - -bool GLGizmoRotate::on_mouse(const wxMouseEvent &mouse_event) -{ - return use_grabbers(mouse_event); -} - -void GLGizmoRotate::dragging(const UpdateData &data) { on_dragging(data); } - -void GLGizmoRotate::start_dragging() -{ - m_grabbers[0].dragging = true; - on_start_dragging(); -} - -void GLGizmoRotate::stop_dragging() -{ - m_grabbers[0].dragging = false; - on_stop_dragging(); -} - -void GLGizmoRotate::enable_grabber() { m_grabbers[0].enabled = true; } -void GLGizmoRotate::disable_grabber() { m_grabbers[0].enabled = false; } - -bool GLGizmoRotate::on_init() -{ - m_grabbers.push_back(Grabber()); - return true; -} - -void GLGizmoRotate::on_start_dragging() -{ - const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); - m_center = box.center(); - m_radius = Offset + box.radius(); - m_snap_coarse_in_radius = m_radius / 3.0f; - m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; - m_snap_fine_in_radius = m_radius; - m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth; -} - -void GLGizmoRotate::on_dragging(const UpdateData &data) -{ - const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, m_parent.get_selection())); - - const Vec2d orig_dir = Vec2d::UnitX(); - const Vec2d new_dir = mouse_pos.normalized(); - - double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); - if (cross2(orig_dir, new_dir) < 0.0) - theta = 2.0 * (double)PI - theta; - - const double len = mouse_pos.norm(); - - // snap to coarse snap region - if (m_snap_coarse_in_radius <= len && len <= m_snap_coarse_out_radius) { - const double step = 2.0 * double(PI) / double(SnapRegionsCount); - theta = step * std::round(theta / step); - } - else { - // snap to fine snap region (scale) - if (m_snap_fine_in_radius <= len && len <= m_snap_fine_out_radius) { - const double step = 2.0 * double(PI) / double(ScaleStepsCount); - theta = step * std::round(theta / step); - } - } - - if (theta == 2.0 * double(PI)) - theta = 0.0; - - m_angle = theta; -} - -void GLGizmoRotate::on_render() -{ - if (!m_grabbers.front().enabled) - return; - - if (!m_cone.is_initialized()) - m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 12.0)); - - const Selection& selection = m_parent.get_selection(); - const BoundingBoxf3& box = selection.get_bounding_box(); - - if (m_hover_id != 0 && !m_grabbers.front().dragging) { - m_center = box.center(); - m_radius = Offset + box.radius(); - m_snap_coarse_in_radius = m_radius / 3.0f; - m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; - m_snap_fine_in_radius = m_radius; - m_snap_fine_out_radius = m_radius * (1.0f + ScaleLongTooth); - } - - const double grabber_radius = (double)m_radius * (1.0 + (double)GrabberOffset); - m_grabbers.front().center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0); - m_grabbers.front().angles.z() = m_angle; - - glsafe(::glEnable(GL_DEPTH_TEST)); - -#if ENABLE_GL_SHADERS_ATTRIBUTES - m_grabbers.front().matrix = local_transform(selection); -#else - glsafe(::glPushMatrix()); - transform_to_local(selection); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - - glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); -#if ENABLE_LEGACY_OPENGL_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader("flat"); - if (shader != nullptr) { - shader->start_using(); - -#if ENABLE_GL_SHADERS_ATTRIBUTES - const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d view_model_matrix = camera.get_view_matrix() * m_grabbers.front().matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - - const bool radius_changed = std::abs(m_old_radius - m_radius) > EPSILON; - m_old_radius = m_radius; - - ColorRGBA color((m_hover_id != -1) ? m_drag_color : m_highlight_color); - render_circle(color, radius_changed); - if (m_hover_id != -1) { - const bool hover_radius_changed = std::abs(m_old_hover_radius - m_radius) > EPSILON; - m_old_hover_radius = m_radius; - - render_scale(color, hover_radius_changed); - render_snap_radii(color, hover_radius_changed); - render_reference_radius(color, hover_radius_changed); - render_angle_arc(m_highlight_color, hover_radius_changed); - } - - render_grabber_connection(color, radius_changed); - shader->stop_using(); - } -#else - glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color.data() : m_highlight_color.data())); - - render_circle(); - - if (m_hover_id != -1) { - render_scale(); - render_snap_radii(); - render_reference_radius(); - } - - glsafe(::glColor4fv(m_highlight_color.data())); - - if (m_hover_id != -1) - render_angle(); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - render_grabber(box); - render_grabber_extension(box, false); - -#if !ENABLE_GL_SHADERS_ATTRIBUTES - glsafe(::glPopMatrix()); -#endif // !ENABLE_GL_SHADERS_ATTRIBUTES -} - -void GLGizmoRotate::on_render_for_picking() -{ - const Selection& selection = m_parent.get_selection(); - - glsafe(::glDisable(GL_DEPTH_TEST)); - -#if ENABLE_GL_SHADERS_ATTRIBUTES - m_grabbers.front().matrix = local_transform(selection); -#else - glsafe(::glPushMatrix()); - transform_to_local(selection); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - - const BoundingBoxf3& box = selection.get_bounding_box(); - render_grabbers_for_picking(box); - render_grabber_extension(box, true); - -#if !ENABLE_GL_SHADERS_ATTRIBUTES - glsafe(::glPopMatrix()); -#endif // !ENABLE_GL_SHADERS_ATTRIBUTES -} - -void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit) -{ - if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) - return; - - RotoptimzeWindow popup{m_imgui, m_rotoptimizewin_state, {x, y, bottom_limit}}; -} - -void GLGizmoRotate3D::load_rotoptimize_state() -{ - std::string accuracy_str = - wxGetApp().app_config->get("sla_auto_rotate", "accuracy"); - - std::string method_str = - wxGetApp().app_config->get("sla_auto_rotate", "method_id"); - - if (!accuracy_str.empty()) { - float accuracy = std::stof(accuracy_str); - accuracy = std::max(0.f, std::min(accuracy, 1.f)); - - m_rotoptimizewin_state.accuracy = accuracy; - } - - if (!method_str.empty()) { - int method_id = std::stoi(method_str); - if (method_id < int(RotoptimizeJob::get_methods_count())) - m_rotoptimizewin_state.method_id = method_id; - } -} - -#if ENABLE_LEGACY_OPENGL_REMOVAL -void GLGizmoRotate::render_circle(const ColorRGBA& color, bool radius_changed) -#else -void GLGizmoRotate::render_circle() const -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -{ -#if ENABLE_LEGACY_OPENGL_REMOVAL - if (!m_circle.is_initialized() || radius_changed) { - m_circle.reset(); - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::LineLoop, GLModel::Geometry::EVertexLayout::P3 }; - init_data.reserve_vertices(ScaleStepsCount); - init_data.reserve_indices(ScaleStepsCount); - - // vertices + indices - for (unsigned int i = 0; i < ScaleStepsCount; ++i) { - const float angle = float(i * ScaleStepRad); - init_data.add_vertex(Vec3f(::cos(angle) * m_radius, ::sin(angle) * m_radius, 0.0f)); - init_data.add_index(i); - } - - m_circle.init_from(std::move(init_data)); - } - - m_circle.set_color(color); - m_circle.render(); -#else - ::glBegin(GL_LINE_LOOP); - for (unsigned int i = 0; i < ScaleStepsCount; ++i) { - float angle = (float)i * ScaleStepRad; - float x = ::cos(angle) * m_radius; - float y = ::sin(angle) * m_radius; - float z = 0.0f; - ::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); - } - glsafe(::glEnd()); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -} - -#if ENABLE_LEGACY_OPENGL_REMOVAL -void GLGizmoRotate::render_scale(const ColorRGBA& color, bool radius_changed) -#else -void GLGizmoRotate::render_scale() const -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -{ - const float out_radius_long = m_snap_fine_out_radius; - const float out_radius_short = m_radius * (1.0f + 0.5f * ScaleLongTooth); - -#if ENABLE_LEGACY_OPENGL_REMOVAL - if (!m_scale.is_initialized() || radius_changed) { - m_scale.reset(); - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - init_data.reserve_vertices(2 * ScaleStepsCount); - init_data.reserve_indices(2 * ScaleStepsCount); - - // vertices + indices - for (unsigned int i = 0; i < ScaleStepsCount; ++i) { - const float angle = float(i * ScaleStepRad); - const float cosa = ::cos(angle); - const float sina = ::sin(angle); - const float in_x = cosa * m_radius; - const float in_y = sina * m_radius; - const float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; - const float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; - - // vertices - init_data.add_vertex(Vec3f(in_x, in_y, 0.0f)); - init_data.add_vertex(Vec3f(out_x, out_y, 0.0f)); - - // indices - init_data.add_line(i * 2, i * 2 + 1); - } - - m_scale.init_from(std::move(init_data)); - } - - m_scale.set_color(color); - m_scale.render(); -#else - ::glBegin(GL_LINES); - for (unsigned int i = 0; i < ScaleStepsCount; ++i) { - const float angle = (float)i * ScaleStepRad; - const float cosa = ::cos(angle); - const float sina = ::sin(angle); - const float in_x = cosa * m_radius; - const float in_y = sina * m_radius; - const float in_z = 0.0f; - const float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; - const float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; - const float out_z = 0.0f; - ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z); - ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z); - } - glsafe(::glEnd()); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -} - -#if ENABLE_LEGACY_OPENGL_REMOVAL -void GLGizmoRotate::render_snap_radii(const ColorRGBA& color, bool radius_changed) -#else -void GLGizmoRotate::render_snap_radii() const -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -{ - const float step = 2.0f * float(PI) / float(SnapRegionsCount); - const float in_radius = m_radius / 3.0f; - const float out_radius = 2.0f * in_radius; - -#if ENABLE_LEGACY_OPENGL_REMOVAL - if (!m_snap_radii.is_initialized() || radius_changed) { - m_snap_radii.reset(); - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - init_data.reserve_vertices(2 * ScaleStepsCount); - init_data.reserve_indices(2 * ScaleStepsCount); - - // vertices + indices - for (unsigned int i = 0; i < ScaleStepsCount; ++i) { - const float angle = float(i * step); - const float cosa = ::cos(angle); - const float sina = ::sin(angle); - const float in_x = cosa * in_radius; - const float in_y = sina * in_radius; - const float out_x = cosa * out_radius; - const float out_y = sina * out_radius; - - // vertices - init_data.add_vertex(Vec3f(in_x, in_y, 0.0f)); - init_data.add_vertex(Vec3f(out_x, out_y, 0.0f)); - - // indices - init_data.add_line(i * 2, i * 2 + 1); - } - - m_snap_radii.init_from(std::move(init_data)); - } - - m_snap_radii.set_color(color); - m_snap_radii.render(); -#else - ::glBegin(GL_LINES); - for (unsigned int i = 0; i < SnapRegionsCount; ++i) { - const float angle = (float)i * step; - const float cosa = ::cos(angle); - const float sina = ::sin(angle); - const float in_x = cosa * in_radius; - const float in_y = sina * in_radius; - const float in_z = 0.0f; - const float out_x = cosa * out_radius; - const float out_y = sina * out_radius; - const float out_z = 0.0f; - ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z); - ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z); - } - glsafe(::glEnd()); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -} - -#if ENABLE_LEGACY_OPENGL_REMOVAL -void GLGizmoRotate::render_reference_radius(const ColorRGBA& color, bool radius_changed) -{ - if (!m_reference_radius.is_initialized() || radius_changed) { - m_reference_radius.reset(); - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - init_data.reserve_vertices(2); - init_data.reserve_indices(2); - - // vertices - init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); - init_data.add_vertex(Vec3f(m_radius * (1.0f + GrabberOffset), 0.0f, 0.0f)); - - // indices - init_data.add_line(0, 1); - - m_reference_radius.init_from(std::move(init_data)); - } - - m_reference_radius.set_color(color); - m_reference_radius.render(); -} -#else -void GLGizmoRotate::render_reference_radius() const -{ - ::glBegin(GL_LINES); - ::glVertex3f(0.0f, 0.0f, 0.0f); - ::glVertex3f((GLfloat)(m_radius * (1.0f + GrabberOffset)), 0.0f, 0.0f); - glsafe(::glEnd()); -} -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - -#if ENABLE_LEGACY_OPENGL_REMOVAL -void GLGizmoRotate::render_angle_arc(const ColorRGBA& color, bool radius_changed) -#else -void GLGizmoRotate::render_angle() const -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -{ - const float step_angle = float(m_angle) / float(AngleResolution); - const float ex_radius = m_radius * (1.0f + GrabberOffset); - -#if ENABLE_LEGACY_OPENGL_REMOVAL - const bool angle_changed = std::abs(m_old_angle - m_angle) > EPSILON; - m_old_angle = m_angle; - - if (!m_angle_arc.is_initialized() || radius_changed || angle_changed) { - m_angle_arc.reset(); - if (m_angle > 0.0f) { - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; - init_data.reserve_vertices(1 + AngleResolution); - init_data.reserve_indices(1 + AngleResolution); - - // vertices + indices - for (unsigned int i = 0; i <= AngleResolution; ++i) { - const float angle = float(i) * step_angle; - init_data.add_vertex(Vec3f(::cos(angle) * ex_radius, ::sin(angle) * ex_radius, 0.0f)); - init_data.add_index(i); - } - - m_angle_arc.init_from(std::move(init_data)); - } - } - - m_angle_arc.set_color(color); - m_angle_arc.render(); -#else - ::glBegin(GL_LINE_STRIP); - for (unsigned int i = 0; i <= AngleResolution; ++i) { - float angle = (float)i * step_angle; - float x = ::cos(angle) * ex_radius; - float y = ::sin(angle) * ex_radius; - float z = 0.0f; - ::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); - } - glsafe(::glEnd()); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -} - -#if ENABLE_LEGACY_OPENGL_REMOVAL -void GLGizmoRotate::render_grabber_connection(const ColorRGBA& color, bool radius_changed) -{ - if (!m_grabber_connection.model.is_initialized() || radius_changed || !m_grabber_connection.old_center.isApprox(m_grabbers.front().center)) { - m_grabber_connection.model.reset(); - m_grabber_connection.old_center = m_grabbers.front().center; - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - init_data.reserve_vertices(2); - init_data.reserve_indices(2); - - // vertices - init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); - init_data.add_vertex((Vec3f)m_grabbers.front().center.cast()); - - // indices - init_data.add_line(0, 1); - - m_grabber_connection.model.init_from(std::move(init_data)); - } - - m_grabber_connection.model.set_color(color); - m_grabber_connection.model.render(); -} -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - -void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) -{ -#if !ENABLE_LEGACY_OPENGL_REMOVAL - const double grabber_radius = double(m_radius) * (1.0 + double(GrabberOffset)); - m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0); - m_grabbers[0].angles.z() = m_angle; - - glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color.data() : m_highlight_color.data())); - - ::glBegin(GL_LINES); - ::glVertex3f(0.0f, 0.0f, 0.0f); - ::glVertex3dv(m_grabbers[0].center.data()); - glsafe(::glEnd()); -#endif // !ENABLE_LEGACY_OPENGL_REMOVAL - - m_grabbers.front().color = m_highlight_color; - render_grabbers(box); -} - -void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool picking) -{ - const float mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 3.0); - const double size = m_dragging ? double(m_grabbers.front().get_dragging_half_size(mean_size)) : double(m_grabbers.front().get_half_size(mean_size)); - -#if ENABLE_LEGACY_OPENGL_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader(picking ? "flat" : "gouraud_light"); - if (shader == nullptr) - return; - - m_cone.set_color((!picking && m_hover_id != -1) ? complementary(m_grabbers.front().color) : m_grabbers.front().color); - - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); -#else - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) - return; - - m_cone.set_color(-1, (!picking && m_hover_id != -1) ? complementary(m_grabbers.front().color) : m_grabbers.front().color); - if (!picking) { - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); - } -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - const Vec3d& center = m_grabbers.front().center; - -#if ENABLE_GL_SHADERS_ATTRIBUTES - const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d& view_matrix = camera.get_view_matrix(); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - - Transform3d view_model_matrix = view_matrix * m_grabbers.front().matrix * - Geometry::assemble_transform(center, Vec3d(0.5 * PI, 0.0, m_angle)) * - Geometry::assemble_transform(2.0 * size * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(0.75 * size, 0.75 * size, 3.0 * size)); - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); -#else - glsafe(::glPushMatrix()); - glsafe(::glTranslated(center.x(), center.y(), center.z())); - glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0)); - glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); - glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); - glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - m_cone.render(); -#if ENABLE_GL_SHADERS_ATTRIBUTES - view_model_matrix = view_matrix * m_grabbers.front().matrix * - Geometry::assemble_transform(center, Vec3d(-0.5 * PI, 0.0, m_angle)) * - Geometry::assemble_transform(2.0 * size * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(0.75 * size, 0.75 * size, 3.0 * size)); - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); -#else - glsafe(::glPopMatrix()); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(center.x(), center.y(), center.z())); - glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0)); - glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); - glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); - glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - m_cone.render(); -#if !ENABLE_GL_SHADERS_ATTRIBUTES - glsafe(::glPopMatrix()); -#endif // !ENABLE_GL_SHADERS_ATTRIBUTES - -#if !ENABLE_LEGACY_OPENGL_REMOVAL - if (! picking) -#endif // !ENABLE_LEGACY_OPENGL_REMOVAL - shader->stop_using(); -} - -#if ENABLE_GL_SHADERS_ATTRIBUTES -Transform3d GLGizmoRotate::local_transform(const Selection& selection) const -{ - Transform3d ret; - - switch (m_axis) - { - case X: - { - ret = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, 0.5 * PI, 0.0)) * Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, 0.0, -0.5 * PI)); - break; - } - case Y: - { - ret = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, 0.0, -0.5 * PI)) * Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, -0.5 * PI, 0.0)); - break; - } - default: - case Z: - { - ret = Transform3d::Identity(); - break; - } - } - - if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) - ret = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true) * ret; - - return Geometry::assemble_transform(m_center) * ret; -} -#else -void GLGizmoRotate::transform_to_local(const Selection& selection) const -{ - glsafe(::glTranslated(m_center.x(), m_center.y(), m_center.z())); - - if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) { - const Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); - glsafe(::glMultMatrixd(orient_matrix.data())); - } - - switch (m_axis) - { - case X: - { - glsafe(::glRotatef(90.0f, 0.0f, 1.0f, 0.0f)); - glsafe(::glRotatef(-90.0f, 0.0f, 0.0f, 1.0f)); - break; - } - case Y: - { - glsafe(::glRotatef(-90.0f, 0.0f, 0.0f, 1.0f)); - glsafe(::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f)); - break; - } - default: - case Z: - { - // no rotation - break; - } - } -} -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - -Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const -{ - double half_pi = 0.5 * double(PI); - - Transform3d m = Transform3d::Identity(); - - switch (m_axis) - { - case X: - { - m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ())); - m.rotate(Eigen::AngleAxisd(-half_pi, Vec3d::UnitY())); - break; - } - case Y: - { - m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitY())); - m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ())); - break; - } - default: - case Z: - { - // no rotation applied - break; - } - } - - if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) - m = m * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true).inverse(); - - m.translate(-m_center); - - return transform(mouse_ray, m).intersect_plane(0.0); -} - -GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) - , m_gizmos({ - GLGizmoRotate(parent, GLGizmoRotate::X), - GLGizmoRotate(parent, GLGizmoRotate::Y), - GLGizmoRotate(parent, GLGizmoRotate::Z) }) -{} - -bool GLGizmoRotate3D::on_mouse(const wxMouseEvent &mouse_event) -{ - if (mouse_event.Dragging() && m_dragging) { - // Apply new temporary rotations - TransformationType transformation_type( - TransformationType::World_Relative_Joint); - if (mouse_event.AltDown()) transformation_type.set_independent(); - m_parent.get_selection().rotate(get_rotation(), transformation_type); - } - return use_grabbers(mouse_event); -} - -void GLGizmoRotate3D::data_changed() { - const Selection &selection = m_parent.get_selection(); - bool is_wipe_tower = selection.is_wipe_tower(); - if (is_wipe_tower) { - DynamicPrintConfig& config = wxGetApp().preset_bundle->prints.get_edited_preset().config; - float wipe_tower_rotation_angle = - dynamic_cast( - config.option("wipe_tower_rotation_angle")) - ->value; - set_rotation(Vec3d(0., 0., (M_PI / 180.) * wipe_tower_rotation_angle)); - m_gizmos[0].disable_grabber(); - m_gizmos[1].disable_grabber(); - } else { - set_rotation(Vec3d::Zero()); - m_gizmos[0].enable_grabber(); - m_gizmos[1].enable_grabber(); - } -} - -bool GLGizmoRotate3D::on_init() -{ - for (GLGizmoRotate& g : m_gizmos) - if (!g.init()) return false; - - for (unsigned int i = 0; i < 3; ++i) - m_gizmos[i].set_highlight_color(AXES_COLOR[i]); - - m_shortcut_key = WXK_CONTROL_R; - - return true; -} - -std::string GLGizmoRotate3D::on_get_name() const -{ - return _u8L("Rotate"); -} - -bool GLGizmoRotate3D::on_is_activable() const -{ - return !m_parent.get_selection().is_empty(); -} - -void GLGizmoRotate3D::on_start_dragging() -{ - assert(0 <= m_hover_id && m_hover_id < 3); - m_gizmos[m_hover_id].start_dragging(); -} - -void GLGizmoRotate3D::on_stop_dragging() -{ - assert(0 <= m_hover_id && m_hover_id < 3); - m_parent.do_rotate(L("Gizmo-Rotate")); - m_gizmos[m_hover_id].stop_dragging(); -} - -void GLGizmoRotate3D::on_dragging(const UpdateData &data) -{ - assert(0 <= m_hover_id && m_hover_id < 3); - m_gizmos[m_hover_id].dragging(data); -} - -void GLGizmoRotate3D::on_render() -{ - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - - if (m_hover_id == -1 || m_hover_id == 0) - m_gizmos[X].render(); - - if (m_hover_id == -1 || m_hover_id == 1) - m_gizmos[Y].render(); - - if (m_hover_id == -1 || m_hover_id == 2) - m_gizmos[Z].render(); -} - -GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, - State & state, - const Alignment &alignment) - : m_imgui{imgui} -{ - imgui->begin(_L("Optimize orientation"), ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_AlwaysAutoResize | - ImGuiWindowFlags_NoCollapse); - - // adjust window position to avoid overlap the view toolbar - float win_h = ImGui::GetWindowHeight(); - float x = alignment.x, y = alignment.y; - y = std::min(y, alignment.bottom_limit - win_h); - ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); - - float max_text_w = 0.; - auto padding = ImGui::GetStyle().FramePadding; - padding.x *= 2.f; - padding.y *= 2.f; - - for (size_t i = 0; i < RotoptimizeJob::get_methods_count(); ++i) { - float w = - ImGui::CalcTextSize(RotoptimizeJob::get_method_name(i).c_str()).x + - padding.x + ImGui::GetFrameHeight(); - max_text_w = std::max(w, max_text_w); - } - - ImGui::PushItemWidth(max_text_w); - - if (ImGui::BeginCombo("", RotoptimizeJob::get_method_name(state.method_id).c_str())) { - for (size_t i = 0; i < RotoptimizeJob::get_methods_count(); ++i) { - if (ImGui::Selectable(RotoptimizeJob::get_method_name(i).c_str())) { - state.method_id = i; - wxGetApp().app_config->set("sla_auto_rotate", - "method_id", - std::to_string(state.method_id)); - } - - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", RotoptimizeJob::get_method_description(i).c_str()); - } - - ImGui::EndCombo(); - } - - ImVec2 sz = ImGui::GetItemRectSize(); - - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", RotoptimizeJob::get_method_description(state.method_id).c_str()); - - ImGui::Separator(); - - auto btn_txt = _L("Apply"); - auto btn_txt_sz = ImGui::CalcTextSize(btn_txt.c_str()); - ImVec2 button_sz = {btn_txt_sz.x + padding.x, btn_txt_sz.y + padding.y}; - ImGui::SetCursorPosX(padding.x + sz.x - button_sz.x); - - if (!wxGetApp().plater()->get_ui_job_worker().is_idle()) - imgui->disabled_begin(true); - - if ( imgui->button(btn_txt) ) { - replace_job(wxGetApp().plater()->get_ui_job_worker(), - std::make_unique()); - } - - imgui->disabled_end(); -} - -GLGizmoRotate3D::RotoptimzeWindow::~RotoptimzeWindow() -{ - m_imgui->end(); -} - -} // namespace GUI -} // namespace Slic3r +// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +#include "GLGizmoRotate.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/ImGuiWrapper.hpp" + +#include + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "libslic3r/PresetBundle.hpp" + +#include "slic3r/GUI/Jobs/RotoptimizeJob.hpp" + +namespace Slic3r { +namespace GUI { + + +const float GLGizmoRotate::Offset = 5.0f; +const unsigned int GLGizmoRotate::AngleResolution = 64; +const unsigned int GLGizmoRotate::ScaleStepsCount = 72; +const float GLGizmoRotate::ScaleStepRad = 2.0f * float(PI) / GLGizmoRotate::ScaleStepsCount; +const unsigned int GLGizmoRotate::ScaleLongEvery = 2; +const float GLGizmoRotate::ScaleLongTooth = 0.1f; // in percent of radius +const unsigned int GLGizmoRotate::SnapRegionsCount = 8; +const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius + +GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) + : GLGizmoBase(parent, "", -1) + , m_axis(axis) + , m_angle(0.0) + , m_center(0.0, 0.0, 0.0) + , m_radius(0.0f) + , m_snap_coarse_in_radius(0.0f) + , m_snap_coarse_out_radius(0.0f) + , m_snap_fine_in_radius(0.0f) + , m_snap_fine_out_radius(0.0f) + , m_drag_color(DEFAULT_DRAG_COLOR) + , m_highlight_color(DEFAULT_HIGHLIGHT_COLOR) +{ + m_group_id = static_cast(axis); +} + +void GLGizmoRotate::set_highlight_color(const ColorRGBA &color) +{ + m_highlight_color = color; +} + +void GLGizmoRotate::set_angle(double angle) +{ + if (std::abs(angle - 2.0 * double(PI)) < EPSILON) + angle = 0.0; + + m_angle = angle; +} + +std::string GLGizmoRotate::get_tooltip() const +{ + std::string axis; + switch (m_axis) + { + case X: { axis = "X"; break; } + case Y: { axis = "Y"; break; } + case Z: { axis = "Z"; break; } + } + return (m_hover_id == 0 || m_grabbers.front().dragging) ? axis + ": " + format(float(Geometry::rad2deg(m_angle)), 4) : ""; +} + +bool GLGizmoRotate::on_mouse(const wxMouseEvent &mouse_event) +{ + return use_grabbers(mouse_event); +} + +void GLGizmoRotate::dragging(const UpdateData &data) { on_dragging(data); } + +void GLGizmoRotate::start_dragging() +{ + m_grabbers[0].dragging = true; + on_start_dragging(); +} + +void GLGizmoRotate::stop_dragging() +{ + m_grabbers[0].dragging = false; + on_stop_dragging(); +} + +void GLGizmoRotate::enable_grabber() { m_grabbers[0].enabled = true; } +void GLGizmoRotate::disable_grabber() { m_grabbers[0].enabled = false; } + +bool GLGizmoRotate::on_init() +{ + m_grabbers.push_back(Grabber()); +#if ENABLE_GIZMO_GRABBER_REFACTOR + m_grabbers.back().extensions = (GLGizmoBase::EGrabberExtension)(int(GLGizmoBase::EGrabberExtension::PosY) | int(GLGizmoBase::EGrabberExtension::NegY)); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + return true; +} + +void GLGizmoRotate::on_start_dragging() +{ + const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); + m_center = box.center(); + m_radius = Offset + box.radius(); + m_snap_coarse_in_radius = m_radius / 3.0f; + m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; + m_snap_fine_in_radius = m_radius; + m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth; +} + +void GLGizmoRotate::on_dragging(const UpdateData &data) +{ + const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, m_parent.get_selection())); + + const Vec2d orig_dir = Vec2d::UnitX(); + const Vec2d new_dir = mouse_pos.normalized(); + + double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); + if (cross2(orig_dir, new_dir) < 0.0) + theta = 2.0 * (double)PI - theta; + + const double len = mouse_pos.norm(); + + // snap to coarse snap region + if (m_snap_coarse_in_radius <= len && len <= m_snap_coarse_out_radius) { + const double step = 2.0 * double(PI) / double(SnapRegionsCount); + theta = step * std::round(theta / step); + } + else { + // snap to fine snap region (scale) + if (m_snap_fine_in_radius <= len && len <= m_snap_fine_out_radius) { + const double step = 2.0 * double(PI) / double(ScaleStepsCount); + theta = step * std::round(theta / step); + } + } + + if (theta == 2.0 * double(PI)) + theta = 0.0; + + m_angle = theta; +} + +void GLGizmoRotate::on_render() +{ + if (!m_grabbers.front().enabled) + return; + +#if !ENABLE_GIZMO_GRABBER_REFACTOR + if (!m_cone.is_initialized()) + m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 12.0)); +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR + + const Selection& selection = m_parent.get_selection(); + const BoundingBoxf3& box = selection.get_bounding_box(); + + if (m_hover_id != 0 && !m_grabbers.front().dragging) { + m_center = box.center(); + m_radius = Offset + box.radius(); + m_snap_coarse_in_radius = m_radius / 3.0f; + m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; + m_snap_fine_in_radius = m_radius; + m_snap_fine_out_radius = m_radius * (1.0f + ScaleLongTooth); + } + + const double grabber_radius = (double)m_radius * (1.0 + (double)GrabberOffset); + m_grabbers.front().center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0); + m_grabbers.front().angles.z() = m_angle; + + glsafe(::glEnable(GL_DEPTH_TEST)); + +#if ENABLE_GL_SHADERS_ATTRIBUTES + m_grabbers.front().matrix = local_transform(selection); +#else + glsafe(::glPushMatrix()); + transform_to_local(selection); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + + glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); +#if ENABLE_LEGACY_OPENGL_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d view_model_matrix = camera.get_view_matrix() * m_grabbers.front().matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + + const bool radius_changed = std::abs(m_old_radius - m_radius) > EPSILON; + m_old_radius = m_radius; + + ColorRGBA color((m_hover_id != -1) ? m_drag_color : m_highlight_color); + render_circle(color, radius_changed); + if (m_hover_id != -1) { + const bool hover_radius_changed = std::abs(m_old_hover_radius - m_radius) > EPSILON; + m_old_hover_radius = m_radius; + + render_scale(color, hover_radius_changed); + render_snap_radii(color, hover_radius_changed); + render_reference_radius(color, hover_radius_changed); + render_angle_arc(m_highlight_color, hover_radius_changed); + } + + render_grabber_connection(color, radius_changed); + shader->stop_using(); + } +#else + glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color.data() : m_highlight_color.data())); + + render_circle(); + + if (m_hover_id != -1) { + render_scale(); + render_snap_radii(); + render_reference_radius(); + } + + glsafe(::glColor4fv(m_highlight_color.data())); + + if (m_hover_id != -1) + render_angle(); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + + render_grabber(box); +#if !ENABLE_GIZMO_GRABBER_REFACTOR + render_grabber_extension(box, false); +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR + +#if !ENABLE_GL_SHADERS_ATTRIBUTES + glsafe(::glPopMatrix()); +#endif // !ENABLE_GL_SHADERS_ATTRIBUTES +} + +void GLGizmoRotate::on_render_for_picking() +{ + const Selection& selection = m_parent.get_selection(); + + glsafe(::glDisable(GL_DEPTH_TEST)); + +#if ENABLE_GL_SHADERS_ATTRIBUTES + m_grabbers.front().matrix = local_transform(selection); +#else + glsafe(::glPushMatrix()); + transform_to_local(selection); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + + const BoundingBoxf3& box = selection.get_bounding_box(); + render_grabbers_for_picking(box); +#if !ENABLE_GIZMO_GRABBER_REFACTOR + render_grabber_extension(box, true); +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR + +#if !ENABLE_GL_SHADERS_ATTRIBUTES + glsafe(::glPopMatrix()); +#endif // !ENABLE_GL_SHADERS_ATTRIBUTES +} + +void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit) +{ + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) + return; + + RotoptimzeWindow popup{m_imgui, m_rotoptimizewin_state, {x, y, bottom_limit}}; +} + +void GLGizmoRotate3D::load_rotoptimize_state() +{ + std::string accuracy_str = + wxGetApp().app_config->get("sla_auto_rotate", "accuracy"); + + std::string method_str = + wxGetApp().app_config->get("sla_auto_rotate", "method_id"); + + if (!accuracy_str.empty()) { + float accuracy = std::stof(accuracy_str); + accuracy = std::max(0.f, std::min(accuracy, 1.f)); + + m_rotoptimizewin_state.accuracy = accuracy; + } + + if (!method_str.empty()) { + int method_id = std::stoi(method_str); + if (method_id < int(RotoptimizeJob::get_methods_count())) + m_rotoptimizewin_state.method_id = method_id; + } +} + +#if ENABLE_LEGACY_OPENGL_REMOVAL +void GLGizmoRotate::render_circle(const ColorRGBA& color, bool radius_changed) +#else +void GLGizmoRotate::render_circle() const +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +{ +#if ENABLE_LEGACY_OPENGL_REMOVAL + if (!m_circle.is_initialized() || radius_changed) { + m_circle.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineLoop, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(ScaleStepsCount); + init_data.reserve_indices(ScaleStepsCount); + + // vertices + indices + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = float(i * ScaleStepRad); + init_data.add_vertex(Vec3f(::cos(angle) * m_radius, ::sin(angle) * m_radius, 0.0f)); + init_data.add_index(i); + } + + m_circle.init_from(std::move(init_data)); + } + + m_circle.set_color(color); + m_circle.render(); +#else + ::glBegin(GL_LINE_LOOP); + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + float angle = (float)i * ScaleStepRad; + float x = ::cos(angle) * m_radius; + float y = ::sin(angle) * m_radius; + float z = 0.0f; + ::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); + } + glsafe(::glEnd()); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +} + +#if ENABLE_LEGACY_OPENGL_REMOVAL +void GLGizmoRotate::render_scale(const ColorRGBA& color, bool radius_changed) +#else +void GLGizmoRotate::render_scale() const +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +{ + const float out_radius_long = m_snap_fine_out_radius; + const float out_radius_short = m_radius * (1.0f + 0.5f * ScaleLongTooth); + +#if ENABLE_LEGACY_OPENGL_REMOVAL + if (!m_scale.is_initialized() || radius_changed) { + m_scale.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2 * ScaleStepsCount); + init_data.reserve_indices(2 * ScaleStepsCount); + + // vertices + indices + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = float(i * ScaleStepRad); + const float cosa = ::cos(angle); + const float sina = ::sin(angle); + const float in_x = cosa * m_radius; + const float in_y = sina * m_radius; + const float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; + const float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; + + // vertices + init_data.add_vertex(Vec3f(in_x, in_y, 0.0f)); + init_data.add_vertex(Vec3f(out_x, out_y, 0.0f)); + + // indices + init_data.add_line(i * 2, i * 2 + 1); + } + + m_scale.init_from(std::move(init_data)); + } + + m_scale.set_color(color); + m_scale.render(); +#else + ::glBegin(GL_LINES); + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = (float)i * ScaleStepRad; + const float cosa = ::cos(angle); + const float sina = ::sin(angle); + const float in_x = cosa * m_radius; + const float in_y = sina * m_radius; + const float in_z = 0.0f; + const float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; + const float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; + const float out_z = 0.0f; + ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z); + ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z); + } + glsafe(::glEnd()); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +} + +#if ENABLE_LEGACY_OPENGL_REMOVAL +void GLGizmoRotate::render_snap_radii(const ColorRGBA& color, bool radius_changed) +#else +void GLGizmoRotate::render_snap_radii() const +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +{ + const float step = 2.0f * float(PI) / float(SnapRegionsCount); + const float in_radius = m_radius / 3.0f; + const float out_radius = 2.0f * in_radius; + +#if ENABLE_LEGACY_OPENGL_REMOVAL + if (!m_snap_radii.is_initialized() || radius_changed) { + m_snap_radii.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2 * ScaleStepsCount); + init_data.reserve_indices(2 * ScaleStepsCount); + + // vertices + indices + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = float(i * step); + const float cosa = ::cos(angle); + const float sina = ::sin(angle); + const float in_x = cosa * in_radius; + const float in_y = sina * in_radius; + const float out_x = cosa * out_radius; + const float out_y = sina * out_radius; + + // vertices + init_data.add_vertex(Vec3f(in_x, in_y, 0.0f)); + init_data.add_vertex(Vec3f(out_x, out_y, 0.0f)); + + // indices + init_data.add_line(i * 2, i * 2 + 1); + } + + m_snap_radii.init_from(std::move(init_data)); + } + + m_snap_radii.set_color(color); + m_snap_radii.render(); +#else + ::glBegin(GL_LINES); + for (unsigned int i = 0; i < SnapRegionsCount; ++i) { + const float angle = (float)i * step; + const float cosa = ::cos(angle); + const float sina = ::sin(angle); + const float in_x = cosa * in_radius; + const float in_y = sina * in_radius; + const float in_z = 0.0f; + const float out_x = cosa * out_radius; + const float out_y = sina * out_radius; + const float out_z = 0.0f; + ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z); + ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z); + } + glsafe(::glEnd()); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +} + +#if ENABLE_LEGACY_OPENGL_REMOVAL +void GLGizmoRotate::render_reference_radius(const ColorRGBA& color, bool radius_changed) +{ + if (!m_reference_radius.is_initialized() || radius_changed) { + m_reference_radius.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); + init_data.add_vertex(Vec3f(m_radius * (1.0f + GrabberOffset), 0.0f, 0.0f)); + + // indices + init_data.add_line(0, 1); + + m_reference_radius.init_from(std::move(init_data)); + } + + m_reference_radius.set_color(color); + m_reference_radius.render(); +} +#else +void GLGizmoRotate::render_reference_radius() const +{ + ::glBegin(GL_LINES); + ::glVertex3f(0.0f, 0.0f, 0.0f); + ::glVertex3f((GLfloat)(m_radius * (1.0f + GrabberOffset)), 0.0f, 0.0f); + glsafe(::glEnd()); +} +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + +#if ENABLE_LEGACY_OPENGL_REMOVAL +void GLGizmoRotate::render_angle_arc(const ColorRGBA& color, bool radius_changed) +#else +void GLGizmoRotate::render_angle() const +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +{ + const float step_angle = float(m_angle) / float(AngleResolution); + const float ex_radius = m_radius * (1.0f + GrabberOffset); + +#if ENABLE_LEGACY_OPENGL_REMOVAL + const bool angle_changed = std::abs(m_old_angle - m_angle) > EPSILON; + m_old_angle = m_angle; + + if (!m_angle_arc.is_initialized() || radius_changed || angle_changed) { + m_angle_arc.reset(); + if (m_angle > 0.0f) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(1 + AngleResolution); + init_data.reserve_indices(1 + AngleResolution); + + // vertices + indices + for (unsigned int i = 0; i <= AngleResolution; ++i) { + const float angle = float(i) * step_angle; + init_data.add_vertex(Vec3f(::cos(angle) * ex_radius, ::sin(angle) * ex_radius, 0.0f)); + init_data.add_index(i); + } + + m_angle_arc.init_from(std::move(init_data)); + } + } + + m_angle_arc.set_color(color); + m_angle_arc.render(); +#else + ::glBegin(GL_LINE_STRIP); + for (unsigned int i = 0; i <= AngleResolution; ++i) { + float angle = (float)i * step_angle; + float x = ::cos(angle) * ex_radius; + float y = ::sin(angle) * ex_radius; + float z = 0.0f; + ::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); + } + glsafe(::glEnd()); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +} + +#if ENABLE_LEGACY_OPENGL_REMOVAL +void GLGizmoRotate::render_grabber_connection(const ColorRGBA& color, bool radius_changed) +{ + if (!m_grabber_connection.model.is_initialized() || radius_changed || !m_grabber_connection.old_center.isApprox(m_grabbers.front().center)) { + m_grabber_connection.model.reset(); + m_grabber_connection.old_center = m_grabbers.front().center; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); + init_data.add_vertex((Vec3f)m_grabbers.front().center.cast()); + + // indices + init_data.add_line(0, 1); + + m_grabber_connection.model.init_from(std::move(init_data)); + } + + m_grabber_connection.model.set_color(color); + m_grabber_connection.model.render(); +} +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + +void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) +{ +#if !ENABLE_LEGACY_OPENGL_REMOVAL + const double grabber_radius = double(m_radius) * (1.0 + double(GrabberOffset)); + m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0); + m_grabbers[0].angles.z() = m_angle; + + glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color.data() : m_highlight_color.data())); + + ::glBegin(GL_LINES); + ::glVertex3f(0.0f, 0.0f, 0.0f); + ::glVertex3dv(m_grabbers[0].center.data()); + glsafe(::glEnd()); +#endif // !ENABLE_LEGACY_OPENGL_REMOVAL + + m_grabbers.front().color = m_highlight_color; + render_grabbers(box); +} + +#if !ENABLE_GIZMO_GRABBER_REFACTOR +void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool picking) +{ + const float mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 3.0); + const double size = m_dragging ? double(m_grabbers.front().get_dragging_half_size(mean_size)) : double(m_grabbers.front().get_half_size(mean_size)); + +#if ENABLE_LEGACY_OPENGL_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader(picking ? "flat" : "gouraud_light"); + if (shader == nullptr) + return; + + m_cone.set_color((!picking && m_hover_id != -1) ? complementary(m_grabbers.front().color) : m_grabbers.front().color); + + shader->start_using(); + shader->set_uniform("emission_factor", 0.1f); +#else + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + + m_cone.set_color(-1, (!picking && m_hover_id != -1) ? complementary(m_grabbers.front().color) : m_grabbers.front().color); + if (!picking) { + shader->start_using(); + shader->set_uniform("emission_factor", 0.1f); + } +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + + const Vec3d& center = m_grabbers.front().center; + +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + Transform3d view_model_matrix = view_matrix * m_grabbers.front().matrix * + Geometry::assemble_transform(center, Vec3d(0.5 * PI, 0.0, m_angle)) * + Geometry::assemble_transform(2.0 * size * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(0.75 * size, 0.75 * size, 3.0 * size)); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); +#else + glsafe(::glPushMatrix()); + glsafe(::glTranslated(center.x(), center.y(), center.z())); + glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0)); + glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); + glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); + glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + m_cone.render(); +#if ENABLE_GL_SHADERS_ATTRIBUTES + view_model_matrix = view_matrix * m_grabbers.front().matrix * + Geometry::assemble_transform(center, Vec3d(-0.5 * PI, 0.0, m_angle)) * + Geometry::assemble_transform(2.0 * size * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(0.75 * size, 0.75 * size, 3.0 * size)); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); +#else + glsafe(::glPopMatrix()); + glsafe(::glPushMatrix()); + glsafe(::glTranslated(center.x(), center.y(), center.z())); + glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0)); + glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); + glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); + glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + m_cone.render(); +#if !ENABLE_GL_SHADERS_ATTRIBUTES + glsafe(::glPopMatrix()); +#endif // !ENABLE_GL_SHADERS_ATTRIBUTES + +#if !ENABLE_LEGACY_OPENGL_REMOVAL + if (! picking) +#endif // !ENABLE_LEGACY_OPENGL_REMOVAL + shader->stop_using(); +} +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR + +#if ENABLE_GL_SHADERS_ATTRIBUTES +Transform3d GLGizmoRotate::local_transform(const Selection& selection) const +{ + Transform3d ret; + + switch (m_axis) + { + case X: + { + ret = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, 0.5 * PI, 0.0)) * Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, 0.0, -0.5 * PI)); + break; + } + case Y: + { + ret = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, 0.0, -0.5 * PI)) * Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, -0.5 * PI, 0.0)); + break; + } + default: + case Z: + { + ret = Transform3d::Identity(); + break; + } + } + + if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) + ret = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true) * ret; + + return Geometry::assemble_transform(m_center) * ret; +} +#else +void GLGizmoRotate::transform_to_local(const Selection& selection) const +{ + glsafe(::glTranslated(m_center.x(), m_center.y(), m_center.z())); + + if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) { + const Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); + glsafe(::glMultMatrixd(orient_matrix.data())); + } + + switch (m_axis) + { + case X: + { + glsafe(::glRotatef(90.0f, 0.0f, 1.0f, 0.0f)); + glsafe(::glRotatef(-90.0f, 0.0f, 0.0f, 1.0f)); + break; + } + case Y: + { + glsafe(::glRotatef(-90.0f, 0.0f, 0.0f, 1.0f)); + glsafe(::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f)); + break; + } + default: + case Z: + { + // no rotation + break; + } + } +} +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + +Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const +{ + double half_pi = 0.5 * double(PI); + + Transform3d m = Transform3d::Identity(); + + switch (m_axis) + { + case X: + { + m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ())); + m.rotate(Eigen::AngleAxisd(-half_pi, Vec3d::UnitY())); + break; + } + case Y: + { + m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitY())); + m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ())); + break; + } + default: + case Z: + { + // no rotation applied + break; + } + } + + if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) + m = m * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true).inverse(); + + m.translate(-m_center); + + return transform(mouse_ray, m).intersect_plane(0.0); +} + +GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoBase(parent, icon_filename, sprite_id) + , m_gizmos({ + GLGizmoRotate(parent, GLGizmoRotate::X), + GLGizmoRotate(parent, GLGizmoRotate::Y), + GLGizmoRotate(parent, GLGizmoRotate::Z) }) +{} + +bool GLGizmoRotate3D::on_mouse(const wxMouseEvent &mouse_event) +{ + if (mouse_event.Dragging() && m_dragging) { + // Apply new temporary rotations + TransformationType transformation_type( + TransformationType::World_Relative_Joint); + if (mouse_event.AltDown()) transformation_type.set_independent(); + m_parent.get_selection().rotate(get_rotation(), transformation_type); + } + return use_grabbers(mouse_event); +} + +void GLGizmoRotate3D::data_changed() { + const Selection &selection = m_parent.get_selection(); + bool is_wipe_tower = selection.is_wipe_tower(); + if (is_wipe_tower) { + DynamicPrintConfig& config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + float wipe_tower_rotation_angle = + dynamic_cast( + config.option("wipe_tower_rotation_angle")) + ->value; + set_rotation(Vec3d(0., 0., (M_PI / 180.) * wipe_tower_rotation_angle)); + m_gizmos[0].disable_grabber(); + m_gizmos[1].disable_grabber(); + } else { + set_rotation(Vec3d::Zero()); + m_gizmos[0].enable_grabber(); + m_gizmos[1].enable_grabber(); + } +} + +bool GLGizmoRotate3D::on_init() +{ + for (GLGizmoRotate& g : m_gizmos) + if (!g.init()) return false; + + for (unsigned int i = 0; i < 3; ++i) + m_gizmos[i].set_highlight_color(AXES_COLOR[i]); + + m_shortcut_key = WXK_CONTROL_R; + + return true; +} + +std::string GLGizmoRotate3D::on_get_name() const +{ + return _u8L("Rotate"); +} + +bool GLGizmoRotate3D::on_is_activable() const +{ + return !m_parent.get_selection().is_empty(); +} + +void GLGizmoRotate3D::on_start_dragging() +{ + assert(0 <= m_hover_id && m_hover_id < 3); + m_gizmos[m_hover_id].start_dragging(); +} + +void GLGizmoRotate3D::on_stop_dragging() +{ + assert(0 <= m_hover_id && m_hover_id < 3); + m_parent.do_rotate(L("Gizmo-Rotate")); + m_gizmos[m_hover_id].stop_dragging(); +} + +void GLGizmoRotate3D::on_dragging(const UpdateData &data) +{ + assert(0 <= m_hover_id && m_hover_id < 3); + m_gizmos[m_hover_id].dragging(data); +} + +void GLGizmoRotate3D::on_render() +{ + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + + if (m_hover_id == -1 || m_hover_id == 0) + m_gizmos[X].render(); + + if (m_hover_id == -1 || m_hover_id == 1) + m_gizmos[Y].render(); + + if (m_hover_id == -1 || m_hover_id == 2) + m_gizmos[Z].render(); +} + +GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, + State & state, + const Alignment &alignment) + : m_imgui{imgui} +{ + imgui->begin(_L("Optimize orientation"), ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoCollapse); + + // adjust window position to avoid overlap the view toolbar + float win_h = ImGui::GetWindowHeight(); + float x = alignment.x, y = alignment.y; + y = std::min(y, alignment.bottom_limit - win_h); + ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); + + float max_text_w = 0.; + auto padding = ImGui::GetStyle().FramePadding; + padding.x *= 2.f; + padding.y *= 2.f; + + for (size_t i = 0; i < RotoptimizeJob::get_methods_count(); ++i) { + float w = + ImGui::CalcTextSize(RotoptimizeJob::get_method_name(i).c_str()).x + + padding.x + ImGui::GetFrameHeight(); + max_text_w = std::max(w, max_text_w); + } + + ImGui::PushItemWidth(max_text_w); + + if (ImGui::BeginCombo("", RotoptimizeJob::get_method_name(state.method_id).c_str())) { + for (size_t i = 0; i < RotoptimizeJob::get_methods_count(); ++i) { + if (ImGui::Selectable(RotoptimizeJob::get_method_name(i).c_str())) { + state.method_id = i; + wxGetApp().app_config->set("sla_auto_rotate", + "method_id", + std::to_string(state.method_id)); + } + + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", RotoptimizeJob::get_method_description(i).c_str()); + } + + ImGui::EndCombo(); + } + + ImVec2 sz = ImGui::GetItemRectSize(); + + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", RotoptimizeJob::get_method_description(state.method_id).c_str()); + + ImGui::Separator(); + + auto btn_txt = _L("Apply"); + auto btn_txt_sz = ImGui::CalcTextSize(btn_txt.c_str()); + ImVec2 button_sz = {btn_txt_sz.x + padding.x, btn_txt_sz.y + padding.y}; + ImGui::SetCursorPosX(padding.x + sz.x - button_sz.x); + + if (!wxGetApp().plater()->get_ui_job_worker().is_idle()) + imgui->disabled_begin(true); + + if ( imgui->button(btn_txt) ) { + replace_job(wxGetApp().plater()->get_ui_job_worker(), + std::make_unique()); + } + + imgui->disabled_end(); +} + +GLGizmoRotate3D::RotoptimzeWindow::~RotoptimzeWindow() +{ + m_imgui->end(); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index b58fde1ee..f4594bc33 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -1,213 +1,218 @@ -#ifndef slic3r_GLGizmoRotate_hpp_ -#define slic3r_GLGizmoRotate_hpp_ - -#include "GLGizmoBase.hpp" - -namespace Slic3r { -namespace GUI { -class Selection; -class GLGizmoRotate : public GLGizmoBase -{ - static const float Offset; - static const unsigned int AngleResolution; - static const unsigned int ScaleStepsCount; - static const float ScaleStepRad; - static const unsigned int ScaleLongEvery; - static const float ScaleLongTooth; - static const unsigned int SnapRegionsCount; - static const float GrabberOffset; - -public: - enum Axis : unsigned char - { - X=0, - Y=1, - Z=2 - }; - -private: - Axis m_axis; - double m_angle{ 0.0 }; - Vec3d m_center{ Vec3d::Zero() }; - float m_radius{ 0.0f }; - float m_snap_coarse_in_radius{ 0.0f }; - float m_snap_coarse_out_radius{ 0.0f }; - float m_snap_fine_in_radius{ 0.0f }; - float m_snap_fine_out_radius{ 0.0f }; - - GLModel m_cone; -#if ENABLE_LEGACY_OPENGL_REMOVAL - GLModel m_circle; - GLModel m_scale; - GLModel m_snap_radii; - GLModel m_reference_radius; - GLModel m_angle_arc; - struct GrabberConnection - { - GLModel model; - Vec3d old_center{ Vec3d::Zero() }; - }; - GrabberConnection m_grabber_connection; - float m_old_radius{ 0.0f }; - float m_old_hover_radius{ 0.0f }; - float m_old_angle{ 0.0f }; -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - ColorRGBA m_drag_color; - ColorRGBA m_highlight_color; -public: - GLGizmoRotate(GLCanvas3D& parent, Axis axis); - virtual ~GLGizmoRotate() = default; - - double get_angle() const { return m_angle; } - void set_angle(double angle); - - std::string get_tooltip() const override; - - void start_dragging(); - void stop_dragging(); - - void enable_grabber(); - void disable_grabber(); - - void set_highlight_color(const ColorRGBA &color); - - /// - /// Postpone to Grabber for move - /// Detect move of object by dragging - /// - /// Keep information about mouse click - /// Return True when use the information otherwise False. - bool on_mouse(const wxMouseEvent &mouse_event) override; - void dragging(const UpdateData &data); -protected: - bool on_init() override; - std::string on_get_name() const override { return ""; } - void on_start_dragging() override; - void on_dragging(const UpdateData &data) override; - void on_render() override; - void on_render_for_picking() override; - -private: -#if ENABLE_LEGACY_OPENGL_REMOVAL - void render_circle(const ColorRGBA& color, bool radius_changed); - void render_scale(const ColorRGBA& color, bool radius_changed); - void render_snap_radii(const ColorRGBA& color, bool radius_changed); - void render_reference_radius(const ColorRGBA& color, bool radius_changed); - void render_angle_arc(const ColorRGBA& color, bool radius_changed); - void render_grabber_connection(const ColorRGBA& color, bool radius_changed); -#else - void render_circle() const; - void render_scale() const; - void render_snap_radii() const; - void render_reference_radius() const; - void render_angle() const; -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - void render_grabber(const BoundingBoxf3& box); - void render_grabber_extension(const BoundingBoxf3& box, bool picking); - -#if ENABLE_GL_SHADERS_ATTRIBUTES - Transform3d local_transform(const Selection& selection) const; -#else - void transform_to_local(const Selection& selection) const; -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - - // returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate - Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const; -}; - -class GLGizmoRotate3D : public GLGizmoBase -{ - std::array m_gizmos; - -public: - GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - - Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } - void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } - - std::string get_tooltip() const override { - std::string tooltip = m_gizmos[X].get_tooltip(); - if (tooltip.empty()) - tooltip = m_gizmos[Y].get_tooltip(); - if (tooltip.empty()) - tooltip = m_gizmos[Z].get_tooltip(); - return tooltip; - } - - /// - /// Postpone to Rotation - /// - /// Keep information about mouse click - /// Return True when use the information otherwise False. - bool on_mouse(const wxMouseEvent &mouse_event) override; - - void data_changed() override; -protected: - bool on_init() override; - std::string on_get_name() const override; - void on_set_state() override { - for (GLGizmoRotate& g : m_gizmos) - g.set_state(m_state); - } - void on_set_hover_id() override { - for (int i = 0; i < 3; ++i) - m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); - } - void on_enable_grabber(unsigned int id) override { - if (id < 3) - m_gizmos[id].enable_grabber(); - } - void on_disable_grabber(unsigned int id) override { - if (id < 3) - m_gizmos[id].disable_grabber(); - } - bool on_is_activable() const override; - void on_start_dragging() override; - void on_stop_dragging() override; - void on_dragging(const UpdateData &data) override; - - void on_render() override; - void on_render_for_picking() override { - for (GLGizmoRotate& g : m_gizmos) { - g.render_for_picking(); - } - } - - void on_render_input_window(float x, float y, float bottom_limit) override; - -private: - - class RotoptimzeWindow - { - ImGuiWrapper *m_imgui = nullptr; - - public: - struct State { - float accuracy = 1.f; - int method_id = 0; - }; - - struct Alignment { float x, y, bottom_limit; }; - - RotoptimzeWindow(ImGuiWrapper * imgui, - State & state, - const Alignment &bottom_limit); - - ~RotoptimzeWindow(); - - RotoptimzeWindow(const RotoptimzeWindow&) = delete; - RotoptimzeWindow(RotoptimzeWindow &&) = delete; - RotoptimzeWindow& operator=(const RotoptimzeWindow &) = delete; - RotoptimzeWindow& operator=(RotoptimzeWindow &&) = delete; - }; - - RotoptimzeWindow::State m_rotoptimizewin_state = {}; - - void load_rotoptimize_state(); -}; - -} // namespace GUI -} // namespace Slic3r - -#endif // slic3r_GLGizmoRotate_hpp_ +#ifndef slic3r_GLGizmoRotate_hpp_ +#define slic3r_GLGizmoRotate_hpp_ + +#include "GLGizmoBase.hpp" + +namespace Slic3r { +namespace GUI { +class Selection; +class GLGizmoRotate : public GLGizmoBase +{ + static const float Offset; + static const unsigned int AngleResolution; + static const unsigned int ScaleStepsCount; + static const float ScaleStepRad; + static const unsigned int ScaleLongEvery; + static const float ScaleLongTooth; + static const unsigned int SnapRegionsCount; + static const float GrabberOffset; + +public: + enum Axis : unsigned char + { + X=0, + Y=1, + Z=2 + }; + +private: + Axis m_axis; + double m_angle{ 0.0 }; + Vec3d m_center{ Vec3d::Zero() }; + float m_radius{ 0.0f }; + float m_snap_coarse_in_radius{ 0.0f }; + float m_snap_coarse_out_radius{ 0.0f }; + float m_snap_fine_in_radius{ 0.0f }; + float m_snap_fine_out_radius{ 0.0f }; + +#if !ENABLE_GIZMO_GRABBER_REFACTOR + GLModel m_cone; +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR +#if ENABLE_LEGACY_OPENGL_REMOVAL + GLModel m_circle; + GLModel m_scale; + GLModel m_snap_radii; + GLModel m_reference_radius; + GLModel m_angle_arc; + struct GrabberConnection + { + GLModel model; + Vec3d old_center{ Vec3d::Zero() }; + }; + GrabberConnection m_grabber_connection; + float m_old_radius{ 0.0f }; + float m_old_hover_radius{ 0.0f }; + float m_old_angle{ 0.0f }; +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + + ColorRGBA m_drag_color; + ColorRGBA m_highlight_color; + +public: + GLGizmoRotate(GLCanvas3D& parent, Axis axis); + virtual ~GLGizmoRotate() = default; + + double get_angle() const { return m_angle; } + void set_angle(double angle); + + std::string get_tooltip() const override; + + void start_dragging(); + void stop_dragging(); + + void enable_grabber(); + void disable_grabber(); + + void set_highlight_color(const ColorRGBA &color); + + /// + /// Postpone to Grabber for move + /// Detect move of object by dragging + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + void dragging(const UpdateData &data); +protected: + bool on_init() override; + std::string on_get_name() const override { return ""; } + void on_start_dragging() override; + void on_dragging(const UpdateData &data) override; + void on_render() override; + void on_render_for_picking() override; + +private: +#if ENABLE_LEGACY_OPENGL_REMOVAL + void render_circle(const ColorRGBA& color, bool radius_changed); + void render_scale(const ColorRGBA& color, bool radius_changed); + void render_snap_radii(const ColorRGBA& color, bool radius_changed); + void render_reference_radius(const ColorRGBA& color, bool radius_changed); + void render_angle_arc(const ColorRGBA& color, bool radius_changed); + void render_grabber_connection(const ColorRGBA& color, bool radius_changed); +#else + void render_circle() const; + void render_scale() const; + void render_snap_radii() const; + void render_reference_radius() const; + void render_angle() const; +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + void render_grabber(const BoundingBoxf3& box); +#if !ENABLE_GIZMO_GRABBER_REFACTOR + void render_grabber_extension(const BoundingBoxf3& box, bool picking); +#endif // !ENABLE_GIZMO_GRABBER_REFACTOR + +#if ENABLE_GL_SHADERS_ATTRIBUTES + Transform3d local_transform(const Selection& selection) const; +#else + void transform_to_local(const Selection& selection) const; +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + + // returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate + Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const; +}; + +class GLGizmoRotate3D : public GLGizmoBase +{ + std::array m_gizmos; + +public: + GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + + Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } + void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } + + std::string get_tooltip() const override { + std::string tooltip = m_gizmos[X].get_tooltip(); + if (tooltip.empty()) + tooltip = m_gizmos[Y].get_tooltip(); + if (tooltip.empty()) + tooltip = m_gizmos[Z].get_tooltip(); + return tooltip; + } + + /// + /// Postpone to Rotation + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + + void data_changed() override; +protected: + bool on_init() override; + std::string on_get_name() const override; + void on_set_state() override { + for (GLGizmoRotate& g : m_gizmos) + g.set_state(m_state); + } + void on_set_hover_id() override { + for (int i = 0; i < 3; ++i) + m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); + } + void on_enable_grabber(unsigned int id) override { + if (id < 3) + m_gizmos[id].enable_grabber(); + } + void on_disable_grabber(unsigned int id) override { + if (id < 3) + m_gizmos[id].disable_grabber(); + } + bool on_is_activable() const override; + void on_start_dragging() override; + void on_stop_dragging() override; + void on_dragging(const UpdateData &data) override; + + void on_render() override; + void on_render_for_picking() override { + for (GLGizmoRotate& g : m_gizmos) { + g.render_for_picking(); + } + } + + void on_render_input_window(float x, float y, float bottom_limit) override; + +private: + + class RotoptimzeWindow + { + ImGuiWrapper *m_imgui = nullptr; + + public: + struct State { + float accuracy = 1.f; + int method_id = 0; + }; + + struct Alignment { float x, y, bottom_limit; }; + + RotoptimzeWindow(ImGuiWrapper * imgui, + State & state, + const Alignment &bottom_limit); + + ~RotoptimzeWindow(); + + RotoptimzeWindow(const RotoptimzeWindow&) = delete; + RotoptimzeWindow(RotoptimzeWindow &&) = delete; + RotoptimzeWindow& operator=(const RotoptimzeWindow &) = delete; + RotoptimzeWindow& operator=(RotoptimzeWindow &&) = delete; + }; + + RotoptimzeWindow::State m_rotoptimizewin_state = {}; + + void load_rotoptimize_state(); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoRotate_hpp_