From bd700fa43199c8046dd4433186ca4fc46595f17b Mon Sep 17 00:00:00 2001 From: Andrew Leek Date: Tue, 13 May 2025 16:33:16 +0200 Subject: [PATCH 1/6] Extra perimeters on overhangs vs Bridging fixup: Only build extra overhangs if no bridging is possible. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From my understanding “Extra perimeters on overhangs” detects areas where bridges cannot be anchored, and fills them with extra perimeter paths. However, the old logic only forced bridging when the unbridgeable area was less than 20% of the overhang area AND unsupported distance was less than 20% of the overhand total length. Which basically boils down to always creating extra perimeters on overhangs if the unbridgable area is greater than 20%. This change ensures that we bridge an area if any part of it is bridgable and if it is not bridgable then treat it as an overhang and create the extra perimeters to fill it. While this change may seem heavy handed, it seems to fix a lot of issues people have been having with “Extra perimeters on overhangs” Now while a better solution might be to bridge the bridgable parts and create extra overhangs on the non-bridgible parts, while investigating this approach I found that for this to work you would need to be able to apply bridging and then overhangs, but forcing this reorder may not be possible at the core of the slicer (please correct me if im wrong). Additionally, this approach currently requires much more investigation regarding merging of perimeters with overhangs and bridging. --- src/libslic3r/PerimeterGenerator.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index c67f0cf3f3..a4e737ac74 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -909,6 +909,8 @@ std::tuple, Polygons> generate_extra_perimeters_over Polygon anchoring_convex_hull = Geometry::convex_hull(anchoring); double unbridgeable_area = area(diff(real_overhang, {anchoring_convex_hull})); + Polygons bridgeable = intersection(real_overhang, anchoring_convex_hull); + double bridgeable_area = area(bridgeable); auto [dir, unsupp_dist] = detect_bridging_direction(real_overhang, anchors); @@ -928,7 +930,7 @@ std::tuple, Polygons> generate_extra_perimeters_over } #endif - if (unbridgeable_area < 0.2 * area(real_overhang) && unsupp_dist < total_length(real_overhang) * 0.2) { + if (bridgeable_area > 0.0) { inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(),overhang_to_cover.begin(),overhang_to_cover.end()); perimeter_polygon.clear(); } else { From 2bd038a57a54b5aef89dc2bb1e526b650dbdcd37 Mon Sep 17 00:00:00 2001 From: Andrew Leek Date: Tue, 13 May 2025 16:51:16 +0200 Subject: [PATCH 2/6] Extra perimeters on overhangs vs Bridging: Dont bridge a bridgable area of it has a hole If an unsupported area has a hole in it, it should be considered an overhang instead of a bridge as the internal hole perimeter cannot be bridged to since it is created after the bridge. --- src/libslic3r/PerimeterGenerator.cpp | 14 ++++++++------ src/libslic3r/PerimeterGenerator.hpp | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index a4e737ac74..dd7f51464f 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -860,7 +860,8 @@ std::tuple, Polygons> generate_extra_perimeters_over const Flow &overhang_flow, double scaled_resolution, const PrintObjectConfig &object_config, - const PrintConfig &print_config) + const PrintConfig &print_config, + ExPolygon surface) { coord_t anchors_size = std::min(coord_t(scale_(EXTERNAL_INFILL_MARGIN)), overhang_flow.scaled_spacing() * (perimeter_count + 1)); @@ -930,7 +931,7 @@ std::tuple, Polygons> generate_extra_perimeters_over } #endif - if (bridgeable_area > 0.0) { + if (bridgeable_area > 0.0 && surface.holes.size() == 0) { inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(),overhang_to_cover.begin(),overhang_to_cover.end()); perimeter_polygon.clear(); } else { @@ -1058,7 +1059,7 @@ std::tuple, Polygons> generate_extra_perimeters_over return {extra_perims, diff(inset_overhang_area, inset_overhang_area_left_unfilled)}; } -void PerimeterGenerator::apply_extra_perimeters(ExPolygons &infill_area) +void PerimeterGenerator::apply_extra_perimeters(ExPolygons &infill_area, const ExPolygon &surface) { if (!m_spiral_vase && this->lower_slices != nullptr && this->config->detect_overhang_wall && this->config->extra_perimeters_on_overhangs && this->config->wall_loops > 0 && this->layer_id > this->object_config->raft_layers) { @@ -1066,7 +1067,8 @@ void PerimeterGenerator::apply_extra_perimeters(ExPolygons &infill_area) auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_area, this->lower_slices_polygons(), this->config->wall_loops, this->overhang_flow, this->m_scaled_resolution, *this->object_config, - *this->print_config); + *this->print_config, + surface); if (!extra_perimeters.empty()) { ExtrusionEntityCollection *this_islands_perimeters = static_cast(this->loops->entities.back()); ExtrusionEntityCollection new_perimeters{}; @@ -1644,7 +1646,7 @@ void PerimeterGenerator::process_classic() } this->fill_surfaces->append(infill_exp, stInternal); - apply_extra_perimeters(infill_exp); + apply_extra_perimeters(infill_exp, surface.expolygon); // BBS: get the no-overlap infill expolygons { @@ -2504,7 +2506,7 @@ void PerimeterGenerator::process_arachne() } this->fill_surfaces->append(infill_exp, stInternal); - apply_extra_perimeters(infill_exp); + apply_extra_perimeters(infill_exp, surface.expolygon); // BBS: get the no-overlap infill expolygons { diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 74be04294e..6a359f9d79 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -139,7 +139,7 @@ public: private: std::vector generate_lower_polygons_series(float width); void split_top_surfaces(const ExPolygons &orig_polygons, ExPolygons &top_fills, ExPolygons &non_top_polygons, ExPolygons &fill_clip) const; - void apply_extra_perimeters(ExPolygons& infill_area); + void apply_extra_perimeters(ExPolygons& infill_area, const ExPolygon& surface); void process_no_bridge(Surfaces& all_surfaces, coord_t perimeter_spacing, coord_t ext_perimeter_width); private: From 1bb7891f755a9d320d7f093fc3a717c5c4cf163c Mon Sep 17 00:00:00 2001 From: Andrew Leek Date: Tue, 13 May 2025 16:56:43 +0200 Subject: [PATCH 3/6] Extra perimeters on overhangs: better test for if first overhang perimeter is anchored Unfortunately if the first polyline is exactly aligned with a perimeter of optimized_lower_slices it is considered intersecting. This fix adds a half extrusion width offset because we want to make sure we are actually intersecting with optimized_lower_slices and are truly anchored. NOTE: The offset could probably be just as effective at a lower value. --- src/libslic3r/PerimeterGenerator.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index dd7f51464f..7f022b8556 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -1014,9 +1014,17 @@ std::tuple, Polygons> generate_extra_perimeters_over // When this happens, the first overhang perimeter is also a closed loop, and needs special check // instead of the following simple is_anchored lambda, which checks only the first and last point (not very useful on closed // polyline) + + // Unfortunately if the first polyline is exactly aligned with a perimeter of optimized_lower_slices it is considered intersecting, + // This fix adds a half extrusion width offset because we want to make sure we are actually intersecting with optimized_lower_slices + // Basically this ensures we are truly anchored. + Polygons first_overhang_polygons = to_polygons({overhang_region.front().polyline}); + Polygons first_overhang_polygons_shrunk = offset(first_overhang_polygons, -0.25 * overhang_flow.scaled_spacing()); + Polylines first_overhang_polyline_shrunk = to_polylines(first_overhang_polygons_shrunk); + bool first_overhang_is_closed_and_anchored = (overhang_region.front().first_point() == overhang_region.front().last_point() && - !intersection_pl(overhang_region.front().polyline, optimized_lower_slices).empty()); + !intersection_pl(first_overhang_polyline_shrunk, optimized_lower_slices).empty()); auto is_anchored = [&lower_layer_aabb_tree](const ExtrusionPath &path) { return lower_layer_aabb_tree.distance_from_lines(path.first_point()) <= 0 || From a00309c60db59b88502d1472e11c55e9f1e20078 Mon Sep 17 00:00:00 2001 From: Andrew Leek Date: Wed, 14 May 2025 14:20:55 +0200 Subject: [PATCH 4/6] Extra perimeters on overhangs vs Bridging: hole fix Check that surface holes intersect with real_overhang --- src/libslic3r/PerimeterGenerator.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 7f022b8556..90f4857e05 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -902,6 +902,17 @@ std::tuple, Polygons> generate_extra_perimeters_over overhang_to_cover.end()); continue; } + + ExPolygons real_overhang_ex = simplify_polygons_ex(real_overhang); + + for (ExPolygon& expol : real_overhang_ex) { + expol.holes.clear(); + } + + Polygons real_overhang_no_holes = to_polygons(real_overhang_ex); + + Polygons real_overhang_upper_layer_holes = intersection(surface.holes, real_overhang_no_holes); + ExtrusionPaths &overhang_region = extra_perims.emplace_back(); Polygons anchoring = intersection(expanded_overhang_to_cover, inset_anchors); @@ -931,7 +942,7 @@ std::tuple, Polygons> generate_extra_perimeters_over } #endif - if (bridgeable_area > 0.0 && surface.holes.size() == 0) { + if (bridgeable_area > 0.0 && real_overhang_upper_layer_holes.empty()) { inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(),overhang_to_cover.begin(),overhang_to_cover.end()); perimeter_polygon.clear(); } else { From d2690e5f12c6807d609dafbd49ffd7bac4a1f87b Mon Sep 17 00:00:00 2001 From: Andrew Leek Date: Fri, 20 Jun 2025 18:07:52 +0200 Subject: [PATCH 5/6] Extra perimeters on overhangs vs Bridging: calculate anchors_size using BRIDGE_INFILL_MARGIN instead of EXTERNAL_INFILL_MARGIN EXTERNAL_INFILL_MARGIN seems to be quite large and is even noted as such in libslic3r.h line 84. Additionally, BRIDGE_INFILL_MARGIN exists and is smaller and we are technically working with overhangs/bridges so it makes sense to move to this --- src/libslic3r/PerimeterGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 90f4857e05..6e72daa991 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -863,7 +863,7 @@ std::tuple, Polygons> generate_extra_perimeters_over const PrintConfig &print_config, ExPolygon surface) { - coord_t anchors_size = std::min(coord_t(scale_(EXTERNAL_INFILL_MARGIN)), overhang_flow.scaled_spacing() * (perimeter_count + 1)); + coord_t anchors_size = std::min(coord_t(scale_(BRIDGE_INFILL_MARGIN)), overhang_flow.scaled_spacing() * (perimeter_count + 1)); BoundingBox infill_area_bb = get_extents(infill_area).inflated(SCALED_EPSILON); Polygons optimized_lower_slices = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_slices_polygons, infill_area_bb); From a9154b0af0bc72270e673b4d4966c1573e0b4bf5 Mon Sep 17 00:00:00 2001 From: Andrew Leek Date: Mon, 4 Aug 2025 11:12:00 +0200 Subject: [PATCH 6/6] Bridging/Overhang Anchor Size: minimum size is now a single extrusion width --- src/libslic3r/PerimeterGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 6e72daa991..6112362597 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -863,7 +863,7 @@ std::tuple, Polygons> generate_extra_perimeters_over const PrintConfig &print_config, ExPolygon surface) { - coord_t anchors_size = std::min(coord_t(scale_(BRIDGE_INFILL_MARGIN)), overhang_flow.scaled_spacing() * (perimeter_count + 1)); + coord_t anchors_size = std::min(coord_t(scale_(BRIDGE_INFILL_MARGIN)), overhang_flow.scaled_spacing()); BoundingBox infill_area_bb = get_extents(infill_area).inflated(SCALED_EPSILON); Polygons optimized_lower_slices = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_slices_polygons, infill_area_bb);