From bb3819fd1833043af9cf987992c638dd2582623e Mon Sep 17 00:00:00 2001 From: Lukas Matena <lukasmatena@seznam.cz> Date: Mon, 25 Feb 2019 13:03:06 +0100 Subject: [PATCH] SLA support gizmo hotkeys added (A,M,Esc,Enter) --- src/slic3r/GUI/GLCanvas3D.cpp | 30 ++- src/slic3r/GUI/GLCanvas3D.hpp | 6 +- src/slic3r/GUI/GLGizmo.cpp | 375 +++++++++++++++++----------------- src/slic3r/GUI/GLGizmo.hpp | 9 +- 4 files changed, 226 insertions(+), 194 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c6d465ad2..24fcd4a70 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4625,7 +4625,19 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) switch (keyCode) { // key ESC - case WXK_ESCAPE: { m_gizmos.reset_all_states(); m_dirty = true; break; } + case WXK_ESCAPE: { + if (m_gizmos.get_current_type() != Gizmos::SlaSupports || !m_gizmos.mouse_event(SLAGizmoEventType::DiscardChanges)) + m_gizmos.reset_all_states(); + m_dirty = true; + break; + } + + case WXK_RETURN: { + if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.mouse_event(SLAGizmoEventType::ApplyChanges)) + m_dirty = true; + break; + } + #ifdef __APPLE__ case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead. #else /* __APPLE__ */ @@ -4648,11 +4660,25 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case '-': { post_event(Event<int>(EVT_GLCANVAS_INCREASE_INSTANCES, -1)); break; } case '?': { post_event(SimpleEvent(EVT_GLCANVAS_QUESTION_MARK)); break; } case 'A': - case 'a': { post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); break; } + case 'a': { + if (m_gizmos.get_current_type() == Gizmos::SlaSupports) { + if (m_gizmos.mouse_event(SLAGizmoEventType::AutomaticGeneration)) + m_dirty = true; + } + else + post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); + break; + } case 'B': case 'b': { zoom_to_bed(); break; } case 'I': case 'i': { set_camera_zoom(1.0f); break; } + case 'M': + case 'm': { + if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.mouse_event(SLAGizmoEventType::ManualEditing)) + m_dirty = true; + break; + } case 'O': case 'o': { set_camera_zoom(-1.0f); break; } case 'Z': diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index bcae0f9af..008e77056 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -125,7 +125,11 @@ enum class SLAGizmoEventType { Dragging, Delete, SelectAll, - ShiftUp + ShiftUp, + ApplyChanges, + DiscardChanges, + AutomaticGeneration, + ManualEditing }; diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 723ac32a2..f1a4589fe 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1941,12 +1941,6 @@ bool GLGizmoSlaSupports::is_mesh_update_necessary() const //if (m_state != On || !m_model_object || m_model_object->instances.empty() || ! m_instance_matrix.isApprox(m_source_data.matrix)) // return false; - - // following should detect direct mesh changes (can be removed after the mesh is made completely immutable): - /*const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex(); - Vec3d first_point((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]); - if (first_point != m_source_data.mesh_first_point) - return true;*/ } void GLGizmoSlaSupports::update_mesh() @@ -2022,139 +2016,161 @@ Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) // concludes that the event was not intended for it, it should return false. bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down) { - if (!m_editing_mode) - return false; + if (m_editing_mode) { - // left down - show the selection rectangle: - if (action == SLAGizmoEventType::LeftDown && shift_down) { - if (m_hover_id == -1) { - m_selection_rectangle_active = true; - m_selection_rectangle_start_corner = mouse_position; + // left down - show the selection rectangle: + if (action == SLAGizmoEventType::LeftDown && shift_down) { + if (m_hover_id == -1) { + m_selection_rectangle_active = true; + m_selection_rectangle_start_corner = mouse_position; + m_selection_rectangle_end_corner = mouse_position; + m_canvas_width = m_parent.get_canvas_size().get_width(); + m_canvas_height = m_parent.get_canvas_size().get_height(); + } + else + select_point(m_hover_id); + + return true; + } + + // dragging the selection rectangle: + if (action == SLAGizmoEventType::Dragging && m_selection_rectangle_active) { m_selection_rectangle_end_corner = mouse_position; - m_canvas_width = m_parent.get_canvas_size().get_width(); - m_canvas_height = m_parent.get_canvas_size().get_height(); - } - else - select_point(m_hover_id); - - return true; - } - - // dragging the selection rectangle: - if (action == SLAGizmoEventType::Dragging && m_selection_rectangle_active) { - m_selection_rectangle_end_corner = mouse_position; - return true; - } - - // mouse up without selection rectangle - place point on the mesh: - if (action == SLAGizmoEventType::LeftUp && !m_selection_rectangle_active && !shift_down) { - if (m_ignore_up_event) { - m_ignore_up_event = false; - return false; + return true; } - int instance_id = m_parent.get_selection().get_instance_idx(); - if (m_old_instance_id != instance_id) - { - bool something_selected = (m_old_instance_id != -1); - m_old_instance_id = instance_id; - if (something_selected) + // mouse up without selection rectangle - place point on the mesh: + if (action == SLAGizmoEventType::LeftUp && !m_selection_rectangle_active && !shift_down) { + if (m_ignore_up_event) { + m_ignore_up_event = false; return false; - } - if (instance_id == -1) - return false; - - // If there is some selection, don't add new point and deselect everything instead. - if (m_selection_empty) { - Vec3f new_pos; - try { - new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new point in that case - m_editing_mode_cache.emplace_back(std::make_pair(sla::SupportPoint(new_pos, m_new_point_head_diameter/2.f, false), false)); - m_unsaved_changes = true; } - catch (...) { // not clicked on object - return true; // prevents deselection of the gizmo by GLCanvas3D + + int instance_id = m_parent.get_selection().get_instance_idx(); + if (m_old_instance_id != instance_id) + { + bool something_selected = (m_old_instance_id != -1); + m_old_instance_id = instance_id; + if (something_selected) + return false; } - } - else - select_point(NoPoints); + if (instance_id == -1) + return false; - return true; - } - - // left up with selection rectangle - select points inside the rectangle: - if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp) - && m_selection_rectangle_active) { - if (action == SLAGizmoEventType::ShiftUp) - m_ignore_up_event = true; - const Transform3d& instance_matrix = m_model_object->instances[m_active_instance]->get_transformation().get_matrix(); - GLint viewport[4]; - ::glGetIntegerv(GL_VIEWPORT, viewport); - GLdouble modelview_matrix[16]; - ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix); - GLdouble projection_matrix[16]; - ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix); - - const GLCanvas3D::Selection& selection = m_parent.get_selection(); - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - double z_offset = volume->get_sla_shift_z(); - - // bounding box created from the rectangle corners - will take care of order of the corners - BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast<int>()), Point(m_selection_rectangle_end_corner.cast<int>())}); - - const Transform3d& instance_matrix_no_translation = volume->get_instance_transformation().get_matrix(true); - // we'll recover current look direction from the modelview matrix (in world coords)... - Vec3f direction_to_camera(modelview_matrix[2], modelview_matrix[6], modelview_matrix[10]); - // ...and transform it to model coords. - direction_to_camera = (instance_matrix_no_translation.inverse().cast<float>() * direction_to_camera).normalized().eval(); - - // Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh: - for (unsigned int i=0; i<m_editing_mode_cache.size(); ++i) { - const sla::SupportPoint &support_point = m_editing_mode_cache[i].first; - Vec3f pos = instance_matrix.cast<float>() * support_point.pos; - pos(2) += z_offset; - GLdouble out_x, out_y, out_z; - ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); - out_y = m_canvas_height - out_y; - - if (rectangle.contains(Point(out_x, out_y))) { - bool is_obscured = false; - // Cast a ray in the direction of the camera and look for intersection with the mesh: - std::vector<igl::Hit> hits; - // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies. - if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera * (support_point.head_front_radius + EPSILON), direction_to_camera, hits)) - // FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction. - // Also, the threshold is in mesh coordinates, not in actual dimensions. - if (hits.size() > 1 || hits.front().t > 0.001f) - is_obscured = true; - - if (!is_obscured) - select_point(i); + // If there is some selection, don't add new point and deselect everything instead. + if (m_selection_empty) { + Vec3f new_pos; + try { + new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new point in that case + m_editing_mode_cache.emplace_back(std::make_pair(sla::SupportPoint(new_pos, m_new_point_head_diameter/2.f, false), false)); + m_unsaved_changes = true; + } + catch (...) { // not clicked on object + return true; // prevents deselection of the gizmo by GLCanvas3D + } } + else + select_point(NoPoints); + + return true; } - m_selection_rectangle_active = false; - return true; - } - if (action == SLAGizmoEventType::Delete) { - // delete key pressed - delete_selected_points(); - return true; - } + // left up with selection rectangle - select points inside the rectangle: + if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp) + && m_selection_rectangle_active) { + if (action == SLAGizmoEventType::ShiftUp) + m_ignore_up_event = true; + const Transform3d& instance_matrix = m_model_object->instances[m_active_instance]->get_transformation().get_matrix(); + GLint viewport[4]; + ::glGetIntegerv(GL_VIEWPORT, viewport); + GLdouble modelview_matrix[16]; + ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix); + GLdouble projection_matrix[16]; + ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix); - if (action == SLAGizmoEventType::RightDown) { - if (m_hover_id != -1) { - select_point(NoPoints); - select_point(m_hover_id); + const GLCanvas3D::Selection& selection = m_parent.get_selection(); + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + double z_offset = volume->get_sla_shift_z(); + + // bounding box created from the rectangle corners - will take care of order of the corners + BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast<int>()), Point(m_selection_rectangle_end_corner.cast<int>())}); + + const Transform3d& instance_matrix_no_translation = volume->get_instance_transformation().get_matrix(true); + // we'll recover current look direction from the modelview matrix (in world coords)... + Vec3f direction_to_camera(modelview_matrix[2], modelview_matrix[6], modelview_matrix[10]); + // ...and transform it to model coords. + direction_to_camera = (instance_matrix_no_translation.inverse().cast<float>() * direction_to_camera).normalized().eval(); + + // Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh: + for (unsigned int i=0; i<m_editing_mode_cache.size(); ++i) { + const sla::SupportPoint &support_point = m_editing_mode_cache[i].first; + Vec3f pos = instance_matrix.cast<float>() * support_point.pos; + pos(2) += z_offset; + GLdouble out_x, out_y, out_z; + ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); + out_y = m_canvas_height - out_y; + + if (rectangle.contains(Point(out_x, out_y))) { + bool is_obscured = false; + // Cast a ray in the direction of the camera and look for intersection with the mesh: + std::vector<igl::Hit> hits; + // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies. + if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera * (support_point.head_front_radius + EPSILON), direction_to_camera, hits)) + // FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction. + // Also, the threshold is in mesh coordinates, not in actual dimensions. + if (hits.size() > 1 || hits.front().t > 0.001f) + is_obscured = true; + + if (!is_obscured) + select_point(i); + } + } + m_selection_rectangle_active = false; + return true; + } + + if (action == SLAGizmoEventType::Delete) { + // delete key pressed delete_selected_points(); return true; } - return false; + + if (action == SLAGizmoEventType::ApplyChanges) { + editing_mode_apply_changes(); + return true; + } + + if (action == SLAGizmoEventType::DiscardChanges) { + editing_mode_discard_changes(); + return true; + } + + if (action == SLAGizmoEventType::RightDown) { + if (m_hover_id != -1) { + select_point(NoPoints); + select_point(m_hover_id); + delete_selected_points(); + return true; + } + return false; + } + + if (action == SLAGizmoEventType::SelectAll) { + select_point(AllPoints); + return true; + } } - if (action == SLAGizmoEventType::SelectAll) { - select_point(AllPoints); - return true; + if (!m_editing_mode) { + if (action == SLAGizmoEventType::AutomaticGeneration) { + auto_generate(); + return true; + } + + if (action == SLAGizmoEventType::ManualEditing) { + switch_to_editing_mode(); + return true; + } } return false; @@ -2241,7 +2257,6 @@ RENDER_AGAIN: bool force_refresh = false; bool remove_selected = false; - bool old_editing_state = m_editing_mode; if (m_editing_mode) { m_imgui->text(_(L("Left mouse click - add point"))); @@ -2285,9 +2300,6 @@ RENDER_AGAIN: if (apply_changes) { editing_mode_apply_changes(); force_refresh = true; - // Recalculate support structures once the editing mode is left. - // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); - wxGetApp().plater()->reslice_SLA_supports(*m_model_object); } ImGui::SameLine(); bool discard_changes = m_imgui->button(_(L("Discard changes"))); @@ -2305,70 +2317,24 @@ RENDER_AGAIN: ImGui::SameLine(); value_changed |= ImGui::InputDouble("%", &m_density, 0.0f, 0.0f, "%.f");*/ - bool generate = m_imgui->button(_(L("Auto-generate points"))); + bool generate = m_imgui->button(_(L("Auto-generate points [A]"))); - if (generate) { -#if SLAGIZMO_IMGUI_MODAL - ImGui::OpenPopup(_(L("Warning"))); - m_show_modal = true; - force_refresh = true; -#else - wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L( - "Autogeneration will erase all manually edited points.\n\n" - "Are you sure you want to do it?\n" - )), _(L("Warning")), wxICON_WARNING | wxYES | wxNO); - if (m_model_object->sla_support_points.empty() || dlg.ShowModal() == wxID_YES) { - m_model_object->sla_support_points.clear(); - m_editing_mode_cache.clear(); - wxGetApp().plater()->reslice_SLA_supports(*m_model_object); - } -#endif - } -#if SLAGIZMO_IMGUI_MODAL - if (m_show_modal) { - if (ImGui::BeginPopupModal(_(L("Warning")), &m_show_modal/*, ImGuiWindowFlags_NoDecoration*/)) - { - m_imgui->text(_(L("Autogeneration will erase all manually edited points."))); - m_imgui->text(""); - m_imgui->text(_(L("Are you sure you want to do it?"))); + if (generate) + auto_generate(); - if (m_imgui->button(_(L("Continue")))) - { - ImGui::CloseCurrentPopup(); - m_show_modal = false; - - m_model_object->sla_support_points.clear(); - m_editing_mode_cache.clear(); - wxGetApp().plater()->reslice_SLA_supports(*m_model_object); - } - ImGui::SameLine(); - if (m_imgui->button(_(L("Cancel")))) { - ImGui::CloseCurrentPopup(); - m_show_modal = false; - } - ImGui::EndPopup(); - } - - if (!m_show_modal) - force_refresh = true; - } -#endif m_imgui->text(""); m_imgui->text(""); - bool editing_clicked = m_imgui->button(_(L("Manual editing"))); - if (editing_clicked) { - editing_mode_reload_cache(); - m_editing_mode = true; - } + if (m_imgui->button(_(L("Manual editing [M]")))) + switch_to_editing_mode(); } m_imgui->end(); - if (m_editing_mode != old_editing_state) { // user just toggled between editing/non-editing mode + if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode); force_refresh = true; } - + m_old_editing_state = m_editing_mode; if (remove_selected) { force_refresh = false; @@ -2434,18 +2400,13 @@ void GLGizmoSlaSupports::on_set_state() m_parent.toggle_model_objects_visibility(true); m_editing_mode = false; // so it is not active next time the gizmo opens - -#if SLAGIZMO_IMGUI_MODAL - if (m_show_modal) { - m_show_modal = false; - on_render_input_window(0,0,m_parent.get_selection()); // this is necessary to allow ImGui to terminate the modal dialog correctly - } -#endif } } m_old_state = m_state; } + + void GLGizmoSlaSupports::on_start_dragging(const GLCanvas3D::Selection& selection) { if (m_hover_id != -1) { @@ -2454,6 +2415,8 @@ void GLGizmoSlaSupports::on_start_dragging(const GLCanvas3D::Selection& selectio } } + + void GLGizmoSlaSupports::select_point(int i) { if (i == AllPoints || i == NoPoints) { @@ -2467,6 +2430,8 @@ void GLGizmoSlaSupports::select_point(int i) } } + + void GLGizmoSlaSupports::editing_mode_discard_changes() { m_editing_mode_cache.clear(); @@ -2476,6 +2441,8 @@ void GLGizmoSlaSupports::editing_mode_discard_changes() m_unsaved_changes = false; } + + void GLGizmoSlaSupports::editing_mode_apply_changes() { // If there are no changes, don't touch the front-end. The data in the cache could have been @@ -2487,8 +2454,14 @@ void GLGizmoSlaSupports::editing_mode_apply_changes() } m_editing_mode = false; m_unsaved_changes = false; + + // Recalculate support structures once the editing mode is left. + // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().plater()->reslice_SLA_supports(*m_model_object); } + + void GLGizmoSlaSupports::editing_mode_reload_cache() { m_editing_mode_cache.clear(); @@ -2497,6 +2470,8 @@ void GLGizmoSlaSupports::editing_mode_reload_cache() m_unsaved_changes = false; } + + void GLGizmoSlaSupports::get_data_from_backend() { for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { @@ -2514,6 +2489,34 @@ void GLGizmoSlaSupports::get_data_from_backend() } + +void GLGizmoSlaSupports::auto_generate() +{ + wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L( + "Autogeneration will erase all manually edited points.\n\n" + "Are you sure you want to do it?\n" + )), _(L("Warning")), wxICON_WARNING | wxYES | wxNO); + + if (m_model_object->sla_support_points.empty() || dlg.ShowModal() == wxID_YES) { + m_model_object->sla_support_points.clear(); + m_editing_mode_cache.clear(); + wxGetApp().plater()->reslice_SLA_supports(*m_model_object); + } +} + + + +void GLGizmoSlaSupports::switch_to_editing_mode() +{ + editing_mode_reload_cache(); + m_editing_mode = true; +} + + + + + + // GLGizmoCut class GLGizmoCutPanel : public wxPanel diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 7118c1650..6996f5576 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -439,7 +439,6 @@ protected: -#define SLAGIZMO_IMGUI_MODAL 0 class GLGizmoSlaSupports : public GLGizmoBase { private: @@ -457,7 +456,7 @@ private: igl::AABB<Eigen::MatrixXf,3> m_AABB; struct SourceDataSummary { - Vec3d mesh_first_point; + Geometry::Transformation transformation; }; // This holds information to decide whether recalculation is necessary: @@ -491,6 +490,7 @@ private: bool m_lock_unique_islands = false; bool m_editing_mode = false; + bool m_old_editing_state = false; float m_new_point_head_diameter = 0.4f; double m_minimal_point_distance = 20.; double m_density = 100.; @@ -504,9 +504,6 @@ private: bool m_unsaved_changes = false; bool m_selection_empty = true; EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) -#if SLAGIZMO_IMGUI_MODAL - bool m_show_modal = false; -#endif int m_canvas_width; int m_canvas_height; @@ -521,6 +518,8 @@ private: void editing_mode_discard_changes(); void editing_mode_reload_cache(); void get_data_from_backend(); + void auto_generate(); + void switch_to_editing_mode(); protected: void on_set_state() override;