From 25fa3eddf025e73d622ad0b72212b2b7f6e6f022 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 13 Jul 2021 11:06:59 +0200 Subject: [PATCH] Fixed slab slicing for special cases (slicing plane intersects object plane). --- src/libslic3r/TriangleMeshSlicer.cpp | 189 ++++++++++++++++++++------- 1 file changed, 143 insertions(+), 46 deletions(-) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 04481da7e..4304b66b0 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -31,6 +31,8 @@ #include #include +// #define SLIC3R_DEBUG_SLICE_PROCESSING + #if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING) #include "SVG.hpp" #endif @@ -88,6 +90,10 @@ public: Top, // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane. Bottom, + // Two vertices are aligned with the cutting plane, the edge is shared by two triangles, where one + // triangle is below or at the cutting plane and the other is above or at the cutting plane (only one + // vertex may lie on the plane). + TopBottom, // All three vertices of a face are aligned with the cutting plane. Horizontal, // Edge @@ -382,7 +388,8 @@ template void slice_facet_with_slabs( // Scaled or unscaled vertices. transform_vertex_fn may scale zs. const std::vector &mesh_vertices, - const stl_triangle_vertex_indices &indices, + const std::vector &mesh_triangles, + const size_t facet_idx, const Vec3i &facet_neighbors, const Vec3i &facet_edge_ids, // Increase edge_ids at the top plane of the slab edges by num_edges to allow chaining @@ -392,6 +399,7 @@ void slice_facet_with_slabs( SlabLines &lines, std::array &lines_mutex) { + const stl_triangle_vertex_indices &indices = mesh_triangles[facet_idx]; stl_vertex vertices[3] { mesh_vertices[indices(0)], mesh_vertices[indices(1)], mesh_vertices[indices(2)] }; // find facet extents @@ -416,12 +424,31 @@ void slice_facet_with_slabs( // Horizontal face or a nearly horizontal face that fits between two layers or below the bottom most or above the top most layer. assert(horizontal || zs.empty() || max_z < zs.front() || min_z > zs.back() || (min_layer == max_layer && min_layer != zs.end() && min_layer != zs.begin() && *(min_layer - 1) < min_z && *min_layer > max_z)); - size_t slab_id; if (horizontal && min_layer != zs.end() && *min_layer == min_z) { - // slicing the triangle. + // Slicing a horizontal triangle with a slicing plane. The triangle has to be upwards facing for ProjectionFromTop + // and downwards facing for ! ProjectionFromTop. assert(min_layer != max_layer); - slab_id = min_layer - zs.begin(); + size_t line_id = min_layer - zs.begin(); + for (int iedge = 0; iedge < 3; ++ iedge) + if (facet_neighbors(iedge) == -1) { + int i = iedge; + int j = next_idx_modulo(i, 3); + assert(vertices[i].z() == zs[line_id]); + assert(vertices[j].z() == zs[line_id]); + IntersectionLine il { + { to_2d(vertices[i]).cast(), to_2d(vertices[j]).cast() }, + indices(i), indices(j), -1, -1, + ProjectionFromTop ? IntersectionLine::FacetEdgeType::Bottom : IntersectionLine::FacetEdgeType::Top + }; + // Don't flip the FacetEdgeType::Top edge, it will be flipped when chaining. + // if (! ProjectionFromTop) il.reverse(); + boost::lock_guard l(lines_mutex[line_id >> 6]); + lines.at_slice[line_id].emplace_back(il); + } } else { + // Triangle is completely between two slicing planes, the triangle may or may not be horizontal, which + // does not matter for the processing of such a triangle. + size_t slab_id; if (ProjectionFromTop) { if (max_layer == zs.begin()) { // Not slicing the triangle and it is below the lowest layer. @@ -440,46 +467,88 @@ void slice_facet_with_slabs( slab_id = min_layer - zs.begin(); } } + if (ProjectionFromTop) + -- slab_id; + for (int iedge = 0; iedge < 3; ++ iedge) + if (facet_neighbors(iedge) == -1) { + int i = iedge; + int j = next_idx_modulo(i, 3); + assert(ProjectionFromTop ? vertices[i].z() >= zs[slab_id] : vertices[i].z() <= zs[slab_id]); + assert(ProjectionFromTop ? vertices[j].z() >= zs[slab_id] : vertices[j].z() <= zs[slab_id]); + emit_slab_edge( + IntersectionLine { + { to_2d(vertices[i]).cast(), to_2d(vertices[j]).cast() }, + indices(i), indices(j), -1, -1, IntersectionLine::FacetEdgeType::Slab + }, + slab_id, ! ProjectionFromTop); + } } - if (ProjectionFromTop) - -- slab_id; - for (int iedge = 0; iedge < 3; ++ iedge) - if (facet_neighbors(iedge) == -1) { - int i = iedge; - int j = next_idx_modulo(i, 3); - assert(ProjectionFromTop ? vertices[i].z() >= zs[slab_id] : vertices[i].z() <= zs[slab_id]); - assert(ProjectionFromTop ? vertices[j].z() >= zs[slab_id] : vertices[j].z() <= zs[slab_id]); - emit_slab_edge( - IntersectionLine { - { to_2d(vertices[i]).cast(), to_2d(vertices[j]).cast() }, - indices(i), indices(j), -1, -1, IntersectionLine::FacetEdgeType::Slab - }, - slab_id, ! ProjectionFromTop); - } } else { - int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0); + // 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); IntersectionLine il_prev; for (auto it = min_layer; it != max_layer; ++ it) { IntersectionLine il; auto type = slice_facet(*it, vertices, indices, facet_edge_ids, idx_vertex_lowest, false, il); - if (type == FacetSliceType::Slicing) { - if (! ProjectionFromTop) - il.reverse(); - size_t line_id = it - zs.begin(); - boost::lock_guard l(lines_mutex[line_id >> 6]); - lines.at_slice[line_id].emplace_back(il); - } else if (type == FacetSliceType::Cutting) { - // One edge is in plane, the 3rd vertex is above the plane. In case this edge has a neighbor, - // its opposite edge is added by slicing the neighboring triangle. Only if this edge has no neighbor, - // add this edge to lines. - assert(il.a_id != -1 && il.b_id != -1); - assert(il.edge_a_id == -1 && il.edge_b_id == -1); - // Identify edge ID from the edge vertices. - int edge_id = il.a_id == indices(0) ? 0 : il.a_id == indices(1) ? 1 : 2; - assert(il.a_id == indices(edge_id)); - assert(il.b_id == indices(next_idx_modulo(edge_id, 3))); - if (facet_neighbors(edge_id) == -1) { - // Open edge. + if (type == FacetSliceType::NoSlice) { + // One and exactly one vertex is touching the slicing plane. + } else { + if (il.edge_type == IntersectionLine::FacetEdgeType::Top || il.edge_type == IntersectionLine::FacetEdgeType::Bottom) { + // The non-horizontal triangle is being sliced at one of its edges. + // If the edge is open (it does not have a neighbor), add it. + // If the edge has a neighbor, then add it as TopBottom, and do it just once. + assert(il.a_id != -1 && il.b_id != -1); + assert(il.edge_a_id == -1 && il.edge_b_id == -1); + // Identify edge ID from the edge vertices. + int edge_id; + if (type == FacetSliceType::Cutting) { + // The edge is oriented CCW along the face perimeter. + assert(il.edge_type == IntersectionLine::FacetEdgeType::Bottom); + edge_id = il.a_id == indices(0) ? 0 : il.a_id == indices(1) ? 1 : 2; + assert(il.a_id == indices(edge_id)); + assert(il.b_id == indices(next_idx_modulo(edge_id, 3))); + } else { + // The edge is oriented CW along the face perimeter. + assert(il.edge_type == IntersectionLine::FacetEdgeType::Top); + edge_id = il.b_id == indices(0) ? 0 : il.b_id == indices(1) ? 1 : 2; + assert(il.b_id == indices(edge_id)); + assert(il.a_id == indices(next_idx_modulo(edge_id, 3))); + } + int neighbor_idx = facet_neighbors(edge_id); + if (neighbor_idx == -1) { + // Save the open edge for sure. + type = FacetSliceType::Slicing; + } else { + const stl_triangle_vertex_indices &neighbor = mesh_triangles[neighbor_idx]; + float z = *it; +#ifndef NDEBUG + int num_on_plane = (mesh_vertices[neighbor(0)].z() == z) + (mesh_vertices[neighbor(1)].z() == z) + (mesh_vertices[neighbor(2)].z() == z); + assert(num_on_plane == 2 || num_on_plane == 3); +#endif // NDEBUG + if (mesh_vertices[neighbor(0)].z() == z && mesh_vertices[neighbor(1)].z() == z && mesh_vertices[neighbor(2)].z() == z) { + // The neighbor triangle is horizontal. + // Is the corner convex or concave? + if (il.edge_type == (ProjectionFromTop ? IntersectionLine::FacetEdgeType::Top : IntersectionLine::FacetEdgeType::Bottom)) { + // Convex corner. Add this edge to both slabs, the edge is a boundary edge of both the projection patch below and + // above this slicing plane. + type = FacetSliceType::Slicing; + il.edge_type = IntersectionLine::FacetEdgeType::TopBottom; + } else { + // Concave corner. Ignore this edge, it is internal to the projection patch. + type = FacetSliceType::Cutting; + } + } else if (il.edge_type == IntersectionLine::FacetEdgeType::Top) { + // Indicate that the edge belongs to both the slab below and above the plane. + assert(type == FacetSliceType::Slicing); + il.edge_type = IntersectionLine::FacetEdgeType::TopBottom; + } else { + // Don't add this edge, as the neighbor triangle will add the same edge as FacetEdgeType::TopBottom. + assert(type == FacetSliceType::Cutting); + assert(il.edge_type == IntersectionLine::FacetEdgeType::Bottom); + } + } + } + if (type == FacetSliceType::Slicing) { if (! ProjectionFromTop) il.reverse(); size_t line_id = it - zs.begin(); @@ -668,7 +737,7 @@ inline std::pair slice_slabs_make_lines( if (fo2 != FaceOrientation::Up && fo2 != FaceOrientation::Degenerate) neighbors(i) = -1; } - slice_facet_with_slabs(vertices, indices[face_idx], neighbors, edge_ids, num_edges, zs, lines_top, lines_mutex_top); + slice_facet_with_slabs(vertices, indices, face_idx, neighbors, edge_ids, num_edges, zs, lines_top, lines_mutex_top); } if (bottom && (fo == FaceOrientation::Down || fo == FaceOrientation::Degenerate)) { Vec3i neighbors = face_neighbors[face_idx]; @@ -679,7 +748,7 @@ inline std::pair slice_slabs_make_lines( if (fo2 != FaceOrientation::Down && fo2 != FaceOrientation::Degenerate) neighbors(i) = -1; } - slice_facet_with_slabs(vertices, indices[face_idx], neighbors, edge_ids, num_edges, zs, lines_bottom, lines_mutex_bottom); + slice_facet_with_slabs(vertices, indices, face_idx, neighbors, edge_ids, num_edges, zs, lines_bottom, lines_mutex_bottom); } } } @@ -1131,7 +1200,7 @@ static Polygons make_loops( { static int iRun = 0; SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-%d.svg", iRun ++).c_str(), bbox_svg); - svg.draw(union_ex(*loops)); + svg.draw(union_ex(loops)); for (const OpenPolyline &pl : open_polylines) svg.draw(Polyline(pl.points), "red"); svg.Close(); @@ -1148,7 +1217,7 @@ static Polygons make_loops( { static int iRun = 0; SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines2-%d.svg", iRun++).c_str(), bbox_svg); - svg.draw(union_ex(*loops)); + svg.draw(union_ex(loops)); for (const OpenPolyline &pl : open_polylines) { if (pl.points.empty()) continue; @@ -1178,7 +1247,7 @@ static Polygons make_loops( { static int iRun = 0; SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-final-%d.svg", iRun++).c_str(), bbox_svg); - svg.draw(union_ex(*loops)); + svg.draw(union_ex(loops)); for (const OpenPolyline &pl : open_polylines) { if (pl.points.empty()) continue; @@ -1254,6 +1323,11 @@ static std::vector make_slab_loops( int num_edges, ThrowOnCancel throw_on_cancel) { +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + static int iRun = 0; + ++ iRun; +#endif // SLIC3R_DEBUG_SLICE_PROCESSING + assert(! lines.at_slice.empty() && lines.at_slice.size() == lines.between_slices.size()); std::vector layers; layers.resize(lines.at_slice.size()); @@ -1317,16 +1391,17 @@ static std::vector make_slab_loops( if (! in.empty()) { #ifdef SLIC3R_DEBUG_SLICE_PROCESSING BoundingBox bbox_svg; + coordf_t stroke_width = scale_(0.02); { - static int iRun = 0; for (const IntersectionLine &line : in) { bbox_svg.merge(line.a); bbox_svg.merge(line.b); } - SVG svg(debug_out_path("make_slab_loops-%d.svg", iRun++).c_str(), bbox_svg); + SVG svg(debug_out_path("make_slab_loops-in-%d-%d-%s.svg", iRun, line_idx, ProjectionFromTop ? "top" : "bottom").c_str(), bbox_svg); + svg.arrows = true; for (const IntersectionLine& line : in) { const char* color = line.source == IntersectionLine::Source::BottomPlane ? "red" : line.source == IntersectionLine::Source::TopPlane ? "blue" : "green"; - svg.draw(line, color, scaled(0.1)); + svg.draw(line, color, stroke_width); } svg.Close(); } @@ -1334,6 +1409,26 @@ static std::vector make_slab_loops( Polygons &loops = layers[line_idx]; std::vector open_polylines; chain_lines_by_triangle_connectivity(in, loops, open_polylines); +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + { + SVG svg(debug_out_path("make_slab_loops-out-%d-%d-%s.svg", iRun, line_idx, ProjectionFromTop ? "top" : "bottom").c_str(), bbox_svg); + svg.arrows = true; + for (const IntersectionLine& line : in) { + const char* color = line.source == IntersectionLine::Source::BottomPlane ? "red" : line.source == IntersectionLine::Source::TopPlane ? "blue" : "green"; + svg.draw(line, color, stroke_width); + } + svg.draw(loops, "black"); + svg.Close(); + } + { + SVG svg(debug_out_path("make_slab_loops-open-polylines-%d-%d-%s.svg", iRun, line_idx, ProjectionFromTop ? "top" : "bottom").c_str(), bbox_svg); + svg.draw(loops, "black"); + svg.arrows = true; + for (const OpenPolyline &open_polyline : open_polylines) + svg.draw(Polyline(open_polyline.points), "black", stroke_width); + svg.Close(); + } +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ assert(! loops.empty()); assert(open_polylines.empty()); } @@ -1694,8 +1789,10 @@ void slice_mesh_slabs( size_t num_duplicates = v.end() - std::unique(v.begin(), v.end()); assert(num_duplicates == 0); } + if (0) { // Verify that there are no T-joints. + // The T-joints could likely be already part of the source mesh. for (const auto &tri : mesh.indices) for (int i = 0; i < 3; ++ i) { int j = next_idx_modulo(i, 3);