From 595ef873ad0fb3c52ce3705bc80569326c089ec3 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 7 Dec 2022 15:23:38 +0100 Subject: [PATCH] Change the way how cut gizmo detects hits on cut plane: it did not work well on meshes with overlapping surfaces --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 73 +++++++++++++--------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- src/slic3r/GUI/MeshUtils.cpp | 45 ++--------------- src/slic3r/GUI/MeshUtils.hpp | 8 +-- 4 files changed, 40 insertions(+), 88 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 29d578fba9..3cd661ba3f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1144,11 +1144,11 @@ void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data) void GLGizmoCut3D::dragging_connector(const GLGizmoBase::UpdateData &data) { CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - std::pair pos_and_normal; + Vec3d pos; Vec3d pos_world; - if (unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal, pos_world)) { - connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first; + if (unproject_on_cut_plane(data.mouse_pos.cast(), pos, pos_world)) { + connectors[m_hover_id - m_connectors_group_id].pos = pos; update_raycasters_for_picking_transform(); } } @@ -2006,44 +2006,41 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal // Return false if no intersection was found, true otherwise. -bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair& pos_and_normal, Vec3d& pos_world) +bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& pos, Vec3d& pos_world) { const float sla_shift = m_c->selection_info()->get_sla_shift(); const ModelObject* mo = m_c->selection_info()->model_object(); const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; - const Transform3d instance_trafo = sla_shift > 0.f ? - translation_transform(sla_shift * Vec3d::UnitZ()) * mi->get_transformation().get_matrix() : mi->get_transformation().get_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); - int mesh_id = -1; - for (const ModelVolume* mv : mo->volumes) { - ++mesh_id; - if (!mv->is_model_part()) - continue; - Vec3f normal; - Vec3f hit; - bool clipping_plane_was_hit = false; + // Calculate intersection with the clipping plane. + const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(true); + Vec3d point; + Vec3d direction; + Vec3d hit; + MeshRaycaster::line_from_mouse_pos(mouse_position, Transform3d::Identity(), camera, point, direction); + Vec3d normal = -cp->get_normal().cast(); + double den = normal.dot(direction); + if (den != 0.) { + double t = (-cp->get_offset() - normal.dot(point))/den; + hit = (point + t * direction); + } else + return false; + + if (! m_c->object_clipper()->is_projection_inside_cut(hit)) + return false; -// const Transform3d volume_trafo = get_volume_transformation(mv); - const Transform3d volume_trafo = mv->get_transformation().get_matrix(); + // recalculate hit to object's local position + Vec3d hit_d = hit; + hit_d -= mi->get_offset(); + hit_d[Z] -= sla_shift; - m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * volume_trafo, - camera, hit, normal, m_c->object_clipper()->get_clipping_plane(true), - nullptr, &clipping_plane_was_hit); - if (clipping_plane_was_hit) { - // recalculate hit to object's local position - Vec3d hit_d = hit.cast(); - hit_d -= mi->get_offset(); - hit_d[Z] -= sla_shift; + // Return both the point and the facet normal. + pos = hit_d; + pos_world = hit; - // Return both the point and the facet normal. - pos_and_normal = std::make_pair(hit_d, normal.cast()); - pos_world = hit.cast(); - return true; - } - } - return false; + return true; } void GLGizmoCut3D::clear_selection() @@ -2139,17 +2136,13 @@ bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_p if (!m_connectors_editing) return false; - std::pair pos_and_normal; + Vec3d pos; Vec3d pos_world; - if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal, pos_world)) { - // check if pos is out of enabled clipping plane - if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(pos_world)) - return true; - + if (unproject_on_cut_plane(mouse_position.cast(), pos, pos_world)) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); unselect_all_connectors(); - connectors.emplace_back(pos_and_normal.first, m_rotation_m, + connectors.emplace_back(pos, m_rotation_m, m_connector_size * 0.5f, m_connector_depth_ratio, m_connector_size_tolerance, m_connector_depth_ratio_tolerance, CutConnectorAttributes( CutConnectorType(m_connector_type), @@ -2247,9 +2240,9 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi if (!m_connectors_editing) { if (0 && action == SLAGizmoEventType::LeftDown) { // disable / enable current contour - std::pair pos_and_normal; + Vec3d pos; Vec3d pos_world; - if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal, pos_world)) { + if (unproject_on_cut_plane(mouse_position.cast(), pos, pos_world)) { // Following would inform the clipper about the mouse click, so it can // toggle the respective contour as disabled. m_c->object_clipper()->pass_mouse_click(pos_world); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 8a3ee6d074..e9412357d7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -155,7 +155,7 @@ public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); std::string get_tooltip() const override; - bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair& pos_and_normal, Vec3d& pos_world); + bool unproject_on_cut_plane(const Vec2d& mouse_pos, Vec3d& pos, Vec3d& pos_world); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); bool is_in_editing_mode() const override { return m_connectors_editing; } diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 39e463a19b..a581915282 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -365,11 +365,8 @@ void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3 bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane, - size_t* facet_idx, bool* was_clipping_plane_hit) const + size_t* facet_idx) const { - if (was_clipping_plane_hit) - *was_clipping_plane_hit = false; - Vec3d point; Vec3d direction; line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); @@ -390,26 +387,9 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& break; } - if (i==hits.size()) { - // All hits are clipped. - return false; - } - if (clipping_plane && (hits.size()-i) % 2 != 0) { - // There is an odd number of unclipped hits - meaning the nearest must be from inside the mesh. - // In that case, calculate intersection with the clipping place. - if (was_clipping_plane_hit) { - direction = direction + point; - point = trafo * point; // transform to world coords - direction = trafo * direction - point; - - Vec3d normal = -clipping_plane->get_normal().cast(); - double den = normal.dot(direction); - if (den != 0.) { - double t = (-clipping_plane->get_offset() - normal.dot(point))/den; - position = (point + t * direction).cast(); - *was_clipping_plane_hit = true; - } - } + if (i==hits.size() || (hits.size()-i) % 2 != 0) { + // All hits are either clipped, or there is an odd number of unclipped + // hits - meaning the nearest must be from inside the mesh. return false; } @@ -423,24 +403,7 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& return true; } -bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - Vec3d& position, Vec3d& normal) const -{ - Vec3d point; - Vec3d direction; - line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); - std::vector hits = m_emesh.query_ray_hits(point, direction); - - if (hits.empty()) - return false; // no intersection found - - // Now stuff the points in the provided vector and calculate normals if asked about them: - position = hits[0].position(); - normal = hits[0].normal(); - - return true; -} bool MeshRaycaster::is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const { diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 441532fdb2..401ba22f17 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -153,16 +153,12 @@ public: const Vec2d& mouse_pos, const Transform3d& trafo, // how to get the mesh into world coords const Camera& camera, // current camera position - Vec3f& position, // where to save the positibon of the hit (mesh coords if mesh, world coords if clipping plane) + Vec3f& position, // where to save the positibon of the hit (mesh coords) Vec3f& normal, // normal of the triangle that was hit const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active) - size_t* facet_idx = nullptr, // index of the facet hit - bool* was_clipping_plane_hit = nullptr // is the hit on the clipping place cross section? + size_t* facet_idx = nullptr // index of the facet hit ) const; - // Given a mouse position, this returns true in case it is on the mesh. - bool unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& position, Vec3d& normal) const; - bool is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const; // Given a vector of points in woorld coordinates, this returns vector