From 1a91d85e7e6c69b30f0719e045a3ed3a04f54f00 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 6 Jan 2023 17:53:49 +0100 Subject: [PATCH] Fixes of recent RegionExpansion implementation. Enabled thick internal bridges even if external thick bridges are disabled. Fixed compilation of conditionally compiled debugging code. --- src/libslic3r/Algorithm/RegionExpansion.cpp | 24 +++---- src/libslic3r/ClipperZUtils.hpp | 9 ++- src/libslic3r/LayerRegion.cpp | 14 ++-- src/libslic3r/PrintObject.cpp | 74 +++++---------------- src/libslic3r/SupportMaterial.cpp | 2 +- src/libslic3r/TriangleMeshSlicer.cpp | 22 +++--- 6 files changed, 53 insertions(+), 92 deletions(-) diff --git a/src/libslic3r/Algorithm/RegionExpansion.cpp b/src/libslic3r/Algorithm/RegionExpansion.cpp index 3846c47495..844cda8223 100644 --- a/src/libslic3r/Algorithm/RegionExpansion.cpp +++ b/src/libslic3r/Algorithm/RegionExpansion.cpp @@ -76,12 +76,11 @@ RegionExpansionParameters RegionExpansionParameters::build( // similar to expolygons_to_zpaths(), but each contour is expanded before converted to zpath. // The expanded contours are then opened (the first point is repeated at the end). static ClipperLib_Z::Paths expolygons_to_zpaths_expanded_opened( - const ExPolygons &src, const float expansion, coord_t base_idx) + const ExPolygons &src, const float expansion, coord_t &base_idx) { ClipperLib_Z::Paths out; out.reserve(2 * std::accumulate(src.begin(), src.end(), size_t(0), [](const size_t acc, const ExPolygon &expoly) { return acc + expoly.num_contours(); })); - coord_t z = base_idx; ClipperLib::ClipperOffset offsetter; offsetter.ShortestEdgeLength = expansion * ClipperOffsetShortestEdgeFactor; ClipperLib::Paths expansion_cache; @@ -94,9 +93,9 @@ static ClipperLib_Z::Paths expolygons_to_zpaths_expanded_opened( offsetter.AddPath(expoly.contour_or_hole(icontour).points, ClipperLib::jtSquare, ClipperLib::etClosedPolygon); expansion_cache.clear(); offsetter.Execute(expansion_cache, icontour == 0 ? expansion : -expansion); - append(out, ClipperZUtils::to_zpaths(expansion_cache, z)); + append(out, ClipperZUtils::to_zpaths(expansion_cache, base_idx)); } - ++ z; + ++ base_idx; } return out; } @@ -217,7 +216,7 @@ std::vector wave_seeds( Intersections intersections; coord_t idx_boundary_begin = 1; - coord_t idx_boundary_end; + coord_t idx_boundary_end = idx_boundary_begin; coord_t idx_src_end; { @@ -225,17 +224,13 @@ std::vector wave_seeds( ClipperZUtils::ClipperZIntersectionVisitor visitor(intersections); zclipper.ZFillFunction(visitor.clipper_callback()); // as closed contours - { - ClipperLib_Z::Paths zboundary = ClipperZUtils::expolygons_to_zpaths(boundary, idx_boundary_begin); - idx_boundary_end = idx_boundary_begin + coord_t(zboundary.size()); - zclipper.AddPaths(zboundary, ClipperLib_Z::ptClip, true); - } + zclipper.AddPaths(ClipperZUtils::expolygons_to_zpaths(boundary, idx_boundary_end), ClipperLib_Z::ptClip, true); // as open contours std::vector> zsrc_splits; { - ClipperLib_Z::Paths zsrc = expolygons_to_zpaths_expanded_opened(src, tiny_expansion, idx_boundary_end); + idx_src_end = idx_boundary_end; + ClipperLib_Z::Paths zsrc = expolygons_to_zpaths_expanded_opened(src, tiny_expansion, idx_src_end); zclipper.AddPaths(zsrc, ClipperLib_Z::ptSubject, false); - idx_src_end = idx_boundary_end + coord_t(zsrc.size()); zsrc_splits.reserve(zsrc.size()); for (const ClipperLib_Z::Path &path : zsrc) { assert(path.size() >= 2); @@ -267,7 +262,10 @@ std::vector wave_seeds( const ClipperLib_Z::IntPoint &back = path.back(); // Both ends of a seed segment are supposed to be inside a single boundary expolygon. // Thus as long as the seed contour is not closed, it should be open at a boundary point. - assert((front == back && front.z() >= idx_boundary_end && front.z() < idx_src_end) || (front.z() < 0 && back.z() < 0)); + assert((front == back && front.z() >= idx_boundary_end && front.z() < idx_src_end) || + //(front.z() < 0 && back.z() < 0)); + // Hope that at least one end of an open polyline is clipped by the boundary, thus an intersection point is created. + (front.z() < 0 || back.z() < 0)); const Intersection *intersection = nullptr; auto intersection_point_valid = [idx_boundary_end, idx_src_end](const Intersection &is) { return is.first >= 1 && is.first < idx_boundary_end && diff --git a/src/libslic3r/ClipperZUtils.hpp b/src/libslic3r/ClipperZUtils.hpp index dd42e3d66b..4ae78ae235 100644 --- a/src/libslic3r/ClipperZUtils.hpp +++ b/src/libslic3r/ClipperZUtils.hpp @@ -53,17 +53,16 @@ inline ZPaths to_zpaths(const std::vector &paths, coord_t z) // offsetted by base_index. // If Open, then duplicate the first point of each path at its end. template -inline ZPaths expolygons_to_zpaths(const ExPolygons &src, coord_t base_idx) +inline ZPaths expolygons_to_zpaths(const ExPolygons &src, coord_t &base_idx) { ZPaths out; out.reserve(std::accumulate(src.begin(), src.end(), size_t(0), [](const size_t acc, const ExPolygon &expoly) { return acc + expoly.num_contours(); })); - coord_t z = base_idx; for (const ExPolygon &expoly : src) { - out.emplace_back(to_zpath(expoly.contour.points, z)); + out.emplace_back(to_zpath(expoly.contour.points, base_idx)); for (const Polygon &hole : expoly.holes) - out.emplace_back(to_zpath(hole.points, z)); - ++ z; + out.emplace_back(to_zpath(hole.points, base_idx)); + ++ base_idx; } return out; } diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index c26ed7dfdc..0777c5ef78 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -215,19 +215,19 @@ Surfaces expand_bridges_detect_orientations( // bridge_expansions are sorted by boundary id and source id. for (auto it = bridge_expansions.begin(); it != bridge_expansions.end();) { // For each boundary region: - auto it2 = it; - for (++ it2; it2 != bridge_expansions.end() && it2->boundary_id == it->boundary_id; ++ it2); + auto it_begin = it; + auto it_end = std::next(it_begin); + for (; it_end != bridge_expansions.end() && it_end->boundary_id == it_begin->boundary_id; ++ it_end) ; bboxes.clear(); - bboxes.reserve(it2 - it); - for (it2 = it; it2 != bridge_expansions.end() && it2->boundary_id == it->boundary_id; ++ it2) + bboxes.reserve(it_end - it_begin); + for (auto it2 = it_begin; it2 != it_end; ++ it2) bboxes.emplace_back(get_extents(it2->expolygon.contour)); - auto it_end = it2; // For each bridge anchor of the current source: for (; it != it_end; ++ it) { // A grup id for this bridge. - for (it2 = std::next(it); it2 != it_end; ++ it2) + for (auto it2 = std::next(it); it2 != it_end; ++ it2) if (it->src_id != it2->src_id && - bboxes[it - bridge_expansions.begin()].overlap(bboxes[it2 - bridge_expansions.begin()]) && + bboxes[it - it_begin].overlap(bboxes[it2 - it_begin]) && // One may ignore holes, they are irrelevant for intersection test. ! intersection(it->expolygon.contour, it2->expolygon.contour).empty()) { // The two bridge regions intersect. Give them the same group id. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 85e70779ce..7d97ec256b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -964,9 +964,9 @@ void PrintObject::detect_surfaces_type() { static int iRun = 0; std::vector> expolygons_with_attributes; - expolygons_with_attributes.emplace_back(std::make_pair(union_ex(top), SVG::ExPolygonAttributes("green"))); - expolygons_with_attributes.emplace_back(std::make_pair(union_ex(bottom), SVG::ExPolygonAttributes("brown"))); - expolygons_with_attributes.emplace_back(std::make_pair(to_expolygons(layerm->slices.surfaces), SVG::ExPolygonAttributes("black"))); + expolygons_with_attributes.emplace_back(std::make_pair(union_ex(top), SVG::ExPolygonAttributes("green"))); + expolygons_with_attributes.emplace_back(std::make_pair(union_ex(bottom), SVG::ExPolygonAttributes("brown"))); + expolygons_with_attributes.emplace_back(std::make_pair(to_expolygons(layerm->slices().surfaces), SVG::ExPolygonAttributes("black"))); SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ @@ -1399,26 +1399,26 @@ void PrintObject::discover_vertical_shells() #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", debug_idx), get_extents(shell)); - svg.draw(layerm->fill_surfaces.filter_by_type(stInternal), "yellow", 0.5); - svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternal), "black", "blue", scale_(0.05)); + svg.draw(layerm->fill_surfaces().filter_by_type(stInternal), "yellow", 0.5); + svg.draw_outline(layerm->fill_surfaces().filter_by_type(stInternal), "black", "blue", scale_(0.05)); svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); } { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); - svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); - svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); + svg.draw(layerm->fill_surfaces().filter_by_type(stInternalVoid), "yellow", 0.5); + svg.draw_outline(layerm->fill_surfaces().filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); } { - Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); - svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); - svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalsolid-wshell-%d.svg", debug_idx), get_extents(shell)); + svg.draw(layerm->fill_surfaces().filter_by_type(stInternalSolid), "yellow", 0.5); + svg.draw_outline(layerm->fill_surfaces().filter_by_type(stInternalSolid), "black", "blue", scale_(0.05)); svg.draw(shell_ex, "blue", 0.5); - svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); + svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ @@ -1544,7 +1544,7 @@ void PrintObject::bridge_over_infill() internals.reserve(this->layer_count()); for (Layer *layer : m_layers) { Polygons sum; - for (const LayerRegion *layerm : layer->m_regions) + for (const LayerRegion *layerm : layer->regions()) layerm->fill_surfaces().filter_by_type(stInternal, &sum); internals.emplace_back(std::move(sum)); } @@ -1558,7 +1558,7 @@ void PrintObject::bridge_over_infill() const size_t region_id = sparse_infill_regions[task_id % sparse_infill_regions.size()]; Layer *layer = this->get_layer(layer_id); LayerRegion *layerm = layer->m_regions[region_id]; - Flow bridge_flow = layerm->bridging_flow(frSolidInfill); + Flow bridge_flow = layerm->bridging_flow(frSolidInfill, true /* Internal bridges are always thick. */); // Extract the stInternalSolid surfaces that might be transformed into bridges. ExPolygons internal_solid; @@ -1567,32 +1567,27 @@ void PrintObject::bridge_over_infill() // No internal solid -> no new bridges for this layer region. continue; - // check whether the lower area is deep enough for absorbing the extra flow - // (for obvious physical reasons but also for preventing the bridge extrudates - // from overflowing in 3D preview) + // Check whether the lower area is deep enough for absorbing the extra flow, also filter out + // tiny regions from bridging. ExPolygons to_bridge; { Polygons to_bridge_pp = to_polygons(internal_solid); // Iterate through lower layers spanned by bridge_flow. double bottom_z = layer->print_z - bridge_flow.height() - EPSILON; - for (auto i = int(layer_id) - 1; i >= 0; -- i) { - // Stop iterating if layer is lower than bottom_z. - if (m_layers[i]->print_z < bottom_z) - break; + for (auto i = int(layer_id) - 1; i >= 0 && m_layers[i]->print_z > bottom_z; -- i) // Intersect lower sparse infills with the candidate solid surfaces. to_bridge_pp = intersection(to_bridge_pp, internals[i]); - } // there's no point in bridging too thin/short regions //FIXME Vojtech: The offset2 function is not a geometric offset, // therefore it may create 1) gaps, and 2) sharp corners, which are outside the original contour. // The gaps will be filled by a separate region, which makes the infill less stable and it takes longer. { float min_width = float(bridge_flow.scaled_width()) * 3.f; - to_bridge_pp = opening(to_bridge_pp, min_width); + to_bridge_pp = opening(to_bridge_pp, min_width); //, ClipperLib::jtSquare); } if (to_bridge_pp.empty()) { - // Restore internal_solid surfaces. + // Optimization: Nothing to bridge, restore internal_solid surfaces. for (ExPolygon &ex : internal_solid) layerm->m_fill_surfaces.surfaces.push_back(Surface(stInternalSolid, std::move(ex))); continue; @@ -1613,39 +1608,6 @@ void PrintObject::bridge_over_infill() layerm->m_fill_surfaces.surfaces.push_back(Surface(stInternalBridge, std::move(ex))); for (ExPolygon &ex : not_to_bridge) layerm->m_fill_surfaces.surfaces.push_back(Surface(stInternalSolid, std::move(ex))); - /* - # exclude infill from the layers below if needed - # see discussion at https://github.com/alexrj/Slic3r/issues/240 - # Update: do not exclude any infill. Sparse infill is able to absorb the excess material. - if (0) { - my $excess = $layerm->extruders->{infill}->bridge_flow->width - $layerm->height; - for (my $i = $layer_id-1; $excess >= $self->get_layer($i)->height; $i--) { - Slic3r::debugf " skipping infill below those areas at layer %d\n", $i; - foreach my $lower_layerm (@{$self->get_layer($i)->regions}) { - my @new_surfaces = (); - # subtract the area from all types of surfaces - foreach my $group (@{$lower_layerm->fill_surfaces->group}) { - push @new_surfaces, map $group->[0]->clone(expolygon => $_), - @{diff_ex( - [ map $_->p, @$group ], - [ map @$_, @$to_bridge ], - )}; - push @new_surfaces, map Slic3r::Surface->new( - expolygon => $_, - surface_type => stInternalVoid, - ), @{intersection_ex( - [ map $_->p, @$group ], - [ map @$_, @$to_bridge ], - )}; - } - $lower_layerm->fill_surfaces->clear; - $lower_layerm->fill_surfaces->append($_) for @new_surfaces; - } - - $excess -= $self->get_layer($i)->height; - } - } - */ #ifdef SLIC3R_DEBUG_SLICE_PROCESSING layerm->export_region_slices_to_svg_debug("7_bridge_over_infill"); layerm->export_region_fill_surfaces_to_svg_debug("7_bridge_over_infill"); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index d46094f7fb..af3f4f88ad 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -1320,7 +1320,7 @@ namespace SupportMaterialInternal { bridges = union_(bridges); } // remove the entire bridges and only support the unsupported edges - //FIXME the brided regions are already collected as layerm.bridged. Use it? + //FIXME the bridged regions are already collected as layerm.bridged. Use it? for (const Surface &surface : layerm.fill_surfaces()) if (surface.surface_type == stBottomBridge && surface.bridge_angle < 0.0) polygons_append(bridges, surface.expolygon); diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 85e170bd03..96f61ba8db 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -62,6 +62,8 @@ public: // Inherits coord_t x, y }; +#define DEBUG_INTERSECTIONLINE (! defined(NDEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)) + class IntersectionLine : public Line { public: @@ -119,14 +121,14 @@ public: }; uint32_t flags { 0 }; -#ifndef NDEBUG +#if DEBUG_INTERSECTIONLINE enum class Source { BottomPlane, TopPlane, Slab, }; Source source { Source::BottomPlane }; -#endif // NDEBUG +#endif }; using IntersectionLines = std::vector; @@ -1440,24 +1442,24 @@ static std::vector make_slab_loops( for (const IntersectionLine &l : lines.at_slice[slice_below]) if (l.edge_type != IntersectionLine::FacetEdgeType::Top) { in.emplace_back(l); -#ifndef NDEBUG +#if DEBUG_INTERSECTIONLINE in.back().source = IntersectionLine::Source::BottomPlane; -#endif // NDEBUG +#endif // DEBUG_INTERSECTIONLINE } } { // Edges in between slice_below and slice_above. -#ifndef NDEBUG +#if DEBUG_INTERSECTIONLINE size_t old_size = in.size(); -#endif // NDEBUG +#endif // DEBUG_INTERSECTIONLINE // Edge IDs of end points on in-between lines that touch the layer above are already increased with num_edges. append(in, lines.between_slices[line_idx]); -#ifndef NDEBUG +#if DEBUG_INTERSECTIONLINE for (auto it = in.begin() + old_size; it != in.end(); ++ it) { assert(it->edge_type == IntersectionLine::FacetEdgeType::Slab); it->source = IntersectionLine::Source::Slab; } -#endif // NDEBUG +#endif // DEBUG_INTERSECTIONLINE } if (has_slice_above) { for (const IntersectionLine &lsrc : lines.at_slice[slice_above]) @@ -1470,9 +1472,9 @@ static std::vector make_slab_loops( l.edge_a_id += num_edges; if (l.edge_b_id >= 0) l.edge_b_id += num_edges; -#ifndef NDEBUG +#if DEBUG_INTERSECTIONLINE l.source = IntersectionLine::Source::TopPlane; -#endif // NDEBUG +#endif // DEBUG_INTERSECTIONLINE } } if (! in.empty()) {