From 72f47f79630496955010e6653e0f7e2c353887b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 2 Apr 2024 12:24:22 +0200 Subject: [PATCH] Optimize painting using single triangle brush. Especially on the detail models, painting using the single triangle brush is noticeably faster. --- src/libslic3r/TriangleSelector.cpp | 57 +++++++++++++++----- src/libslic3r/TriangleSelector.hpp | 12 ++++- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 6 ++- 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 3c4cd62838..197e84dc81 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -583,11 +583,17 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f &hit, int facet_ 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 == BucketFillPropagate::NO) { + if (m_triangle_selected_by_seed_fill != -1) + this->seed_fill_unselect_triangle(m_triangle_selected_by_seed_fill); + m_triangles[start_facet_idx].select_by_seed_fill(); + m_triangle_selected_by_seed_fill = start_facet_idx; return; + } else { + m_triangle_selected_by_seed_fill = -1; + this->seed_fill_unselect_all_triangles(); } const float facet_angle_limit = std::cos(Geometry::deg2rad(bucket_fill_angle)) - EPSILON; @@ -1286,42 +1292,43 @@ void TriangleSelector::undivide_triangle(int facet_idx) } } -void TriangleSelector::remove_useless_children(int facet_idx) -{ +// Returns true when some triangle during recursive descending was removed (undivided). +bool TriangleSelector::remove_useless_children(int facet_idx) { // Check that all children are leafs of the same type. If not, try to - // make them (recursive call). Remove them if sucessful. + // make them (recursive call). Remove them if successful. assert(facet_idx < int(m_triangles.size()) && m_triangles[facet_idx].valid()); - Triangle& tr = m_triangles[facet_idx]; + Triangle &tr = m_triangles[facet_idx]; - if (! tr.is_split()) { + if (!tr.is_split()) { // This is a leaf, there nothing to do. This can happen during the // first (non-recursive call). Shouldn't otherwise. - return; + return false; } // Call this for all non-leaf children. - for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { + bool children_removed = false; + for (int child_idx = 0; child_idx <= tr.number_of_split_sides(); ++child_idx) { assert(child_idx < int(m_triangles.size()) && m_triangles[child_idx].valid()); if (m_triangles[tr.children[child_idx]].is_split()) - remove_useless_children(tr.children[child_idx]); + children_removed |= remove_useless_children(tr.children[child_idx]); } - // Return if a child is not leaf or two children differ in type. TriangleStateType first_child_type = TriangleStateType::NONE; - for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) { + for (int child_idx = 0; child_idx <= tr.number_of_split_sides(); ++child_idx) { if (m_triangles[tr.children[child_idx]].is_split()) - return; + return children_removed; if (child_idx == 0) first_child_type = m_triangles[tr.children[0]].get_state(); else if (m_triangles[tr.children[child_idx]].get_state() != first_child_type) - return; + return children_removed; } // If we got here, the children can be removed. undivide_triangle(facet_idx); tr.set_state(first_child_type); + return true; } void TriangleSelector::garbage_collect() @@ -2050,7 +2057,22 @@ void TriangleSelector::seed_fill_unselect_all_triangles() triangle.unselect_by_seed_fill(); } +void TriangleSelector::seed_fill_unselect_triangle(const int facet_idx) { + assert(facet_idx > 0 && facet_idx < m_triangles.size()); + Triangle &triangle = m_triangles[facet_idx]; + + assert(!triangle.is_split()); + if (!triangle.is_split()) + triangle.unselect_by_seed_fill(); +} + void TriangleSelector::seed_fill_apply_on_triangles(TriangleStateType new_state) { + if (m_triangle_selected_by_seed_fill != -1) { + this->seed_fill_apply_on_single_triangle(new_state, m_triangle_selected_by_seed_fill); + m_triangle_selected_by_seed_fill = -1; + return; + } + for (Triangle &triangle : m_triangles) if (!triangle.is_split() && triangle.is_selected_by_seed_fill()) triangle.set_state(new_state); @@ -2062,6 +2084,15 @@ void TriangleSelector::seed_fill_apply_on_triangles(TriangleStateType new_state) } } +void TriangleSelector::seed_fill_apply_on_single_triangle(TriangleStateType new_state, const int facet_idx) { + assert(facet_idx > 0 && facet_idx < m_triangles.size()); + + if (Triangle &triangle = m_triangles[facet_idx]; !triangle.is_split() && triangle.is_selected_by_seed_fill()) { + triangle.set_state(new_state); + remove_useless_children(triangle.source_triangle); + } +} + double TriangleSelector::get_triangle_area(const Triangle &triangle) const { const stl_vertex &v0 = m_vertices[triangle.verts_idxs[0]].v; const stl_vertex &v1 = m_vertices[triangle.verts_idxs[1]].v; diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index e0849fd13b..534aa33ec7 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -401,10 +401,17 @@ public: // For all triangles, remove the flag indicating that the triangle was selected by seed fill. void seed_fill_unselect_all_triangles(); + // Remove the flag indicating that the triangle was selected by seed fill. + void seed_fill_unselect_triangle(int facet_idx); + // For all triangles selected by seed fill, set new TriangleStateType and remove flag indicating that triangle was selected by seed fill. // The operation may merge split triangles if they are being assigned the same color. void seed_fill_apply_on_triangles(TriangleStateType new_state); + // For the triangle selected by seed fill, set a new TriangleStateType and remove the flag indicating that the triangle was selected by seed fill. + // The operation may merge a split triangle if it is being assigned the same color. + void seed_fill_apply_on_single_triangle(TriangleStateType new_state, const int facet_idx); + // Compute total area of the triangle. double get_triangle_area(const Triangle &triangle) const; @@ -495,13 +502,16 @@ protected: std::unique_ptr m_cursor; + // Single triangle selected by seed fill. It is used to optimize painting using a single triangle brush. + int m_triangle_selected_by_seed_fill = -1; + // Private functions: private: bool select_triangle(int facet_idx, TriangleStateType type, bool triangle_splitting); bool select_triangle_recursive(int facet_idx, const Vec3i &neighbors, TriangleStateType type, bool triangle_splitting); void undivide_triangle(int facet_idx); void split_triangle(int facet_idx, const Vec3i &neighbors); - void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. + bool remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. bool is_facet_clipped(int facet_idx, const ClippingPlane &clp) const; int push_triangle(int a, int b, int c, int source_triangle, TriangleStateType state = TriangleStateType::NONE); void perform_split(int facet_idx, const Vec3i &neighbors, TriangleStateType old_state); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 36c309c3f3..5d608aad86 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -643,9 +643,11 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if (m_tool_type == ToolType::SMART_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)) { for (const ProjectedMousePosition &projected_mouse_position: projected_mouse_positions) { assert(projected_mouse_position.mesh_idx == mesh_idx); - const Vec3f mesh_hit = projected_mouse_position.mesh_hit; - const int facet_idx = int(projected_mouse_position.facet_idx); + 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) { m_triangle_selectors[mesh_idx]->seed_fill_select_triangles(mesh_hit, facet_idx, trafo_matrix_not_translate, clp, m_smart_fill_angle, m_smart_fill_gap_area, (m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f), TriangleSelector::ForceReselection::YES);