From 44aa32d6e2613d38241bd2a961ee4e95fd1d7fa2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 26 Apr 2023 13:18:12 +0200 Subject: [PATCH] Cut: correspondence between parts and contours --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 244 ++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 10 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 14 +- 3 files changed, 179 insertions(+), 89 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 1975d6ebae..897414b953 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1390,19 +1390,110 @@ void GLGizmoCut3D::render_clipper_cut() { if (! m_connectors_editing) ::glDisable(GL_DEPTH_TEST); - m_c->object_clipper()->render_cut(); + + GLboolean cull_face = GL_FALSE; + ::glGetBooleanv(GL_CULL_FACE, &cull_face); + ::glDisable(GL_CULL_FACE); + m_c->object_clipper()->render_cut(m_part_selection.get_ignored_contours_ptr()); + if (cull_face) + ::glEnable(GL_CULL_FACE); + if (! m_connectors_editing) ::glEnable(GL_DEPTH_TEST); } -void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) + +GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc) +{ + m_model = Model(); + m_model.add_object(*mo); + ModelObjectPtrs cut_part_ptrs = m_model.objects.front()->cut(instance_idx_in, cut_matrix, + ModelObjectCutAttribute::KeepUpper | + ModelObjectCutAttribute::KeepLower | + ModelObjectCutAttribute::KeepAsParts); + assert(cut_part_ptrs.size() == 1); + m_model = Model(); + m_model.add_object(*cut_part_ptrs.front()); + + m_instance_idx = instance_idx_in; + + const ModelVolumePtrs& volumes = model_object()->volumes; + + // split to parts + for (int id = int(volumes.size())-1; id >= 0; id--) + if (volumes[id]->is_splittable()) + volumes[id]->split(1); + + m_parts.clear(); + for (const ModelVolume* volume : volumes) { + assert(volume != nullptr); + m_parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true}); + m_parts.back().glmodel.set_color({ 0.f, 0.f, 1.f, 1.f }); + m_parts.back().glmodel.init_from(volume->mesh()); + + // Now check whether this part is below or above the plane. + Transform3d tr = (model_object()->instances[m_instance_idx]->get_matrix() * volume->get_matrix()).inverse(); + Vec3f pos = (tr * center).cast(); + Vec3f norm = (tr.linear().inverse().transpose() * normal).cast(); + for (const Vec3f& v : volume->mesh().its.vertices) { + double p = (v - pos).dot(norm); + if (std::abs(p) > EPSILON) { + m_parts.back().selected = p > 0.; + break; + } + } + } + + // Now go through the contours and create a map from contours to parts. + m_contour_points.clear(); + m_contour_to_parts.clear(); + m_debug_pts = std::vector>(m_parts.size(), std::vector()); + if (std::vector pts = oc.point_per_contour();! pts.empty()) { + + m_contour_to_parts.resize(pts.size()); + + for (size_t pt_idx=0; pt_idxinstances[m_instance_idx]->get_offset()) * translation_transform(model_object()->volumes[part_id]->get_offset())).inverse(); + for (double d : {-1., 1.}) { + const Vec3d dir_mesh = d * tr.linear().inverse().transpose() * normal; + const Vec3d src = tr * (m_contour_points[pt_idx] + d*0.01 * normal); + AABBMesh::hit_result hit = aabb.query_ray_hit(src, dir_mesh); + + m_debug_pts[part_id].emplace_back(src); + + if (hit.is_inside()) { + // This part belongs to this point. + if (d == 1.) + m_contour_to_parts[pt_idx].first.emplace_back(part_id); + else + m_contour_to_parts[pt_idx].second.emplace_back(part_id); + } + } + } + } + + } + + + m_valid = true; +} + +void GLGizmoCut3D::PartSelection::render(const Vec3d* normal, GLModel& sphere_model) { if (! valid()) return; - if (GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light")) { - const Camera& camera = wxGetApp().plater()->get_camera(); + const Camera& camera = wxGetApp().plater()->get_camera(); + if (GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light")) { shader->start_using(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("emission_factor", 0.f); @@ -1426,8 +1517,52 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) shader->stop_using(); } + + + + // { // Debugging render: + + // static int idx = -1; + // ImGui::Begin("DEBUG"); + // for (int i=0; i= m_parts.size()) + // idx = -1; + // ImGui::End(); + + // ::glDisable(GL_DEPTH_TEST); + // if (valid()) { + // for (size_t i=0; ivolumes[id]->get_offset(); - Transform3d tr = model_object()->instances[m_instance_idx]->get_matrix() * model_object()->volumes[id]->get_matrix(); + Transform3d tr = translation_transform(model_object()->instances[m_instance_idx]->get_offset()) * translation_transform(model_object()->volumes[id]->get_offset()); if (m_parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) { hits_id_and_sqdist.emplace_back(id, (camera_pos - tr*(pos.cast())).squaredNorm()); } @@ -1450,6 +1585,20 @@ void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) size_t id = std::min_element(hits_id_and_sqdist.begin(), hits_id_and_sqdist.end(), [](const std::pair& a, const std::pair& b) { return a.second < b.second; })->first; m_parts[id].selected = ! m_parts[id].selected; + + // And now recalculate the contours which should be ignored. + m_ignored_contours.clear(); + size_t cont_id = 0; + for (const auto& [parts_above, parts_below] : m_contour_to_parts) { + for (size_t upper : parts_above) { + bool upper_sel = m_parts[upper].selected; + if (std::find_if(parts_below.begin(), parts_below.end(), [this, &upper_sel](const size_t& i) { return m_parts[i].selected == upper_sel; }) != parts_below.end()) { + m_ignored_contours.emplace_back(cont_id); + break; + } + } + ++cont_id; + } } } @@ -1468,18 +1617,6 @@ void GLGizmoCut3D::on_render() } - ::glDisable(GL_DEPTH_TEST); - std::vector pts = m_c->object_clipper()->point_per_contour(); - if (! pts.empty()) { - const Vec3d dir = (m_plane_center-pts.front()).dot(m_cut_normal) * m_cut_normal; - for (const Vec3d& pt : pts) - render_model(m_sphere.model, ColorRGBA::GREEN(), wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(pt+dir)); - } - ::glEnable(GL_DEPTH_TEST); - - - - update_clipper(); init_picking_models(); @@ -1489,9 +1626,9 @@ void GLGizmoCut3D::on_render() render_connectors(); if (!m_connectors_editing) - m_part_selection.render(); + m_part_selection.render(nullptr, m_sphere.model); else - m_part_selection.render(&m_cut_normal); + m_part_selection.render(&m_cut_normal, m_sphere.model); render_clipper_cut(); @@ -1745,61 +1882,6 @@ void GLGizmoCut3D::flip_cut_plane() m_part_selection.turn_over_selection(); } - -GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc) -{ - m_model = Model(); - m_model.add_object(*mo); - ModelObjectPtrs cut_part_ptrs = m_model.objects.front()->cut(instance_idx_in, cut_matrix, - ModelObjectCutAttribute::KeepUpper | - ModelObjectCutAttribute::KeepLower | - ModelObjectCutAttribute::KeepAsParts); - assert(cut_part_ptrs.size() == 1); - m_model = Model(); - m_model.add_object(*cut_part_ptrs.front()); - - m_instance_idx = instance_idx_in; - - const ModelVolumePtrs& volumes = model_object()->volumes; - - // split to parts - for (int id = int(volumes.size())-1; id >= 0; id--) - if (volumes[id]->is_splittable()) - volumes[id]->split(1); - - m_parts.clear(); - for (const ModelVolume* volume : volumes) { - assert(volume != nullptr); - m_parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true}); - m_parts.back().glmodel.set_color({ 0.f, 0.f, 1.f, 1.f }); - m_parts.back().glmodel.init_from(volume->mesh()); - - // Now check whether this part is below or above the plane. - Transform3d tr = (model_object()->instances[m_instance_idx]->get_matrix() * volume->get_matrix()).inverse(); - Vec3f pos = (tr * center).cast(); - Vec3f norm = (tr.linear().inverse().transpose() * normal).cast(); - for (const Vec3f& v : volume->mesh().its.vertices) { - double p = (v - pos).dot(norm); - if (std::abs(p) > EPSILON) { - m_parts.back().selected = p > 0.; - break; - } - } - } - - // Now go through the contours and create a map from contours to parts. - if (std::vector pts = oc.point_per_contour();! pts.empty()) { - const Vec3d dir = (center-pts.front()).dot(normal) * normal; - for (Vec3d& pt : pts) - pt = pt+dir; - // pts are now in world coordinates. - } - - - m_valid = true; -} - - void GLGizmoCut3D::reset_cut_by_contours() { m_part_selection = PartSelection(); @@ -2542,8 +2624,18 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& po } }*/ - if (m_c->object_clipper()->is_projection_inside_cut(hit) == -1) - return false; + { + // Do not react to clicks outside a contour (or inside a contour that is ignored) + int cont_id = m_c->object_clipper()->is_projection_inside_cut(hit); + if (cont_id == -1) + return false; + if (m_part_selection.valid()) { + const std::vector& ign = *m_part_selection.get_ignored_contours_ptr(); + if (std::find(ign.begin(), ign.end(), cont_id) != ign.end()) + return false; + } + } + // recalculate hit to object's local position Vec3d hit_d = hit; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index b765ce14b2..0f300b0fad 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -150,12 +150,18 @@ class GLGizmoCut3D : public GLGizmoBase bool selected; }; - void render(const Vec3d* normal = nullptr); + void render(const Vec3d* normal, GLModel& sphere_model); void toggle_selection(const Vec2d& mouse_pos); void turn_over_selection(); ModelObject* model_object() { return m_model.objects.front(); } bool valid() const { return m_valid; } 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: @@ -312,7 +318,7 @@ private: void discard_cut_line_processing(); void render_cut_plane(); - void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix); + static void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix); void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width); void render_rotation_snapping(GrabberID axis, const ColorRGBA& color); void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 3d7b77a50d..79c77692a0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -428,17 +428,9 @@ std::vector ObjectClipper::point_per_contour() const { std::vector pts; - const SelectionInfo* sel_info = get_pool()->selection_info(); - const Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation(); - - for (auto& clipper : m_clippers) { - Geometry::Transformation trafo = inst_trafo * clipper.second; - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); - - // FIXME: do not assume just one clipper - pts = clipper.first->point_per_contour(); - //for (Vec3d& v : pts) - // v = trafo.get_matrix() * v; + for (const auto& clipper : m_clippers) { + const std::vector pts_clipper = clipper.first->point_per_contour(); + pts.insert(pts.end(), pts_clipper.begin(), pts_clipper.end());; } return pts; }