From a117a13b915a6a43fe9135ccb8b3899a35bdce4f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 10 Nov 2022 13:34:47 +0100 Subject: [PATCH] Gizmo measure - Modified states: Pressing CTRL activates 'point selection' Pressing CTRL+SHIFT while hovering a circle or an edge with extra point activates 'center selection' --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 508 ++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 8 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 28 +- 4 files changed, 357 insertions(+), 188 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 895304e5a..d2a12a3a4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -75,10 +75,21 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t std::string ret; switch (type) { case Measure::SurfaceFeatureType::Point: { ret = _u8L("Vertex"); break; } - case Measure::SurfaceFeatureType::Edge: { ret = (hover_id == POINT_ID) ? _u8L("Center of edge") : _u8L("Point on edge"); break; } - case Measure::SurfaceFeatureType::Circle: { ret = (hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } + case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Point on edge"); break; } + case Measure::SurfaceFeatureType::Circle: { ret = _u8L("Point on circle"); break; } case Measure::SurfaceFeatureType::Plane: { ret = _u8L("Point on plane"); break; } - default: { assert(false); break; } + default: { assert(false); break; } + } + return ret; +} + +static std::string center_on_feature_type_as_string(Measure::SurfaceFeatureType type) +{ + std::string ret; + switch (type) { + case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Center of edge"); break; } + case Measure::SurfaceFeatureType::Circle: { ret = _u8L("Center of circle"); break; } + default: { assert(false); break; } } return ret; } @@ -263,10 +274,41 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) else if (m_hover_id == SELECTION_2_ID && m_selected_features.second.feature.has_value()) item = m_selected_features.second; else { - item = { - (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature - }; + switch (m_mode) + { + case EMode::FeatureSelection: + { + item = { surface_feature_type_as_string(m_curr_feature->get_type()), m_curr_feature }; + break; + } + case EMode::PointSelection: + { + item = { point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id), Measure::SurfaceFeature(*m_curr_point_on_feature_position) }; + break; + } + case EMode::CenterSelection: + { + Vec3d position; + switch (m_curr_feature->get_type()) + { + case Measure::SurfaceFeatureType::Circle: + { + position = std::get<0>(m_curr_feature->get_circle()); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + assert(m_curr_feature->get_extra_point().has_value()); + position = *m_curr_feature->get_extra_point(); + break; + } + default: { assert(false); break; } + } + + item = { center_on_feature_type_as_string(m_curr_feature->get_type()), Measure::SurfaceFeature(position) }; + break; + } + } } return item; }; @@ -284,8 +326,14 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_selected_features.second.reset(); else { m_selected_features.second = item; - if (m_mode == EMode::ExtendedSelection) + if (m_mode == EMode::PointSelection || m_mode == EMode::CenterSelection) m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_2_ID, *m_sphere.mesh_raycaster)); + if (m_mode == EMode::CenterSelection) { + // Fake shift up event to exit the center selection mode + gizmo_event(SLAGizmoEventType::ShiftUp, Vec2d::Zero(), false, false, true); + // increase counter to avoid that keeping the SHIFT key pressed triggers a shift down event + m_shift_kar_filter.increase_count(); + } } } else { @@ -296,8 +344,14 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) else { const SelectedFeatures::Item item = item_from_feature(); m_selected_features.first = item; - if (m_mode == EMode::ExtendedSelection) + if (m_mode == EMode::PointSelection || m_mode == EMode::CenterSelection) m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_1_ID, *m_sphere.mesh_raycaster)); + if (m_mode == EMode::CenterSelection) { + // Fake shift up event to exit the center selection mode + gizmo_event(SLAGizmoEventType::ShiftUp, Vec2d::Zero(), false, false, true); + // increase counter to avoid that keeping the SHIFT key pressed triggers a shift down event + m_shift_kar_filter.increase_count(); + } } if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) { @@ -369,21 +423,50 @@ void GLGizmoMeasure::data_changed() m_is_editing_distance_first_frame = true; } +static bool feature_has_center(std::optional feature) +{ + return feature.has_value() ? + (feature->get_type() == Measure::SurfaceFeatureType::Circle || (feature->get_type() == Measure::SurfaceFeatureType::Edge && feature->get_extra_point().has_value())) + : false; +} + bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { + auto activate_center_selection = [this, shift_down, control_down](SLAGizmoEventType action) { + bool ret = false; + switch (action) + { + case SLAGizmoEventType::CtrlDown: { ret = shift_down && feature_has_center(m_curr_feature); break; } + case SLAGizmoEventType::ShiftDown: { ret = control_down && feature_has_center(m_curr_feature); break; } + default: { break; } + } + return ret; + }; + if (action == SLAGizmoEventType::CtrlDown) { if (m_ctrl_kar_filter.is_first()) { - if (m_curr_feature.has_value()) { - m_mode = EMode::ExtendedSelection; - disable_scene_raycasters(); - } + m_mode = activate_center_selection(SLAGizmoEventType::CtrlDown) ? EMode::CenterSelection : EMode::PointSelection; + disable_scene_raycasters(); } - m_ctrl_kar_filter.increase_count(); } else if (action == SLAGizmoEventType::CtrlUp) { m_ctrl_kar_filter.reset_count(); - m_mode = EMode::BasicSelection; + m_mode = EMode::FeatureSelection; + restore_scene_raycasters_state(); + } + else if (action == SLAGizmoEventType::ShiftDown) { + if (m_shift_kar_filter.is_first()) { + if (activate_center_selection(SLAGizmoEventType::ShiftDown)) { + m_mode = EMode::CenterSelection; + disable_scene_raycasters(); + } + } + m_shift_kar_filter.increase_count(); + } + else if (action == SLAGizmoEventType::ShiftUp) { + m_shift_kar_filter.reset_count(); + m_mode = control_down ? EMode::PointSelection : EMode::FeatureSelection; restore_scene_raycasters_state(); } @@ -400,6 +483,7 @@ void GLGizmoMeasure::on_set_state() { if (m_state == Off) { m_ctrl_kar_filter.reset_count(); + m_shift_kar_filter.reset_count(); m_curr_feature.reset(); m_curr_point_on_feature_position.reset(); restore_scene_raycasters_state(); @@ -408,7 +492,7 @@ void GLGizmoMeasure::on_set_state() m_measuring.reset(); } else { - m_mode = EMode::BasicSelection; + m_mode = EMode::FeatureSelection; // store current state of scene raycaster for later use m_scene_raycasters.clear(); auto scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); @@ -467,7 +551,7 @@ void GLGizmoMeasure::on_render() Vec3f normal_on_model; size_t model_facet_idx; const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, m_volume_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); - const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1; + const bool is_hovering_on_feature = (m_mode == EMode::PointSelection || m_mode == EMode::CenterSelection) && m_hover_id != -1; auto update_circle = [this, inv_zoom]() { if (m_last_inv_zoom != inv_zoom || m_last_circle != m_curr_feature) { @@ -483,61 +567,75 @@ void GLGizmoMeasure::on_render() return false; }; - if (m_mode == EMode::BasicSelection) { - std::optional curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; - m_curr_point_on_feature_position.reset(); - if (m_curr_feature != curr_feature || - (curr_feature.has_value() && curr_feature->get_type() == Measure::SurfaceFeatureType::Circle && (m_curr_feature != curr_feature || m_last_inv_zoom != inv_zoom))) { + if (m_mode == EMode::FeatureSelection || m_mode == EMode::PointSelection) { + if ((m_hover_id == SELECTION_1_ID && boost::algorithm::istarts_with(m_selected_features.first.source, _u8L("Center"))) || + (m_hover_id == SELECTION_2_ID && boost::algorithm::istarts_with(m_selected_features.second.source, _u8L("Center")))) { + // Skip feature detection if hovering on a selected center + m_curr_feature.reset(); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); - m_raycasters.clear(); - m_curr_feature = curr_feature; - if (!m_curr_feature.has_value()) - return; + m_curr_point_on_feature_position.reset(); + } + else { + std::optional curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; + if (m_curr_feature != curr_feature || + (curr_feature.has_value() && curr_feature->get_type() == Measure::SurfaceFeatureType::Circle && (m_curr_feature != curr_feature || m_last_inv_zoom != inv_zoom))) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); + m_raycasters.clear(); + m_curr_feature = curr_feature; + if (!m_curr_feature.has_value()) + return; - switch (m_curr_feature->get_type()) { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); - if (m_curr_feature->get_extra_point().has_value()) + switch (m_curr_feature->get_type()) { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - update_circle(); - m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); - m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto [idx, normal, point] = m_curr_feature->get_plane(); - if (m_last_plane_idx != idx) { - m_last_plane_idx = idx; - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx); - m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(init_data.get_as_indexed_triangle_set())); - m_plane.model.init_from(std::move(init_data)); + break; } + case Measure::SurfaceFeatureType::Edge: + { + m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + if (m_curr_feature->get_extra_point().has_value()) + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + update_circle(); + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto [idx, normal, point] = m_curr_feature->get_plane(); + if (m_last_plane_idx != idx) { + m_last_plane_idx = idx; + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx); + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(init_data.get_as_indexed_triangle_set())); + m_plane.model.init_from(std::move(init_data)); + } - m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); - break; - } + m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); + break; + } + } } } } - else if (is_hovering_on_locked_feature) { + + if (m_mode != EMode::PointSelection) + m_curr_point_on_feature_position.reset(); + else if (is_hovering_on_feature) { auto position_on_feature = [this](int feature_type_id, const Camera& camera, std::function callback = nullptr) -> Vec3d { auto it = m_raycasters.find(feature_type_id); if (it != m_raycasters.end() && it->second != nullptr) { @@ -554,47 +652,49 @@ void GLGizmoMeasure::on_render() return Vec3d::Zero(); }; - switch (m_curr_feature->get_type()) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - m_curr_point_on_feature_position = m_curr_feature->get_point(); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - const std::optional extra = m_curr_feature->get_extra_point(); - if (extra.has_value() && m_hover_id == POINT_ID) - m_curr_point_on_feature_position = *extra; - else - m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(PLANE_ID, camera); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto [center, radius, normal] = m_curr_feature->get_circle(); - if (m_hover_id == POINT_ID) - m_curr_point_on_feature_position = center; - else { - const Vec3d world_pof = position_on_feature(CIRCLE_ID, camera, [](const Vec3f& v) { return v; }); - const Eigen::Hyperplane plane(m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()* normal, m_volume_matrix * center); - const Transform3d local_to_model_matrix = Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); - const Vec3d local_proj = local_to_model_matrix.inverse() * m_volume_matrix.inverse() * plane.projection(world_pof); - double angle = std::atan2(local_proj.y(), local_proj.x()); - if (angle < 0.0) - angle += 2.0 * double(M_PI); - - const Vec3d local_pos = radius * Vec3d(std::cos(angle), std::sin(angle), 0.0); - m_curr_point_on_feature_position = local_to_model_matrix * local_pos; + if (m_curr_feature.has_value()) { + switch (m_curr_feature->get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + m_curr_point_on_feature_position = m_curr_feature->get_point(); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const std::optional extra = m_curr_feature->get_extra_point(); + if (extra.has_value() && m_hover_id == POINT_ID) + m_curr_point_on_feature_position = *extra; + else + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(PLANE_ID, camera); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto [center, radius, normal] = m_curr_feature->get_circle(); + if (m_hover_id == POINT_ID) + m_curr_point_on_feature_position = center; + else { + const Vec3d world_pof = position_on_feature(CIRCLE_ID, camera, [](const Vec3f& v) { return v; }); + const Eigen::Hyperplane plane(m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal, m_volume_matrix * center); + const Transform3d local_to_model_matrix = Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + const Vec3d local_proj = local_to_model_matrix.inverse() * m_volume_matrix.inverse() * plane.projection(world_pof); + double angle = std::atan2(local_proj.y(), local_proj.x()); + if (angle < 0.0) + angle += 2.0 * double(M_PI); + + const Vec3d local_pos = radius * Vec3d(std::cos(angle), std::sin(angle), 0.0); + m_curr_point_on_feature_position = local_to_model_matrix * local_pos; + } + break; + } } - break; - } } } else { @@ -617,7 +717,6 @@ void GLGizmoMeasure::on_render() return; shader->start_using(); - shader->set_uniform("emission_factor", 0.25f); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); @@ -634,8 +733,13 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_normal_matrix", view_normal_matrix); }; - auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, - float inv_zoom, bool update_raycasters_transform) { + auto set_emission_uniform = [this, shader](const ColorRGBA& color, bool hover) { + shader->set_uniform("emission_factor", (color == m_parent.get_selection().get_first_volume()->render_color) ? 0.0f : + hover ? 0.5f : 0.25f); + }; + + auto render_feature = [this, set_matrix_uniforms, set_emission_uniform](const Measure::SurfaceFeature& feature, const std::vector& colors, + float inv_zoom, bool hover, bool update_raycasters_transform) { switch (feature.get_type()) { default: { assert(false); break; } @@ -644,6 +748,7 @@ void GLGizmoMeasure::on_render() const Vec3d position = TransformHelper::model_to_world(feature.get_point(), m_volume_matrix); const Transform3d feature_matrix = Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(feature_matrix); + set_emission_uniform(colors.front(), hover); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); if (update_raycasters_transform) { @@ -657,32 +762,37 @@ void GLGizmoMeasure::on_render() { const auto& [center, radius, normal] = feature.get_circle(); // render center - const Vec3d center_world = TransformHelper::model_to_world(center, m_volume_matrix); - const Transform3d center_matrix = Geometry::translation_transform(center_world) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(center_matrix); - m_sphere.model.set_color(colors.front()); - m_sphere.model.render(); if (update_raycasters_transform) { + const Vec3d center_world = TransformHelper::model_to_world(center, m_volume_matrix); + const Transform3d center_matrix = Geometry::translation_transform(center_world) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(center_matrix); + set_emission_uniform(colors.front(), hover); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(center_matrix); } // render circle - const Transform3d circle_matrix = Transform3d::Identity(); - set_matrix_uniforms(circle_matrix); - if (update_raycasters_transform) { - m_circle.model.set_color(colors.back()); - m_circle.model.render(); - auto it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(circle_matrix); - } - else { - GLModel circle; - GLModel::Geometry circle_geometry = init_torus_data(64, 16, center.cast(), float(radius), 5.0f * inv_zoom, normal.cast(), m_volume_matrix.cast()); - circle.init_from(std::move(circle_geometry)); - circle.set_color(colors.back()); - circle.render(); + if (m_mode != EMode::CenterSelection) { + const Transform3d circle_matrix = Transform3d::Identity(); + set_matrix_uniforms(circle_matrix); + if (update_raycasters_transform) { + set_emission_uniform(colors.back(), hover); + m_circle.model.set_color(colors.back()); + m_circle.model.render(); + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); + } + else { + GLModel circle; + GLModel::Geometry circle_geometry = init_torus_data(64, 16, center.cast(), float(radius), 5.0f * inv_zoom, normal.cast(), m_volume_matrix.cast()); + circle.init_from(std::move(circle_geometry)); + set_emission_uniform(colors.back(), hover); + circle.set_color(colors.back()); + circle.render(); + } } break; } @@ -690,42 +800,50 @@ void GLGizmoMeasure::on_render() { const auto& [from, to] = feature.get_edge(); // render extra point - const std::optional extra = feature.get_extra_point(); - if (extra.has_value()) { - const Vec3d extra_world = TransformHelper::model_to_world(*extra, m_volume_matrix); - const Transform3d point_matrix = Geometry::translation_transform(extra_world) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(point_matrix); - m_sphere.model.set_color(colors.front()); - m_sphere.model.render(); - if (update_raycasters_transform) { + if (update_raycasters_transform) { + const std::optional extra = feature.get_extra_point(); + if (extra.has_value()) { + const Vec3d extra_world = TransformHelper::model_to_world(*extra, m_volume_matrix); + const Transform3d point_matrix = Geometry::translation_transform(extra_world) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(point_matrix); + set_emission_uniform(colors.front(), hover); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(point_matrix); } } // render edge - const Vec3d from_world = TransformHelper::model_to_world(from, m_volume_matrix); - const Vec3d to_world = TransformHelper::model_to_world(to, m_volume_matrix); - const Transform3d edge_matrix = Geometry::translation_transform(from_world) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), to_world - from_world) * - Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (to_world - from_world).norm() }); - set_matrix_uniforms(edge_matrix); - m_cylinder.model.set_color(colors.back()); - m_cylinder.model.render(); - if (update_raycasters_transform) { - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(edge_matrix); + if (m_mode != EMode::CenterSelection) { + const Vec3d from_world = TransformHelper::model_to_world(from, m_volume_matrix); + const Vec3d to_world = TransformHelper::model_to_world(to, m_volume_matrix); + const Transform3d edge_matrix = Geometry::translation_transform(from_world) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), to_world - from_world) * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (to_world - from_world).norm() }); + set_matrix_uniforms(edge_matrix); + set_emission_uniform(colors.back(), hover); + m_cylinder.model.set_color(colors.back()); + m_cylinder.model.render(); + if (update_raycasters_transform) { + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(edge_matrix); + } } break; } case Measure::SurfaceFeatureType::Plane: { - const auto& [idx, normal, pt] = feature.get_plane(); - assert(idx < m_plane_models_cache.size()); - set_matrix_uniforms(m_volume_matrix); - m_plane_models_cache[idx].set_color(colors.front()); - m_plane_models_cache[idx].render(); + // no need to render the plane in case it is rendered with the same color as the volume in the 3D scene + if (colors.front() != m_parent.get_selection().get_first_volume()->render_color) { + const auto& [idx, normal, pt] = feature.get_plane(); + assert(idx < m_plane_models_cache.size()); + set_matrix_uniforms(m_volume_matrix); + set_emission_uniform(colors.front(), hover); + m_plane_models_cache[idx].set_color(colors.front()); + m_plane_models_cache[idx].render(); + } if (update_raycasters_transform) { auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -737,11 +855,11 @@ void GLGizmoMeasure::on_render() }; auto hover_selection_color = [this]() { - return saturate(!m_selected_features.first.feature.has_value() ? SELECTED_1ST_COLOR : SELECTED_2ND_COLOR, 1.5f); + return !m_selected_features.first.feature.has_value() ? SELECTED_1ST_COLOR : SELECTED_2ND_COLOR; }; auto hovering_color = [this, hover_selection_color, &selection]() { - return (m_mode == EMode::ExtendedSelection) ? selection.get_first_volume()->render_color : hover_selection_color(); + return (m_mode == EMode::PointSelection) ? selection.get_first_volume()->render_color : hover_selection_color(); }; if (m_curr_feature.has_value()) { @@ -774,12 +892,12 @@ void GLGizmoMeasure::on_render() } } - render_feature(*m_curr_feature, colors, inv_zoom, true); + render_feature(*m_curr_feature, colors, inv_zoom, true, true); } if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { const std::vector colors = { SELECTED_1ST_COLOR }; - render_feature(*m_selected_features.first.feature, colors, inv_zoom, false); + render_feature(*m_selected_features.first.feature, colors, inv_zoom, m_hover_id == SELECTION_1_ID, false); if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_1_ID; }); @@ -789,7 +907,7 @@ void GLGizmoMeasure::on_render() } if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { const std::vector colors = { SELECTED_2ND_COLOR }; - render_feature(*m_selected_features.second.feature, colors, inv_zoom, false); + render_feature(*m_selected_features.second.feature, colors, inv_zoom, m_hover_id == SELECTION_2_ID, false); if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; }); @@ -798,12 +916,14 @@ void GLGizmoMeasure::on_render() } } - if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { + if (is_hovering_on_feature && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { const Vec3d position = TransformHelper::model_to_world(*m_curr_point_on_feature_position, m_volume_matrix); const Transform3d matrix = Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); - m_sphere.model.set_color(hover_selection_color()); + const ColorRGBA color = hover_selection_color(); + set_emission_uniform(color, true); + m_sphere.model.set_color(color); m_sphere.model.render(); } } @@ -1343,12 +1463,15 @@ void GLGizmoMeasure::render_debug_dialog() { case Measure::SurfaceFeatureType::Point: { - add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(item.feature->get_point()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + const Vec3d position = m_volume_matrix * item.feature->get_point(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Edge: { auto [from, to] = item.feature->get_edge(); + from = m_volume_matrix * from; + to = m_volume_matrix * to; add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; @@ -1356,6 +1479,8 @@ void GLGizmoMeasure::render_debug_dialog() case Measure::SurfaceFeatureType::Plane: { auto [idx, normal, origin] = item.feature->get_plane(); + origin = m_volume_matrix * origin; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(idx), ImGui::GetStyleColorVec4(ImGuiCol_Text)); @@ -1364,6 +1489,10 @@ void GLGizmoMeasure::render_debug_dialog() case Measure::SurfaceFeatureType::Circle: { auto [center, radius, normal] = item.feature->get_circle(); + const Vec3d on_circle = m_volume_matrix * (center + radius * Measure::get_orthogonal(normal, true)); + center = m_volume_matrix * center; + normal = (m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal).normalized(); + radius = (on_circle - center).norm(); add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); @@ -1376,6 +1505,28 @@ void GLGizmoMeasure::render_debug_dialog() }; m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + if (ImGui::BeginTable("Mode", 2)) { + std::string txt; + switch (m_mode) + { + case EMode::FeatureSelection: { txt = "Feature selection"; break; } + case EMode::PointSelection: { txt = "Point selection"; break; } + case EMode::CenterSelection: { txt = "Center selection"; break; } + default: { assert(false); break; } + } + add_strings_row_to_table(*m_imgui, "Mode", ImGuiWrapper::COL_ORANGE_LIGHT, txt, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::EndTable(); + } + + ImGui::Separator(); + if (ImGui::BeginTable("Hover", 2)) { + add_strings_row_to_table(*m_imgui, "Hover id", ImGuiWrapper::COL_ORANGE_LIGHT, std::to_string(m_hover_id), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + const std::string txt = m_curr_feature.has_value() ? surface_feature_type_as_string(m_curr_feature->get_type()) : "None"; + add_strings_row_to_table(*m_imgui, "Current feature", ImGuiWrapper::COL_ORANGE_LIGHT, txt, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::EndTable(); + } + + ImGui::Separator(); if (!m_selected_features.first.feature.has_value() && !m_selected_features.second.feature.has_value()) m_imgui->text("Empty selection"); else { @@ -1402,7 +1553,7 @@ void GLGizmoMeasure::render_debug_dialog() void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; - static EMode last_mode = EMode::BasicSelection; + static EMode last_mode = EMode::FeatureSelection; static SelectedFeatures last_selected_features; static float last_y = 0.0f; @@ -1436,12 +1587,13 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit std::string text; ColorRGBA color; if (m_selected_features.second.feature.has_value()) { - if (m_selected_features.second.feature == m_curr_feature && m_mode == EMode::BasicSelection) + if (m_selected_features.second.feature == m_curr_feature && m_mode == EMode::FeatureSelection) text = _u8L("Unselect feature"); else if (m_hover_id == SELECTION_2_ID) - text = _u8L("Unselect point"); + text = (m_mode == EMode::CenterSelection) ? _u8L("Unselect center") : _u8L("Unselect point"); else - text = (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"); + text = (m_mode == EMode::PointSelection) ? _u8L("Select point") : + (m_mode == EMode::CenterSelection) ? _u8L("Select center") : _u8L("Select feature"); color = SELECTED_2ND_COLOR; } else { @@ -1449,11 +1601,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (m_selected_features.first.feature == m_curr_feature) text = _u8L("Unselect feature"); else if (m_hover_id == SELECTION_1_ID) - text = _u8L("Unselect point"); + text = (m_mode == EMode::CenterSelection) ? _u8L("Unselect center") : _u8L("Unselect point"); color = SELECTED_1ST_COLOR; } if (text.empty()) { - text = (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"); + text = (m_mode == EMode::PointSelection) ? _u8L("Select point") : + (m_mode == EMode::CenterSelection) ? _u8L("Select center") : _u8L("Select feature"); color = m_selected_features.first.feature.has_value() ? SELECTED_2ND_COLOR : SELECTED_1ST_COLOR; } } @@ -1467,18 +1620,23 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ); + if (m_mode == EMode::FeatureSelection && m_hover_id != -1) { + add_strings_row_to_table(*m_imgui, CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++row_count; + } + + if (m_mode != EMode::CenterSelection && feature_has_center(m_curr_feature)) { + add_strings_row_to_table(*m_imgui, CTRL_STR + "+" + _u8L("Shift"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable center selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++row_count; + } + if (m_selected_features.first.feature.has_value()) { add_strings_row_to_table(*m_imgui, CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ++row_count; } - if (m_mode == EMode::BasicSelection && m_hover_id != -1) { - add_strings_row_to_table(*m_imgui, CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ++row_count; - } - // add dummy rows to keep dialog size fixed - for (unsigned int i = row_count; i < 3; ++i) { + for (unsigned int i = row_count; i < 4; ++i) { add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); } @@ -1492,16 +1650,22 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit //bool data_text_set = false; //ImGui::Separator(); //if (feature_type != Measure::SurfaceFeatureType::Undef) { - // if (m_mode == EMode::BasicSelection) { + // if (m_mode == EMode::FeatureSelection) { // m_imgui->text(surface_feature_type_as_string(feature_type)); // data_text_set = true; // } - // else if (m_mode == EMode::ExtendedSelection) { + // else if (m_mode == EMode::PointSelection) { // if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { // m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id)); // data_text_set = true; // } // } + // else if (m_mode == EMode::CenterSelection) { + // if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { + // m_imgui->text(center_on_feature_type_as_string(feature_type)); + // data_text_set = true; + // } + // } //} //if (!data_text_set) // m_imgui->text(_u8L("No feature")); @@ -1509,7 +1673,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit //const unsigned int max_data_row_count = 3; //unsigned int data_row_count = 0; //if (ImGui::BeginTable("Data", 2)) { - // if (m_mode == EMode::BasicSelection) { + // if (m_mode == EMode::FeatureSelection) { // switch (feature_type) // { // default: { break; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 4426457ff..7bfc2e8c9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -23,8 +23,9 @@ class GLGizmoMeasure : public GLGizmoBase { enum class EMode : unsigned char { - BasicSelection, - ExtendedSelection + FeatureSelection, + PointSelection, + CenterSelection }; struct SelectedFeatures @@ -67,7 +68,7 @@ class GLGizmoMeasure : public GLGizmoBase } }; - EMode m_mode{ EMode::BasicSelection }; + EMode m_mode{ EMode::FeatureSelection }; Measure::MeasurementResult m_measurement_result; std::unique_ptr m_measuring; // PIMPL @@ -114,6 +115,7 @@ class GLGizmoMeasure : public GLGizmoBase Vec2d m_mouse_pos{ Vec2d::Zero() }; KeyAutoRepeatFilter m_ctrl_kar_filter; + KeyAutoRepeatFilter m_shift_kar_filter; SelectedFeatures m_selected_features; bool m_pending_scale{ false }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 757940226..42faa25f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -26,6 +26,7 @@ enum class SLAGizmoEventType : unsigned char { SelectAll, CtrlDown, CtrlUp, + ShiftDown, ShiftUp, AltUp, ApplyChanges, diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 51a0386ed..07b4baa35 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -606,10 +606,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) const int keyCode = evt.GetKeyCode(); bool processed = false; - if (evt.GetEventType() == wxEVT_KEY_UP) - { - if (m_current == SlaSupports || m_current == Hollow || m_current == Cut) - { + if (evt.GetEventType() == wxEVT_KEY_UP) { + if (m_current == SlaSupports || m_current == Hollow || m_current == Cut) { GLGizmoBase* gizmo = get_current(); const bool is_editing = m_current == Hollow ? true : gizmo->is_in_editing_mode(); const bool is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); @@ -625,18 +623,19 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) processed = true; } } - else if (m_current == Measure && keyCode == WXK_CONTROL) { - gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), false); + else if (m_current == Measure) { + if (keyCode == WXK_CONTROL) + gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown()); + else if (keyCode == WXK_SHIFT) + gizmo_event(SLAGizmoEventType::ShiftUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown()); } // if (processed) // m_parent.set_cursor(GLCanvas3D::Standard); } - else if (evt.GetEventType() == wxEVT_KEY_DOWN) - { - if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) - && get_current()->is_in_editing_mode()) - { + else if (evt.GetEventType() == wxEVT_KEY_DOWN) { + if (m_current == SlaSupports && (keyCode == WXK_SHIFT || keyCode == WXK_ALT) + && get_current()->is_in_editing_mode()) { // m_parent.set_cursor(GLCanvas3D::Cross); processed = true; } @@ -662,8 +661,11 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) if (simplify != nullptr) processed = simplify->on_esc_key_down(); } - else if (m_current == Measure && keyCode == WXK_CONTROL) { - gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), true); + else if (m_current == Measure) { + if (keyCode == WXK_CONTROL) + gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown()); + else if (keyCode == WXK_SHIFT) + gizmo_event(SLAGizmoEventType::ShiftDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown()); } }