From 099756fc1aace24449f3d75293ffe6fe25785c33 Mon Sep 17 00:00:00 2001 From: "zhou.xu" Date: Mon, 20 Nov 2023 10:34:27 +0800 Subject: [PATCH] FIX:fix bugs of non manifold edge Jira: STUDIO-5267 Change-Id: I8ac9a2cf96da0bc07ee00b309e65611b92fd174d --- src/libslic3r/CutUtils.cpp | 5 + src/libslic3r/TriangleMeshSlicer.cpp | 237 +++++++++++++++++++-------- 2 files changed, 172 insertions(+), 70 deletions(-) diff --git a/src/libslic3r/CutUtils.cpp b/src/libslic3r/CutUtils.cpp index a113b16f3..73f41cb2c 100644 --- a/src/libslic3r/CutUtils.cpp +++ b/src/libslic3r/CutUtils.cpp @@ -447,6 +447,11 @@ const ModelObjectPtrs &Cut::perform_by_contour(std::vector parts, int dowe if (m_attributes.has(ModelObjectCutAttribute::KeepLower)) cut_mo->clone_for_cut(&lower); + if (upper && lower) { + upper->name = upper->name + "_A"; + lower->name = lower->name + "_B"; + } + const size_t cut_parts_cnt = parts.size(); bool has_modifiers = false; diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 3cee8b0ac..c69c76e87 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -47,9 +47,12 @@ bool is_equal(float lh, float rh) return abs(lh - rh) <= epson; } -bool is_less(float lh, float rh) -{ - return lh + epson < rh; +bool is_equal_for_sort(float lh, float rh) { + return abs(lh - rh) <= 1e-8; +} + +bool is_equal(const Vec3f &lh, const Vec3f &rh) { + return is_equal(lh[0], rh[0]) && is_equal(lh[1], rh[1]) && is_equal(lh[2], rh[2]); } class IntersectionReference @@ -170,7 +173,7 @@ static FacetSliceType slice_facet( // (external on the right of the line) for (int j = 0; j < 3; ++ j) { // loop through facet edges int edge_id; - const stl_vertex *a, *b; + const stl_vertex *a, *b, *c; int a_id, b_id; { int k = (idx_vertex_lowest + j) % 3; @@ -180,10 +183,11 @@ static FacetSliceType slice_facet( a = vertices + k; b_id = indices[l]; b = vertices + l; + c = vertices + (k + 2) % 3; } // Is edge or face aligned with the cutting plane? - if (a->z() == slice_z && b->z() == slice_z) { + if (is_equal(a->z(), slice_z) && is_equal(b->z() ,slice_z)) { // Edge is horizontal and belongs to the current layer. // The following rotation of the three vertices may not be efficient, but this branch happens rarely. const stl_vertex &v0 = vertices[0]; @@ -204,7 +208,7 @@ static FacetSliceType slice_facet( } else { // Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane. // Is the third vertex below the cutting plane? - bool third_below = v0.z() < slice_z || v1.z() < slice_z || v2.z() < slice_z; + bool third_below = c->z() < slice_z; // Two vertices on the cutting plane, the third vertex is below the plane. Consider the edge to be part of the slice // only if it is the upper edge. // (the bottom most edge resp. vertex of a triangle is not owned by the triangle, but the top most edge resp. vertex is part of the triangle @@ -227,7 +231,7 @@ static FacetSliceType slice_facet( return result; } - if (a->z() == slice_z) { + if (is_equal(a->z() ,slice_z)) { // Only point a alings with the cutting plane. if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) { point_on_layer = num_points; @@ -236,7 +240,7 @@ static FacetSliceType slice_facet( point.y() = a->y(); point.point_id = a_id; } - } else if (b->z() == slice_z) { + } else if (is_equal(b->z() , slice_z)) { // Only point b alings with the cutting plane. if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) { point_on_layer = num_points; @@ -336,7 +340,7 @@ void slice_facet_at_zs( // find layer extents auto min_layer = std::lower_bound(zs.begin(), zs.end(), min_z); // first layer whose slice_z is >= min_z auto max_layer = std::upper_bound(min_layer, zs.end(), max_z); // first layer whose slice_z is > max_z - int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0); + int idx_vertex_lowest = is_equal(vertices[1].z(), min_z) ? 1 : (is_equal(vertices[2].z() , min_z) ? 2 : 0); for (auto it = min_layer; it != max_layer; ++ it) { IntersectionLine il; @@ -392,7 +396,7 @@ static inline IntersectionLines slice_make_lines( const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z())); const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z())); assert(min_z <= plane_z && max_z >= plane_z); - int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0); + int idx_vertex_lowest = is_equal(vertices[1].z(), min_z) ? 1 : (is_equal(vertices[2].z(), min_z) ? 2 : 0); IntersectionLine il; // Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume. if (!is_equal(min_z, max_z) && slice_facet(plane_z, vertices, indices, face_edge_ids[face_idx], idx_vertex_lowest, false, il) == FacetSliceType::Slicing) { @@ -447,8 +451,8 @@ void slice_facet_with_slabs( // find facet extents const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z())); const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z())); - const bool horizontal = min_z == max_z; - + const bool horizontal = is_equal(min_z , max_z); + // find layer extents auto min_layer = std::lower_bound(zs.begin(), zs.end(), min_z); // first layer whose slice_z is >= min_z auto max_layer = std::upper_bound(min_layer, zs.end(), max_z); // first layer whose slice_z is > max_z @@ -537,7 +541,7 @@ void slice_facet_with_slabs( } } else { // The triangle is not horizontal and at least a single slicing plane intersects the triangle. - int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0); + int idx_vertex_lowest = is_equal(vertices[1].z(), min_z) ? 1 : (is_equal(vertices[2].z() , min_z) ? 2 : 0); IntersectionLine il_prev; for (auto it = min_layer; it != max_layer; ++ it) { IntersectionLine il; @@ -1997,15 +2001,16 @@ void slice_mesh_slabs( // Remove duplicates of slice_vertices, optionally triangulate the cut. static void triangulate_slice( - indexed_triangle_set &its, - IntersectionLines &lines, + indexed_triangle_set &its, + IntersectionLines &lines, std::vector &slice_vertices, // Vertices of the original (unsliced) mesh. Newly added vertices are those on the slice. int num_original_vertices, // Z height of the slice. - float z, - bool triangulate, - bool normals_down) + float z, + bool triangulate, + bool normals_down, + const std::map §ion_vertices_map) { sort_remove_duplicates(slice_vertices); @@ -2015,11 +2020,12 @@ static void triangulate_slice( map_vertex_to_index.reserve(slice_vertices.size()); for (int i : slice_vertices) map_vertex_to_index.emplace_back(to_2d(its.vertices[i]), i); - std::sort(map_vertex_to_index.begin(), map_vertex_to_index.end(), - [](const std::pair &l, const std::pair &r) { - return is_less(l.first.x(), r.first.x()) || - (is_equal(l.first.x(), r.first.x()) && (is_less(l.first.y(), r.first.y()) || - (is_equal(l.first.y(), r.first.y()) && l.second < r.second))); }); + std::sort(map_vertex_to_index.begin(), map_vertex_to_index.end(), + [](const std::pair &l, const std::pair &r) { + return l.first.x() < r.first.x() || + (is_equal_for_sort(l.first.x(), r.first.x()) && (l.first.y()< r.first.y() || + (is_equal_for_sort(l.first.y(), r.first.y()) && l.second < r.second))); + }); // 2) Discover duplicate points on the slice. Remap duplicate vertices to a vertex with a lowest index. // Remove denegerate triangles, if they happen to be created by merging duplicate vertices. @@ -2068,14 +2074,40 @@ static void triangulate_slice( stl_triangle_vertex_indices facet; for (size_t j = 0; j < 3; ++ j) { Vec3f v = triangles[i ++].cast(); - auto it = lower_bound_by_predicate(map_vertex_to_index.begin(), map_vertex_to_index.end(), + auto it = lower_bound_by_predicate(map_vertex_to_index.begin(), map_vertex_to_index.end(), [&v](const std::pair &l) { - return is_less(l.first.x(), v.x()) || (is_equal(l.first.x(), v.x()) && is_less(l.first.y(), v.y())); + return l.first.x() < v.x() || (is_equal_for_sort(l.first.x(), v.x()) && l.first.y() < v.y()); }); int idx = -1; - if (it != map_vertex_to_index.end() && is_equal(it->first.x(), v.x()) && is_equal(it->first.y(), v.y())) - idx = it->second; - else { + bool exist = false; + for (auto i = section_vertices_map.begin(); i != section_vertices_map.end(); i++) { + if (is_equal(v, *i->second)) { + idx = i->first; + exist = true; + break; + } + } + // go on finding + if (!exist) { + for (; it != map_vertex_to_index.end(); it++) { + if (is_equal(it->first.x(), v.x()) && is_equal(it->first.y(), v.y())) { + idx = it->second; + exist = true; + break; + } + } + } + // go on finding + if (!exist) { + for (; it != map_vertex_to_index.begin(); it--) { + if (is_equal(it->first.x(), v.x()) && is_equal(it->first.y(), v.y())) { + idx = it->second; + exist = true; + break; + } + } + } + if (!exist){ // Try to find the vertex in the list of newly added vertices. Those vertices are not matched on the cut and they shall be rare. for (size_t k = idx_vertex_new_first; k < its.vertices.size(); ++ k) if (its.vertices[k] == v) { @@ -2156,16 +2188,22 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u IntersectionLines upper_lines, lower_lines; std::vector upper_slice_vertices, lower_slice_vertices; std::vector facets_edge_ids = its_face_edge_ids(mesh); + std::map section_vertices_map; for (int facet_idx = 0; facet_idx < int(mesh.indices.size()); ++ facet_idx) { const stl_triangle_vertex_indices &facet = mesh.indices[facet_idx]; Vec3f vertices[3] { mesh.vertices[facet(0)], mesh.vertices[facet(1)], mesh.vertices[facet(2)] }; float min_z = std::min(vertices[0].z(), std::min(vertices[1].z(), vertices[2].z())); float max_z = std::max(vertices[0].z(), std::max(vertices[1].z(), vertices[2].z())); - + + for (size_t i = 0; i < 3; i++) { + if (is_equal(z, vertices[i].z()) && section_vertices_map[facet(i)] == nullptr) { + section_vertices_map[facet(i)] = new Vec3f(vertices[i].x(), vertices[i].y(), vertices[i].z()); + } + } // intersect facet with cutting plane IntersectionLine line; - int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0); + int idx_vertex_lowest = is_equal(vertices[1].z(), min_z) ? 1 : (is_equal(vertices[2].z() , min_z) ? 2 : 0); FacetSliceType slice_type = FacetSliceType::NoSlice; if (z > min_z - EPSILON && z < max_z + EPSILON) { Vec3f vertices_scaled[3]; @@ -2176,7 +2214,7 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u dst.y() = scale_(src.y()); dst.z() = src.z(); } - slice_type = slice_facet(z, vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, min_z == max_z, line); + slice_type = slice_facet(z, vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, is_equal(min_z , max_z), line); } if (slice_type != FacetSliceType::NoSlice) { @@ -2194,12 +2232,12 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u upper_lines.emplace_back(line); } } - - if (min_z > z || (min_z == z && max_z > z)) { + + if (min_z > z || (is_equal(min_z , z) && max_z > z)) { // facet is above the cut plane and does not belong to it if (upper != nullptr) upper->indices.emplace_back(facet); - } else if (max_z < z || (max_z == z && min_z < z)) { + } else if (max_z < z || (is_equal(max_z, z) && min_z < z)) { // facet is below the cut plane and does not belong to it if (lower != nullptr) lower->indices.emplace_back(facet); @@ -2211,21 +2249,56 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u assert(line.edge_b_id != -1); // look for the vertex on whose side of the slicing plane there are no other vertices - int isolated_vertex = - (vertices[0].z() > z) == (vertices[1].z() > z) ? 2 : - (vertices[1].z() > z) == (vertices[2].z() > z) ? 0 : 1; - - // get vertices starting from the isolated one - int iv = isolated_vertex; - stl_vertex v0v1, v2v0; - assert(facets_edge_ids[facet_idx](iv) == line.edge_a_id || facets_edge_ids[facet_idx](iv) == line.edge_b_id); - if (facets_edge_ids[facet_idx](iv) == line.edge_a_id) { - // Unscale to doubles first, then to floats to reach the same accuracy as triangulate_expolygons_2d(). - v0v1 = to_3d(unscaled(line.a).cast().eval(), z); - v2v0 = to_3d(unscaled(line.b).cast().eval(), z); + int isolated_vertex, isolated_vertex_option = -1; + std::vector list{0, 1, 2}; + auto get_third = [&list](int isolated_vertex, int temp) {// not use vertex data + for (size_t i = 0; i < list.size(); i++) { + if (list[i] != isolated_vertex && list[i] != temp) { + return list[i]; + } + } + return -1; + }; + if (is_equal(vertices[0].z(), z)) { + isolated_vertex = vertices[1].z() > z ? 1 : 2; + isolated_vertex_option = get_third(isolated_vertex, 0); + } else if (is_equal(vertices[1].z(), z)) { + isolated_vertex = vertices[2].z() > z ? 2 : 0; + isolated_vertex_option = get_third(isolated_vertex, 1); + } else if (is_equal(vertices[2].z(), z)) { + isolated_vertex = vertices[0].z() > z ? 0 : 1; + isolated_vertex_option = get_third(isolated_vertex, 2); } else { - v0v1 = to_3d(unscaled(line.b).cast().eval(), z); - v2v0 = to_3d(unscaled(line.a).cast().eval(), z); + isolated_vertex = (vertices[0].z() > z) == (vertices[1].z() > z) ? 2 : (vertices[1].z() > z) == (vertices[2].z() > z) ? 0 : 1; + } + // get vertices starting from the isolated one + stl_vertex v0v1, v2v0; + auto calc_isolated_vertex = [&v0v1, &v2v0, &line, &facet_idx, &facets_edge_ids, &z](int iv, bool &is_find) { + assert(facets_edge_ids[facet_idx](iv) == line.edge_a_id || facets_edge_ids[facet_idx](iv) == line.edge_b_id); + is_find = true; + if (facets_edge_ids[facet_idx](iv) == line.edge_a_id) { + // Unscale to doubles first, then to floats to reach the same accuracy as triangulate_expolygons_2d(). + v0v1 = to_3d(unscaled(line.a).cast().eval(), z); + v2v0 = to_3d(unscaled(line.b).cast().eval(), z); + } else if (facets_edge_ids[facet_idx](iv) == line.edge_b_id) { + v0v1 = to_3d(unscaled(line.b).cast().eval(), z); + v2v0 = to_3d(unscaled(line.a).cast().eval(), z); + } else { + is_find = false; + } + }; + bool find_isolated_vertex; + int iv; + calc_isolated_vertex(isolated_vertex, find_isolated_vertex); + if (!find_isolated_vertex && isolated_vertex_option != -1) { + calc_isolated_vertex(isolated_vertex_option, find_isolated_vertex); + if (!find_isolated_vertex) { + BOOST_LOG_TRIVIAL(trace) << "cut_mesh:error:could not find isolated_vertex"; + continue; + } + iv = isolated_vertex_option; + } else { + iv = isolated_vertex; } const stl_vertex &v0 = vertices[iv]; const int iv0 = facet[iv]; @@ -2239,48 +2312,71 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u const int iv2 = facet[iv]; // intersect v0-v1 and v2-v0 with cutting plane and make new vertices - auto new_vertex = [upper, lower, &upper_slice_vertices, &lower_slice_vertices](const Vec3f &a, const int ia, const Vec3f &b, const int ib, const Vec3f &c) { + auto new_vertex = [upper, lower, &upper_slice_vertices, &lower_slice_vertices](const Vec3f &a, const int ia, const Vec3f &b, const int ib, const Vec3f &c, + const int ic, const Vec3f &new_pt, bool &is_new_vertex) { int iupper, ilower; - if (c == a) + is_new_vertex = false; + if (is_equal(new_pt, a)) iupper = ilower = ia; - else if (c == b) + else if (is_equal(new_pt, b)) iupper = ilower = ib; + else if (is_equal(new_pt, c)) + iupper = ilower = ic; else { // Insert a new vertex into upper / lower. + is_new_vertex = true; if (upper) { iupper = int(upper->vertices.size()); - upper->vertices.emplace_back(c); + upper->vertices.emplace_back(new_pt); upper_slice_vertices.emplace_back(iupper); } if (lower) { ilower = int(lower->vertices.size()); - lower->vertices.emplace_back(c); + lower->vertices.emplace_back(new_pt); lower_slice_vertices.emplace_back(ilower); } } return std::make_pair(iupper, ilower); }; - auto [iv0v1_upper, iv0v1_lower] = new_vertex(v1, iv1, v0, iv0, v0v1); - auto [iv2v0_upper, iv2v0_lower] = new_vertex(v2, iv2, v0, iv0, v2v0); - auto new_face = [](indexed_triangle_set *its, int i, int j, int k) { - if (its != nullptr && i != j && i != k && j != k) - its->indices.emplace_back(i, j, k); + bool is_new_vertex_v0v1; + bool is_new_vertex_v2v0; + auto [iv0v1_upper, iv0v1_lower] = new_vertex(v1, iv1, v0, iv0, v2, iv2, v0v1, is_new_vertex_v0v1); + auto [iv2v0_upper, iv2v0_lower] = new_vertex(v2, iv2, v0, iv0, v1, iv1, v2v0, is_new_vertex_v2v0); + auto new_face = [](indexed_triangle_set *its, int i, int j, int k) { + if (its != nullptr && i != j && i != k && j != k) its->indices.emplace_back(i, j, k); }; - - if (v0.z() > z) { - new_face(upper, iv0, iv0v1_upper, iv2v0_upper); - new_face(lower, iv1, iv2, iv0v1_lower); - new_face(lower, iv2, iv2v0_lower, iv0v1_lower); - } else { - new_face(upper, iv1, iv2, iv0v1_upper); - new_face(upper, iv2, iv2v0_upper, iv0v1_upper); - new_face(lower, iv0, iv0v1_lower, iv2v0_lower); + if (is_new_vertex_v0v1 && is_new_vertex_v2v0) { + if (v0.z() > z) { + new_face(upper, iv0, iv0v1_upper, iv2v0_upper); + new_face(lower, iv1, iv2, iv0v1_lower); + new_face(lower, iv2, iv2v0_lower, iv0v1_lower); + } else { + new_face(upper, iv1, iv2, iv0v1_upper); + new_face(upper, iv2, iv2v0_upper, iv0v1_upper); + new_face(lower, iv0, iv0v1_lower, iv2v0_lower); + } + } else if (is_new_vertex_v0v1) { + if (v0.z() > z) { + new_face(upper, iv0, iv0v1_upper, iv2); + new_face(lower, iv1, iv2, iv0v1_lower); + } else { + new_face(lower, iv0, iv0v1_lower, iv2); + new_face(upper, iv1, iv2, iv0v1_upper); + } + } else if (is_new_vertex_v2v0) { + if (v0.z() > z) { + new_face(upper, iv0, iv1, iv2v0_upper); + new_face(lower, iv1, iv2, iv2v0_lower); + } else { + new_face(lower, iv0, iv1, iv2v0_lower); + new_face(upper, iv1, iv2, iv2v0_upper); + } } } } - + if (upper != nullptr) { - triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_DOWN); + triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_DOWN, section_vertices_map); #ifndef NDEBUG if (triangulate_caps) { size_t num_open_edges_new = its_num_open_edges(*upper); @@ -2290,7 +2386,7 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u } if (lower != nullptr) { - triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_UP); + triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_UP, section_vertices_map); #ifndef NDEBUG if (triangulate_caps) { size_t num_open_edges_new = its_num_open_edges(*lower); @@ -2298,6 +2394,7 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u } #endif // NDEBUG } + std::map().swap(section_vertices_map); } // BBS: implement plane cut with cgal