From ab0ade05395e2c73aeb2d809edd6ae8f164010a3 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 27 Apr 2023 11:14:38 +0200 Subject: [PATCH] Cut: contours indexing no longer breaks when the normal is inverted, detection of the situation when all parts are assigned to one side --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 19 ++++++++++- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 12 +++---- src/slic3r/GUI/MeshUtils.cpp | 48 +++++++++++++++++++++++++--- src/slic3r/GUI/MeshUtils.hpp | 1 + 4 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 897414b95..dc068e7cb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1563,6 +1563,21 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal, GLModel& sphere_mo } +bool GLGizmoCut3D::PartSelection::is_one_object() const +{ + // In theory, the implementation could be just this: + // return m_contour_to_parts.size() == m_ignored_contours.size(); + // However, this would require that the part-contour correspondence works + // flawlessly. Because it is currently not always so for self-intersecting + // objects, let's better check the parts itself: + if (m_parts.size() < 2) + return true; + return std::all_of(m_parts.begin(), m_parts.end(), [this](const Part& part) { + return part.selected == m_parts.front().selected; + }); +} + + void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) { // FIXME: Cache the transforms. @@ -1984,7 +1999,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) add_vertical_scaled_interval(0.75f); - m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_keep_as_parts); + m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_keep_as_parts || (m_part_selection.valid() && m_part_selection.is_one_object())); if (m_imgui->button(has_connectors ? _L("Edit connectors") : _L("Add connectors"))) set_connectors_editing(true); m_imgui->disabled_end(); @@ -2404,6 +2419,8 @@ bool GLGizmoCut3D::can_perform_cut() const { if (! m_invalid_connectors_idxs.empty() || (!m_keep_upper && !m_keep_lower) || m_connectors_editing) return false; + if (m_part_selection.valid()) + return ! m_part_selection.is_one_object(); return true;// has_valid_contour(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 0f300b0fa..02f62d628 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -155,20 +155,20 @@ class GLGizmoCut3D : public GLGizmoBase void turn_over_selection(); ModelObject* model_object() { return m_model.objects.front(); } bool valid() const { return m_valid; } + bool is_one_object() const; const std::vector& parts() const { return m_parts; } const std::vector* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); } - std::vector m_contour_points; // TEMPORARILY PUBLIC - std::vector, std::vector>> m_contour_to_parts; // for each contour, there is a vector of parts above and a vector of parts below - std::vector m_ignored_contours; // contour that should not be rendered (the parts on both sides will both be parts of the same object) - std::vector> m_debug_pts; - - private: Model m_model; int m_instance_idx; std::vector m_parts; bool m_valid = false; + std::vector, std::vector>> m_contour_to_parts; // for each contour, there is a vector of parts above and a vector of parts below + std::vector m_ignored_contours; // contour that should not be rendered (the parts on both sides will both be parts of the same object) + + std::vector m_contour_points; // Debugging + std::vector> m_debug_pts; // Debugging }; PartSelection m_part_selection; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 8e29a1c52..663712ca4 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -174,13 +174,40 @@ bool MeshClipper::has_valid_contour() const std::vector MeshClipper::point_per_contour() const { + assert(m_result); std::vector out; - if (!m_result || m_result->cut_islands.empty()) - return out; - + for (const CutIsland& isl : m_result->cut_islands) { - // FIXME: There might be holes ! - Vec2d c = unscale(isl.expoly.contour.centroid()); + assert(isl.expoly.contour.size() > 2); + // Now return a point lying inside the contour but not in a hole. + // We do this by taking a point lying close to the edge, repeating + // this several times for different edges and distances from them. + // (We prefer point not extremely close to the border. + bool done = false; + Vec2d p; + size_t i = 1; + while (i < isl.expoly.contour.size()) { + const Vec2d& a = unscale(isl.expoly.contour.points[i-1]); + const Vec2d& b = unscale(isl.expoly.contour.points[i]); + Vec2d n = (b-a).normalized(); + std::swap(n.x(), n.y()); + n.x() = -1 * n.x(); + double f = 10.; + while (f > 0.05) { + p = (0.5*(b+a)) + f * n; + if (isl.expoly.contains(Point::new_scale(p))) { + done = true; + break; + } + f = f/10.; + } + if (done) + break; + i += isl.expoly.contour.size() / 5; + } + // If the above failed, just return the centroid, regardless of whether + // it is inside the contour or in a hole (we must return something). + Vec2d c = done ? p : unscale(isl.expoly.contour.centroid()); out.emplace_back(m_result->trafo * Vec3d(c.x(), c.y(), 0.)); } return out; @@ -366,7 +393,18 @@ void MeshClipper::recalculate_triangles() isl.expoly = std::move(exp); isl.expoly_bb = get_extents(exp); + isl.hash = 0; + for (const Point& pt : isl.expoly.contour) { + isl.hash ^= pt.x(); + isl.hash ^= pt.y(); + } } + + // Now sort the islands so they are in defined order. This is a hack needed by cut gizmo, which sometimes + // flips the normal of the cut, in which case the contours stay the same but their order may change. + std::sort(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& a, const CutIsland& b) { + return a.hash < b.hash; + }); } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index be95daa15..e5fa97e39 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -141,6 +141,7 @@ private: ExPolygon expoly; BoundingBox expoly_bb; bool disabled = false; + coord_t hash; }; struct ClipResult { std::vector cut_islands;