diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 302ce06fe5..ad292023bb 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -247,21 +247,15 @@ void TriangleSelector::select_patch(int facet_start, std::unique_ptr &&c // have to pass it around. m_cursor = std::move(cursor); - // In case user changed cursor size since last time, update triangle edge limit. - // It is necessary to compare the internal radius in m_cursor! radius is in - // world coords and does not change after scaling. - if (m_old_cursor_radius_sqr != m_cursor->radius_sqr) { - set_edge_limit(std::sqrt(m_cursor->radius_sqr) / 5.f); - m_old_cursor_radius_sqr = m_cursor->radius_sqr; - } + // In case user changed cursor parameters size since last time, update triangle edge limit. + set_edge_limit(m_cursor->get_edge_limit()); const float highlight_angle_limit = cos(Geometry::deg2rad(highlight_by_angle_deg)); Vec3f vec_down = (trafo_no_translate.inverse() * -Vec3d::UnitZ()).normalized().cast(); // Now start with the facet the pointer points to and check all adjacent facets. - std::vector facets_to_check; + std::vector facets_to_check = m_cursor->get_facets_to_select(facet_start, m_vertices, m_triangles, m_orig_size_vertices, m_orig_size_indices); facets_to_check.reserve(16); - facets_to_check.emplace_back(facet_start); // Keep track of facets of the original mesh we already processed. std::vector visited(m_orig_size_indices, false); // Breadth-first search around the hit point. facets_to_check may grow significantly large. @@ -293,13 +287,13 @@ bool TriangleSelector::is_facet_clipped(int facet_idx, const ClippingPlane &clp) } void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start, const Transform3d& trafo_no_translate, - const ClippingPlane &clp, float seed_fill_angle, float highlight_by_angle_deg, - bool force_reselection) + const ClippingPlane &clp, float seed_fill_angle, + float highlight_by_angle_deg, ForceReselection force_reselection) { assert(facet_start < m_orig_size_indices); // Recompute seed fill only if the cursor is pointing on facet unselected by seed fill or a clipping plane is active. - if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection && !clp.is_active()) + if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill() && force_reselection == ForceReselection::NO && !clp.is_active()) return; this->seed_fill_unselect_all_triangles(); @@ -451,19 +445,20 @@ void TriangleSelector::append_touching_edges(int itriangle, int vertexi, int ver process_subtriangle(touching.second, Partition::Second); } -void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, const ClippingPlane &clp, bool propagate, bool force_reselection) -{ +void TriangleSelector::bucket_fill_select_triangles(const Vec3f &hit, int facet_start, const ClippingPlane &clp, + BucketFillPropagate propagate, + ForceReselection force_reselection) { int start_facet_idx = select_unsplit_triangle(hit, facet_start); assert(start_facet_idx != -1); // Recompute bucket fill only if the cursor is pointing on facet unselected by bucket fill or a clipping plane is active. - if (start_facet_idx == -1 || (m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection && !clp.is_active())) + if (start_facet_idx == -1 || (m_triangles[start_facet_idx].is_selected_by_seed_fill() && force_reselection == ForceReselection::NO && !clp.is_active())) return; assert(!m_triangles[start_facet_idx].is_split()); TriangleStateType start_facet_state = m_triangles[start_facet_idx].get_state(); this->seed_fill_unselect_all_triangles(); - if (!propagate) { + if (propagate == BucketFillPropagate::NO) { m_triangles[start_facet_idx].select_by_seed_fill(); return; } @@ -878,7 +873,7 @@ bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i &nei if (num_of_inside_vertices == 0 && ! m_cursor->is_pointer_in_triangle(*tr, m_vertices) - && ! m_cursor->is_edge_inside_cursor(*tr, m_vertices)) + && ! m_cursor->is_any_edge_inside_cursor(*tr, m_vertices)) return false; if (num_of_inside_vertices == 3) { @@ -949,8 +944,8 @@ void TriangleSelector::split_triangle(int facet_idx, const Vec3i &neighbors) // In case the object is non-uniformly scaled, transform the // points to world coords. - if (! m_cursor->uniform_scaling) { - for (size_t i=0; iuniform_scaling) { + for (size_t i = 0; i < pts.size(); ++i) { pts_transformed[i] = m_cursor->trafo * (*pts[i]); pts[i] = &pts_transformed[i]; } @@ -1011,16 +1006,21 @@ int TriangleSelector::Cursor::vertices_inside(const Triangle &tr, const std::vec return inside; } -// Is any edge inside Sphere cursor? -bool TriangleSelector::Sphere::is_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const -{ +inline std::array TriangleSelector::Cursor::transform_triangle(const Triangle &tr, const std::vector &vertices) const { std::array pts; - for (int i = 0; i < 3; ++i) { + for (size_t i = 0; i < 3; ++i) { pts[i] = vertices[tr.verts_idxs[i]].v; if (!this->uniform_scaling) pts[i] = this->trafo * pts[i]; } + return pts; +} + +// Is any edge inside Sphere cursor? +bool TriangleSelector::Sphere::is_any_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const +{ + const std::array pts = this->transform_triangle(tr, vertices); for (int side = 0; side < 3; ++side) { const Vec3f &edge_a = pts[side]; const Vec3f &edge_b = pts[side < 2 ? side + 1 : 0]; @@ -1031,16 +1031,10 @@ bool TriangleSelector::Sphere::is_edge_inside_cursor(const Triangle &tr, const s } // Is edge inside cursor? -bool TriangleSelector::Circle::is_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const +bool TriangleSelector::Circle::is_any_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const { - std::array pts; - for (int i = 0; i < 3; ++i) { - pts[i] = vertices[tr.verts_idxs[i]].v; - if (!this->uniform_scaling) - pts[i] = this->trafo * pts[i]; - } - - const Vec3f &p = this->center; + const std::array pts = this->transform_triangle(tr, vertices); + const Vec3f &p = this->center; for (int side = 0; side < 3; ++side) { const Vec3f &a = pts[side]; const Vec3f &b = pts[side < 2 ? side + 1 : 0]; @@ -1216,7 +1210,7 @@ void TriangleSelector::reset() void TriangleSelector::set_edge_limit(float edge_limit) { - m_edge_limit_sqr = std::pow(edge_limit, 2.f); + m_edge_limit_sqr = Slic3r::sqr(edge_limit); } int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, const TriangleStateType state) { @@ -1890,6 +1884,8 @@ TriangleSelector::Cursor::Cursor(const Vec3f &source_, float radius_world, const radius_sqr = Slic3r::sqr(radius_world); trafo_normal = trafo.linear().inverse().transpose(); } + + m_edge_limit = std::sqrt(radius_sqr) / 5.f; } TriangleSelector::SinglePointCursor::SinglePointCursor(const Vec3f& center_, const Vec3f& source_, float radius_world, const Transform3d& trafo_, const ClippingPlane &clipping_plane_) @@ -2048,15 +2044,9 @@ bool line_plane_intersection(const Vec3f &line_a, const Vec3f &line_b, const Vec return false; } -bool TriangleSelector::Capsule3D::is_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const +bool TriangleSelector::Capsule3D::is_any_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const { - std::array pts; - for (int i = 0; i < 3; ++i) { - pts[i] = vertices[tr.verts_idxs[i]].v; - if (!this->uniform_scaling) - pts[i] = this->trafo * pts[i]; - } - + const std::array pts = this->transform_triangle(tr, vertices); for (int side = 0; side < 3; ++side) { const Vec3f &edge_a = pts[side]; const Vec3f &edge_b = pts[side < 2 ? side + 1 : 0]; @@ -2068,15 +2058,8 @@ bool TriangleSelector::Capsule3D::is_edge_inside_cursor(const Triangle &tr, cons } // Is edge inside cursor? -bool TriangleSelector::Capsule2D::is_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const +bool TriangleSelector::Capsule2D::is_any_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const { - std::array pts; - for (int i = 0; i < 3; ++i) { - pts[i] = vertices[tr.verts_idxs[i]].v; - if (!this->uniform_scaling) - pts[i] = this->trafo * pts[i]; - } - const Vec3f centers_diff = this->second_center - this->first_center; // Vector in the direction of line |AD| of the rectangle that intersects the circle with the center in first_center. const Vec3f rectangle_da_dir = centers_diff.cross(this->dir); @@ -2095,6 +2078,7 @@ bool TriangleSelector::Capsule2D::is_edge_inside_cursor(const Triangle &tr, cons return false; }; + const std::array pts = this->transform_triangle(tr, vertices); for (int side = 0; side < 3; ++side) { const Vec3f &edge_a = pts[side]; const Vec3f &edge_b = pts[side < 2 ? side + 1 : 0]; diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 2cd5a0a980..2605d28a52 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -67,12 +67,22 @@ protected: struct Vertex; public: - enum CursorType { + enum class CursorType { CIRCLE, SPHERE, POINTER }; + enum class ForceReselection { + NO, + YES + }; + + enum class BucketFillPropagate { + NO, + YES + }; + struct ClippingPlane { Vec3f normal; @@ -91,14 +101,21 @@ public: Cursor() = delete; virtual ~Cursor() = default; + float get_edge_limit() { return m_edge_limit; }; + bool is_pointer_in_triangle(const Triangle &tr, const std::vector &vertices) const; + std::array transform_triangle(const Triangle &tr, const std::vector &vertices) const; virtual bool is_mesh_point_inside(const Vec3f &point) const = 0; virtual bool is_pointer_in_triangle(const Vec3f &p1, const Vec3f &p2, const Vec3f &p3) const = 0; virtual int vertices_inside(const Triangle &tr, const std::vector &vertices) const; - virtual bool is_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const = 0; + virtual bool is_any_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const = 0; virtual bool is_facet_visible(int facet_idx, const std::vector &face_normals) const = 0; + virtual std::vector get_facets_to_select(int facet_idx, const std::vector &vertices, const std::vector &triangles, int orig_size_vertices, int orig_size_indices) const { + return { facet_idx }; + }; + static bool is_facet_visible(const Cursor &cursor, int facet_idx, const std::vector &face_normals); protected: @@ -115,6 +132,8 @@ public: ClippingPlane clipping_plane; // Clipping plane to limit painting to not clipped facets only + float m_edge_limit; + friend TriangleSelector; }; @@ -174,7 +193,7 @@ public: ~Sphere() override = default; bool is_mesh_point_inside(const Vec3f &point) const override; - bool is_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const override; + bool is_any_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const override; bool is_facet_visible(int facet_idx, const std::vector &face_normals) const override { return true; } }; @@ -187,9 +206,8 @@ public: ~Circle() override = default; bool is_mesh_point_inside(const Vec3f &point) const override; - bool is_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const override; - bool is_facet_visible(int facet_idx, const std::vector &face_normals) const override - { + bool is_any_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const override; + bool is_facet_visible(int facet_idx, const std::vector &face_normals) const override { return TriangleSelector::Cursor::is_facet_visible(*this, facet_idx, face_normals); } }; @@ -204,7 +222,7 @@ public: ~Capsule3D() override = default; bool is_mesh_point_inside(const Vec3f &point) const override; - bool is_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const override; + bool is_any_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const override; bool is_facet_visible(int facet_idx, const std::vector &face_normals) const override { return true; } }; @@ -218,9 +236,8 @@ public: ~Capsule2D() override = default; bool is_mesh_point_inside(const Vec3f &point) const override; - bool is_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const override; - bool is_facet_visible(int facet_idx, const std::vector &face_normals) const override - { + bool is_any_edge_inside_cursor(const Triangle &tr, const std::vector &vertices) const override; + bool is_facet_visible(int facet_idx, const std::vector &face_normals) const override { return TriangleSelector::Cursor::is_facet_visible(*this, facet_idx, face_normals); } }; @@ -298,19 +315,19 @@ public: bool triangle_splitting, // If triangles will be split base on the cursor or not float highlight_by_angle_deg = 0.f); // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees. - void seed_fill_select_triangles(const Vec3f &hit, // point where to start - int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to - const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation - const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only - float seed_fill_angle, // the maximal angle between two facets to be painted by the same color - float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees. - bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle + void seed_fill_select_triangles(const Vec3f &hit, // point where to start + int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to + const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation + const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only + float seed_fill_angle, // the maximal angle between two facets to be painted by the same color + float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees. + ForceReselection force_reselection = ForceReselection::NO); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle - void bucket_fill_select_triangles(const Vec3f &hit, // point where to start - int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to - const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only - bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to. - bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle + void bucket_fill_select_triangles(const Vec3f &hit, // point where to start + int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to + const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only + BucketFillPropagate propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to. + ForceReselection force_reselection = ForceReselection::NO); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle bool has_facets(TriangleStateType state) const; static bool has_facets(const TriangleSplittingData &data, TriangleStateType test_state); @@ -450,8 +467,6 @@ protected: int m_orig_size_indices = 0; std::unique_ptr m_cursor; - // Zero indicates an uninitialized state. - float m_old_cursor_radius_sqr = 0; // Private functions: private: diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index e871e1a539..c130973eec 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -139,9 +139,9 @@ void GLGizmoPainterBase::render_cursor() return; if (m_tool_type == ToolType::BRUSH) { - if (m_cursor_type == TriangleSelector::SPHERE) + if (m_cursor_type == TriangleSelector::CursorType::SPHERE) render_cursor_sphere(trafo_matrices[m_rr.mesh_id]); - else if (m_cursor_type == TriangleSelector::CIRCLE) + else if (m_cursor_type == TriangleSelector::CursorType::CIRCLE) render_cursor_circle(); } } @@ -480,7 +480,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous const Transform3d trafo_matrix_not_translate = mi->get_transformation().get_matrix_no_offset() * mo->volumes[m_rr.mesh_id]->get_matrix_no_offset(); const Transform3d trafo_matrix = mi->get_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix(); m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, this->get_clipping_plane_in_volume_coordinates(trafo_matrix), m_smart_fill_angle, - m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true); + m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, TriangleSelector::ForceReselection::YES); m_triangle_selectors[m_rr.mesh_id]->request_update_render_data(); m_seed_fill_last_mesh_id = m_rr.mesh_id; } @@ -561,13 +561,14 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous const Vec3f mesh_hit = projected_mouse_position.mesh_hit; const int facet_idx = int(projected_mouse_position.facet_idx); m_triangle_selectors[mesh_idx]->seed_fill_apply_on_triangles(new_state); - if (m_tool_type == ToolType::SMART_FILL) + if (m_tool_type == ToolType::SMART_FILL) { m_triangle_selectors[mesh_idx]->seed_fill_select_triangles(mesh_hit, facet_idx, trafo_matrix_not_translate, clp, m_smart_fill_angle, - m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true); - else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER) - m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, false, true); - else if (m_tool_type == ToolType::BUCKET_FILL) - m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, true, true); + (m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f), TriangleSelector::ForceReselection::YES); + } else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER) { + m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, TriangleSelector::BucketFillPropagate::NO, TriangleSelector::ForceReselection::YES); + } else if (m_tool_type == ToolType::BUCKET_FILL) { + m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, TriangleSelector::BucketFillPropagate::YES, TriangleSelector::ForceReselection::YES); + } m_seed_fill_last_mesh_id = -1; } @@ -649,9 +650,9 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f); else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER) - m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, false); + m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, TriangleSelector::BucketFillPropagate::NO); else if (m_tool_type == ToolType::BUCKET_FILL) - m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, true); + m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, TriangleSelector::BucketFillPropagate::YES); m_triangle_selectors[m_rr.mesh_id]->request_update_render_data(); m_seed_fill_last_mesh_id = m_rr.mesh_id; return true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 7ae82e4333..2f209ca327 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -132,7 +132,7 @@ protected: // For each model-part volume, store status and division of the triangles. std::vector> m_triangle_selectors; - TriangleSelector::CursorType m_cursor_type = TriangleSelector::SPHERE; + TriangleSelector::CursorType m_cursor_type = TriangleSelector::CursorType::SPHERE; enum class ToolType { BRUSH,