diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 5610c9f78..6e17daa56 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -738,6 +738,13 @@ void its_flip_triangles(indexed_triangle_set &its) std::swap(face(1), face(2)); } +int its_num_degenerate_faces(const indexed_triangle_set &its) +{ + return std::count_if(its.indices.begin(), its.indices.end(), [](auto &face) { + return face(0) == face(1) || face(0) == face(2) || face(1) == face(2); + }); +} + int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit) { auto it = std::remove_if(its.indices.begin(), its.indices.end(), [](auto &face) { diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index c288b6d3a..d8096eae2 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -204,6 +204,8 @@ void its_flip_triangles(indexed_triangle_set &its); // or more than two faces share the same edge position! int its_merge_vertices(indexed_triangle_set &its, bool shrink_to_fit = true); +// Calculate number of degenerate faces. There should be no degenerate faces in a nice mesh. +int its_num_degenerate_faces(const indexed_triangle_set &its); // Remove degenerate faces, return number of faces removed. int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit = true); diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index ae917756b..63802961b 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -242,7 +242,12 @@ static FacetSliceType slice_facet( std::swap(a, b); } IntersectionPoint &point = points[num_points]; - double t = (double(slice_z) - double(b->z())) / (double(a->z()) - double(b->z())); + double t = (double(slice_z) - double(a->z())) / (double(b->z()) - double(a->z())); +#if 0 + // If the intersection point falls into one of the end points, mark it with the end point identifier. + // While this sounds like a good idea, it likely breaks the chaining by logical addresses of the intersection points + // and the branch for 0 < t < 1 does not guarantee uniqness of the interection point anyways. + // Thus this branch is only kept for reference and it is not used in production code. if (t <= 0.) { if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) { point.x() = a->x(); @@ -258,11 +263,26 @@ static FacetSliceType slice_facet( point.point_id = b_id; } } else { - point.x() = coord_t(floor(double(b->x()) + (double(a->x()) - double(b->x())) * t + 0.5)); - point.y() = coord_t(floor(double(b->y()) + (double(a->y()) - double(b->y())) * t + 0.5)); + point.x() = coord_t(floor(double(a->x()) + (double(b->x()) - double(a->x())) * t + 0.5)); + point.y() = coord_t(floor(double(a->y()) + (double(b->y()) - double(a->y())) * t + 0.5)); point.edge_id = edge_id; ++ num_points; } +#else + // Just clamp the intersection point to source triangle edge. + if (t <= 0.) { + point.x() = a->x(); + point.y() = a->y(); + } else if (t >= 1.) { + point.x() = b->x(); + point.y() = b->y(); + } else { + point.x() = coord_t(floor(double(a->x()) + (double(b->x()) - double(a->x())) * t + 0.5)); + point.y() = coord_t(floor(double(a->y()) + (double(b->y()) - double(a->y())) * t + 0.5)); + } + point.edge_id = edge_id; + ++ num_points; +#endif } } @@ -284,6 +304,11 @@ static FacetSliceType slice_facet( assert(line_out.edge_a_id != -1 || line_out.edge_b_id != -1); // General slicing position, use the segment for both slicing and object cutting. #if 0 + // See the discussion on calculating the intersection point on a triangle edge. + // Even if the intersection point is clamped to one of the end points of the triangle edge, + // the intersection point is still marked as "on edge", not "on vertex". Such implementation + // may produce degenerate triangles, but is topologically correct. + // Therefore this block for solving snapping of an intersection edge to triangle vertices is not used. if (line_out.a_id != -1 && line_out.b_id != -1) { // Solving a degenerate case, where both the intersections snapped to an edge. // Correctly classify the face as below or above based on the position of the 3rd point. @@ -2009,6 +2034,7 @@ static void triangulate_slice( (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. { std::vector map_duplicate_vertex(int(its.vertices.size()) - num_original_vertices, -1); int i = 0; @@ -2031,10 +2057,20 @@ static void triangulate_slice( i = j; } map_vertex_to_index.erase(map_vertex_to_index.begin() + k, map_vertex_to_index.end()); - for (stl_triangle_vertex_indices &f : its.indices) - for (i = 0; i < 3; ++ i) - if (f(i) >= num_original_vertices) - f(i) = map_duplicate_vertex[f(i) - num_original_vertices]; + for (i = 0; i < int(its.indices.size());) { + stl_triangle_vertex_indices &f = its.indices[i]; + // Remap the newly added face vertices. + for (k = 0; k < 3; ++ k) + if (f(k) >= num_original_vertices) + f(k) = map_duplicate_vertex[f(k) - num_original_vertices]; + if (f(0) == f(1) || f(0) == f(2) || f(1) == f(2)) { + // Remove degenerate face. + f = its.indices.back(); + its.indices.pop_back(); + } else + // Keep the face. + ++ i; + } } if (triangulate) { @@ -2108,6 +2144,10 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u if (upper == nullptr && lower == nullptr) return; +#ifndef NDEBUG + const size_t had_degenerate_faces = its_num_degenerate_faces(mesh); +#endif // NDEBUG + BOOST_LOG_TRIVIAL(trace) << "cut_mesh - slicing object"; if (upper) { @@ -2251,8 +2291,27 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u new_face(lower, iv0, iv0v1_lower, iv2v0_lower); } } + +/* + char buf[2048]; + static int irun = 0; + ++irun; + temp.indices.emplace_back(int(temp.vertices.size()), int(temp.vertices.size() + 1), int(temp.vertices.size() + 2)); + temp.vertices.emplace_back(vertices[0]); + temp.vertices.emplace_back(vertices[1]); + temp.vertices.emplace_back(vertices[2]); + sprintf(buf, "D:\\temp\\test\\temp-%d.obj", irun); + its_write_obj(temp, buf); + sprintf(buf, "D:\\temp\\test\\upper-%d.obj", irun); + its_write_obj(*upper, buf); + sprintf(buf, "D:\\temp\\test\\lower-%d.obj", irun); + its_write_obj(*lower, buf); +*/ } - + + assert(had_degenerate_faces || ! upper || its_num_degenerate_faces(*upper) == 0); + assert(had_degenerate_faces || ! lower || its_num_degenerate_faces(*lower) == 0); + if (upper != nullptr) { triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_DOWN); #ifndef NDEBUG @@ -2272,6 +2331,9 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u } #endif // NDEBUG } + + assert(had_degenerate_faces || ! upper || its_num_degenerate_faces(*upper) == 0); + assert(had_degenerate_faces || ! lower || its_num_degenerate_faces(*lower) == 0); } } // namespace Slic3r diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp index fbe724433..ea6a7262c 100644 --- a/src/libslic3r/TriangleMeshSlicer.hpp +++ b/src/libslic3r/TriangleMeshSlicer.hpp @@ -130,6 +130,6 @@ void cut_mesh( indexed_triangle_set *lower, bool triangulate_caps = true); -} +} // namespace Slic3r #endif // slic3r_TriangleMeshSlicer_hpp_