diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index cd973ac9de..eee0c2abab 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2284,6 +2284,7 @@ void PrintObject::project_and_append_custom_facets( seam, out); else { std::vector projected; + // Support blockers or enforcers. Project downward facing painted areas upwards to their respective slicing plane. slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){}); // Merge these projections with the output, layer by layer. assert(! projected.empty()); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 9e1eb3e75e..ef23b0871a 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -1645,26 +1645,29 @@ static inline std::tuple detect_overhangs( polygons_append(contact_polygons, diff_polygons); } // for each layer.region - if (has_enforcer) { - // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes. -#ifdef SLIC3R_DEBUG - ExPolygons enforcers_united = union_ex(annotations.enforcers_layers[layer_id]); -#endif // SLIC3R_DEBUG - enforcer_polygons = diff(intersection(layer.lslices, annotations.enforcers_layers[layer_id]), - // Inflate just a tiny bit to avoid intersection of the overhang areas with the object. - expand(lower_layer_polygons, 0.05f * no_interface_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); -#ifdef SLIC3R_DEBUG - SVG::export_expolygons(debug_out_path("support-top-contacts-enforcers-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), - { { layer.lslices, { "layer.lslices", "gray", 0.2f } }, - { { union_ex(lower_layer_polygons) }, { "lower_layer_polygons", "green", 0.5f } }, - { enforcers_united, { "enforcers", "blue", 0.5f } }, - { { union_safety_offset_ex(enforcer_polygons) }, { "new_contacts", "red", "black", "", scaled(0.1f), 0.5f } } }); -#endif /* SLIC3R_DEBUG */ - polygons_append(overhang_polygons, enforcer_polygons); - slices_margin_update(std::min(lower_layer_offset, float(scale_(gap_xy))), no_interface_offset); - polygons_append(contact_polygons, diff(enforcer_polygons, slices_margin.all_polygons.empty() ? slices_margin.polygons : slices_margin.all_polygons)); + if (has_enforcer) + if (const Polygons &enforcer_polygons_src = annotations.enforcers_layers[layer_id]; ! enforcer_polygons_src.empty()) { + // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes. + #ifdef SLIC3R_DEBUG + ExPolygons enforcers_united = union_ex(enforcer_polygons_src); + #endif // SLIC3R_DEBUG + enforcer_polygons = diff(intersection(layer.lslices, enforcer_polygons_src), + // Inflate just a tiny bit to avoid intersection of the overhang areas with the object. + expand(lower_layer_polygons, 0.05f * no_interface_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); + #ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-top-contacts-enforcers-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), + { { layer.lslices, { "layer.lslices", "gray", 0.2f } }, + { { union_ex(lower_layer_polygons) }, { "lower_layer_polygons", "green", 0.5f } }, + { enforcers_united, { "enforcers", "blue", 0.5f } }, + { { union_safety_offset_ex(enforcer_polygons) }, { "new_contacts", "red", "black", "", scaled(0.1f), 0.5f } } }); + #endif /* SLIC3R_DEBUG */ + if (! enforcer_polygons.empty()) { + polygons_append(overhang_polygons, enforcer_polygons); + slices_margin_update(std::min(lower_layer_offset, float(scale_(gap_xy))), no_interface_offset); + polygons_append(contact_polygons, diff(enforcer_polygons, slices_margin.all_polygons.empty() ? slices_margin.polygons : slices_margin.all_polygons)); + } + } } - } return std::make_tuple(std::move(overhang_polygons), std::move(contact_polygons), std::move(enforcer_polygons), no_interface_offset); } diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index e61128c64f..38db180434 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -458,23 +458,33 @@ void slice_facet_with_slabs( // 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); - 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 % lines_mutex.size()]); - lines.at_slice[line_id].emplace_back(il); - } + // Slicing plane with which the triangle is coplanar. + size_t slice_id = min_layer - zs.begin(); +#if 0 + // Project the coplanar bottom facing triangles to their slicing plane for both top and bottom facing surfaces. + // This behavior is different from slice_mesh() / slice_mesh_ex(), which do not slice bottom facing faces exactly on slicing plane. + size_t line_id = slice_id; +#else + // Project the coplanar bottom facing triangles to the plane above the slicing plane to match the behavior of slice_mesh() / slice_mesh_ex(), + // where the slicing plane slices the top facing surfaces, but misses the bottom facing surfaces. + if (size_t line_id = ProjectionFromTop ? slice_id : slice_id + 1; ProjectionFromTop || line_id < lines.at_slice.size()) +#endif + 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[slice_id]); + assert(vertices[j].z() == zs[slice_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 % lines_mutex.size()]); + 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. @@ -539,6 +549,7 @@ void slice_facet_with_slabs( assert(il.b_id == indices(next_idx_modulo(edge_id, 3))); } else { // The edge is oriented CW along the face perimeter. + assert(type == FacetSliceType::Slicing); 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)); @@ -555,8 +566,11 @@ void slice_facet_with_slabs( 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 0 if (mesh_vertices[neighbor(0)].z() == z && mesh_vertices[neighbor(1)].z() == z && mesh_vertices[neighbor(2)].z() == z) { // The neighbor triangle is horizontal. + // Assign the horizontal projections to slicing planes differently from the usual triangle mesh slicing: + // Slicing plane slices top surfaces when projecting from top, it slices bottom surfaces when projecting from bottom. // 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 @@ -567,7 +581,12 @@ void slice_facet_with_slabs( // Concave corner. Ignore this edge, it is internal to the projection patch. type = FacetSliceType::Cutting; } - } else if (il.edge_type == IntersectionLine::FacetEdgeType::Top) { + } else +#else + // Project the coplanar bottom facing triangles to the plane above the slicing plane to match the behavior of slice_mesh() / slice_mesh_ex(), + // where the slicing plane slices the top facing surfaces, but misses the bottom facing surfaces. +#endif + 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; diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp index 5e08b58e77..fbe7244332 100644 --- a/src/libslic3r/TriangleMeshSlicer.hpp +++ b/src/libslic3r/TriangleMeshSlicer.hpp @@ -46,6 +46,17 @@ struct MeshSlicingParamsEx : public MeshSlicingParams double resolution { 0 }; }; +// All the following slicing functions shall produce consistent results with the same mesh, same transformation matrix and slicing parameters. +// Namely, slice_mesh_slabs() shall produce consistent results with slice_mesh() and slice_mesh_ex() in the sense, that projections made by +// slice_mesh_slabs() shall fall onto slicing planes produced by slice_mesh(). +// +// If a slicing plane slices a horizontal face of a mesh exactly, +// an upward facing horizontal face is is considered on slicing plane, +// while a downward facing horizontal face is considered not on slicing plane. +// +// slice_mesh_slabs() thus projects an upward facing horizontal slice to the slicing plane, +// while slice_mesh_slabs() projects a downward facing horizontal slice to the slicing plane above if it exists. + std::vector slice_mesh( const indexed_triangle_set &mesh, const std::vector &zs,