From 79adb72a2529a56dd9cca1628298b4b6bcea1427 Mon Sep 17 00:00:00 2001
From: PavelMikus <pavel.mikus.mail@seznam.cz>
Date: Fri, 17 Mar 2023 17:13:57 +0100
Subject: [PATCH] Fix int overflow, reduces amount of bridging substantially,
 improve code description, extend bridge anchors when anchoring to solid
 surfaces on lower layers

---
 src/libslic3r/PrintObject.cpp | 35 +++++++++++++++++++++--------------
 1 file changed, 21 insertions(+), 14 deletions(-)

diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index b72a2bacb..86cb16e2d 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -1618,7 +1618,8 @@ void PrintObject::bridge_over_infill()
                 if (layer->lower_layer == nullptr) {
                     continue;
                 }
-                auto       spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing();
+                double spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing();
+                // unsupported area will serve as a filter for polygons worth bridging.
                 Polygons   unsupported_area;
                 Polygons   lower_layer_solids;
                 bool contains_only_lightning = true;
@@ -1627,6 +1628,7 @@ void PrintObject::bridge_over_infill()
                         contains_only_lightning = false;
                     }
                     Polygons fill_polys = to_polygons(region->fill_expolygons());
+                    // initially consider the whole layer unsupported, but also gather solid layers to later cut off supported parts
                     unsupported_area.insert(unsupported_area.end(), fill_polys.begin(), fill_polys.end());
                     for (const Surface &surface : region->fill_surfaces()) {
                         if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) {
@@ -1636,25 +1638,30 @@ void PrintObject::bridge_over_infill()
                     }
                 }
                 unsupported_area = closing(unsupported_area, SCALED_EPSILON);
-
-                lower_layer_solids = expand(lower_layer_solids, 4 * spacing);
-                unsupported_area   = shrink(unsupported_area, 4 * spacing);
+                // By expanding the lower layer solids, we avoid making bridges from the tiny internal overhangs that are (very likely) supported by previous layer solids
+                // NOTE that we cannot filter out polygons worth bridging by their area, because sometimes there is a very small internal island that will grow into large hole
+                lower_layer_solids = expand(lower_layer_solids, 3 * spacing);
+                // By shrinking the unsupported area, we avoid making bridges from narrow ensuring region along perimeters.
+                unsupported_area   = shrink(unsupported_area, 3 * spacing);
                 unsupported_area   = diff(unsupported_area, lower_layer_solids);
 
                 for (const LayerRegion *region : layer->regions()) {
                     SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid);
                     for (const Surface *s : region_internal_solids) {
                         Polygons unsupported         = intersection(to_polygons(s->expolygon), unsupported_area);
+                        // The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported. 
+                        // These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs 
                         bool     partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON;
-                        if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) {
-                            Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing));
+                        if (!unsupported.empty() && (!partially_supported || area(unsupported) > 3 * 3 * spacing * spacing)) {
+                            Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 4 * spacing));
+                            // after we extracted the part worth briding, we go over the leftovers and merge the tiny ones back, to not brake the surface too much
                             for (const Polygon& p : diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))) {
-                                auto area = p.area();
+                                double area = p.area();
                                 if (area < spacing * scale_(12.0) && area > spacing * spacing) {
                                     worth_bridging.push_back(p);
                                 }
                             }
-                            worth_bridging = intersection(closing(worth_bridging, 2 * spacing), s->expolygon);
+                            worth_bridging = intersection(closing(worth_bridging, SCALED_EPSILON), s->expolygon);
                             candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0, contains_only_lightning));
 
 #ifdef DEBUG_BRIDGE_OVER_INFILL
@@ -1663,7 +1670,7 @@ void PrintObject::bridge_over_infill()
                                        to_lines(unsupported_area));
 #endif
 #ifdef DEBUG_BRIDGE_OVER_INFILL
-                            debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(s->expolygon)),
+                            debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(unsupported)),
                                        to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))), 
                                        to_lines(diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))),
                                        to_lines(unsupported_area));
@@ -1728,15 +1735,15 @@ void PrintObject::bridge_over_infill()
             layer_area_covered_by_candidates[pair.first] = {};
         }
 
+        // prepare inflated filter for each candidate on each layer. layers will be put into single thread cluster if they are close to each other (z-axis-wise)
+        // and if the inflated AABB polygons overlap somewhere
         tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer,
                                                                                          &layer_area_covered_by_candidates](
                                                                                             tbb::blocked_range<size_t> r) {
             for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) {
                 size_t lidx = layers_with_candidates[job_idx];
                 for (const auto &candidate : surfaces_by_layer.at(lidx)) {
-                    Polygon candiate_inflated_aabb = get_extents(candidate.new_polys)
-                                                         .inflated(candidate.region->flow(frSolidInfill, true).scaled_spacing() * 5)
-                                                         .polygon();
+                    Polygon candiate_inflated_aabb = get_extents(candidate.new_polys).inflated(scale_(7)).polygon();
                     layer_area_covered_by_candidates.at(lidx) = union_(layer_area_covered_by_candidates.at(lidx),
                                                                        Polygons{candiate_inflated_aabb});
                 }
@@ -2088,13 +2095,13 @@ void PrintObject::bridge_over_infill()
                     }
                 }
 
-                deep_infill_area = expand(deep_infill_area, spacing);
+                deep_infill_area = expand(deep_infill_area, spacing * 1.5);
 
                 // Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors
                 Polygons expansion_area;
                 Polygons total_fill_area;
                 for (const LayerRegion *region : layer->regions()) {
-                    Polygons internal_polys = to_polygons(region->fill_surfaces().filter_by_type(stInternal));
+                    Polygons internal_polys = to_polygons(region->fill_surfaces().filter_by_types({stInternal, stInternalSolid}));
                     expansion_area.insert(expansion_area.end(), internal_polys.begin(), internal_polys.end());
                     Polygons fill_polys = to_polygons(region->fill_expolygons());
                     total_fill_area.insert(total_fill_area.end(), fill_polys.begin(), fill_polys.end());