From 48ecbe777f56583723ca8effad7ad01130408599 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Fri, 6 Sep 2019 15:03:49 +0200
Subject: [PATCH] Fix of "excess infill below bridges with 0% infill" #442 Fix
 of "Bridging infill not connecting with infill set to 0%" #1301

Top, bottom and bridging areas are extended into infill as long
as the infill is not zero. If the infill is zero,
top, bottom and bridging areas wound not expand into these "void" areas.

With this commit, the top, bottom and bridging areas are allowed to expand
into the "void" areas as long as these "void" areas are supported below
with perimeters or some other non-empty infill, and slightly beyond
these supporting areas into the voids (currently hard coded to 1mm).
---
 src/libslic3r/Layer.hpp       |  2 +-
 src/libslic3r/LayerRegion.cpp | 55 ++++++++++++++++++-------------
 src/libslic3r/PrintObject.cpp | 61 +++++++++++++++++++++++++++++++++--
 src/libslic3r/Surface.cpp     | 58 ---------------------------------
 src/libslic3r/Surface.hpp     | 22 +++++++------
 5 files changed, 104 insertions(+), 94 deletions(-)

diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp
index 6297b49d0..555017207 100644
--- a/src/libslic3r/Layer.hpp
+++ b/src/libslic3r/Layer.hpp
@@ -62,7 +62,7 @@ public:
     void    slices_to_fill_surfaces_clipped();
     void    prepare_fill_surfaces();
     void    make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
-    void    process_external_surfaces(const Layer* lower_layer);
+    void    process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered);
     double  infill_area_threshold() const;
     // Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer.
     void    trim_surfaces(const Polygons &trimming_polygons);
diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp
index caf8dc20f..3fc8d03d1 100644
--- a/src/libslic3r/LayerRegion.cpp
+++ b/src/libslic3r/LayerRegion.cpp
@@ -86,11 +86,12 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
 //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
 #define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
 
-void LayerRegion::process_external_surfaces(const Layer* lower_layer)
+void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered)
 {
-    const Surfaces &surfaces = this->fill_surfaces.surfaces;
-    const double margin = scale_(EXTERNAL_INFILL_MARGIN);
-    
+    const Surfaces &surfaces   = this->fill_surfaces.surfaces;
+    const bool      has_infill = this->region()->config().fill_density.value > 0.;
+    const float		margin 	   = float(scale_(EXTERNAL_INFILL_MARGIN));
+
 #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
     export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-initial");
 #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
@@ -106,36 +107,44 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
     // Internal surfaces, not grown.
     Surfaces                    internal;
     // Areas, where an infill of various types (top, bottom, bottom bride, sparse, void) could be placed.
-    //FIXME if non zero infill, then fill_boundaries could be cheaply initialized from layerm->fill_expolygons.
-    Polygons                    fill_boundaries;
+    Polygons                    fill_boundaries = to_polygons(this->fill_expolygons);
+    Polygons  					lower_layer_covered_tmp;
 
     // Collect top surfaces and internal surfaces.
     // Collect fill_boundaries: If we're slicing with no infill, we can't extend external surfaces over non-existent infill.
     // This loop destroys the surfaces (aliasing this->fill_surfaces.surfaces) by moving into top/internal/fill_boundaries!
+
     {
-        // bottom_polygons are used to trim inflated top surfaces.
-        fill_boundaries.reserve(number_polygons(surfaces));
-        bool has_infill = this->region()->config().fill_density.value > 0.;
+        // Voids are sparse infills if infill rate is zero.
+        Polygons voids;
         for (const Surface &surface : this->fill_surfaces.surfaces) {
             if (surface.surface_type == stTop) {
                 // Collect the top surfaces, inflate them and trim them by the bottom surfaces.
                 // This gives the priority to bottom surfaces.
-                surfaces_append(top, offset_ex(surface.expolygon, float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
-            } else if (surface.surface_type == stBottom || (surface.surface_type == stBottomBridge && lower_layer == NULL)) {
+                surfaces_append(top, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
+            } else if (surface.surface_type == stBottom || (surface.surface_type == stBottomBridge && lower_layer == nullptr)) {
                 // Grown by 3mm.
-                surfaces_append(bottom, offset_ex(surface.expolygon, float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
+                surfaces_append(bottom, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
             } else if (surface.surface_type == stBottomBridge) {
                 if (! surface.empty())
-                    bridges.push_back(surface);
+                    bridges.emplace_back(surface);
             }
-            bool internal_surface = surface.surface_type != stTop && ! surface.is_bottom();
-            if (has_infill || surface.surface_type != stInternal) {
-                if (internal_surface)
-                    // Make a copy as the following line uses the move semantics.
-                    internal.push_back(surface);
-                polygons_append(fill_boundaries, std::move(surface.expolygon));
-            } else if (internal_surface)
-                internal.push_back(std::move(surface));
+            if (surface.is_internal()) {
+            	assert(surface.surface_type == stInternal);
+            	if (! has_infill && lower_layer != nullptr)
+            		polygons_append(voids, surface.expolygon);
+            	internal.emplace_back(std::move(surface));
+            }
+        }
+        if (! has_infill && lower_layer != nullptr && ! voids.empty()) {
+        	// Remove voids from fill_boundaries, that are not supported by the layer below.
+            if (lower_layer_covered == nullptr) {
+            	lower_layer_covered = &lower_layer_covered_tmp;
+            	lower_layer_covered_tmp = to_polygons(lower_layer->slices.expolygons);
+            }
+            if (! lower_layer_covered->empty())
+            	voids = diff(voids, *lower_layer_covered);
+        	fill_boundaries = diff(fill_boundaries, voids);
         }
     }
 
@@ -184,9 +193,9 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
                         break;
                     }
                 // Grown by 3mm.
-                Polygons polys = offset(to_polygons(bridges[i].expolygon), float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS);
+                Polygons polys = offset(to_polygons(bridges[i].expolygon), margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
                 if (idx_island == -1) {
-                    printf("Bridge did not fall into the source region!\r\n");
+				    BOOST_LOG_TRIVIAL(trace) << "Bridge did not fall into the source region!";
                 } else {
                     // Found an island, to which this bridge region belongs. Trim it,
                     polys = intersection(polys, to_polygons(fill_boundaries_ex[idx_island]));
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 75145145d..29e8e2d42 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -810,15 +810,72 @@ void PrintObject::process_external_surfaces()
 {
     BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..." << log_memory_info();
 
+    // Cached surfaces covered by some extrusion, defining regions, over which the from the surfaces one layer higher are allowed to expand.
+    std::vector<Polygons> surfaces_covered;
+    // Is there any printing region, that has zero infill? If so, then we don't want the expansion to be performed over the complete voids, but only
+    // over voids, which are supported by the layer below.
+    bool 				  has_voids = false;
+	for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
+		if (! this->region_volumes.empty() && this->print()->regions()[region_id]->config().fill_density == 0) {
+			has_voids = true;
+			break;
+		}
+	if (has_voids && m_layers.size() > 1) {
+	    // All but stInternal fill surfaces will get expanded and possibly trimmed.
+	    std::vector<unsigned char> layer_expansions_and_voids(m_layers.size(), false);
+	    for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) {
+	    	const Layer *layer = m_layers[layer_idx];
+	    	bool expansions = false;
+	    	bool voids      = false;
+	    	for (const LayerRegion *layerm : layer->regions()) {
+	    		for (const Surface &surface : layerm->fill_surfaces.surfaces) {
+	    			if (surface.surface_type == stInternal)
+	    				voids = true;
+	    			else
+	    				expansions = true;
+	    			if (voids && expansions) {
+	    				layer_expansions_and_voids[layer_idx] = true;
+	    				goto end;
+	    			}
+	    		}
+	    	}
+		end:;
+		}
+	    BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - start";
+	    surfaces_covered.resize(m_layers.size() - 1, Polygons());
+    	auto unsupported_width = - float(scale_(0.3 * EXTERNAL_INFILL_MARGIN));
+	    tbb::parallel_for(
+	        tbb::blocked_range<size_t>(0, m_layers.size() - 1),
+	        [this, &surfaces_covered, &layer_expansions_and_voids, unsupported_width](const tbb::blocked_range<size_t>& range) {
+	            for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
+	            	if (layer_expansions_and_voids[layer_idx + 1]) {
+		                m_print->throw_if_canceled();
+		                Polygons voids;
+		                for (const LayerRegion *layerm : m_layers[layer_idx]->regions()) {
+		                	if (layerm->region()->config().fill_density.value == 0.)
+		                		for (const Surface &surface : layerm->fill_surfaces.surfaces)
+		                			// Shrink the holes, let the layer above expand slightly inside the unsupported areas.
+		                			polygons_append(voids, offset(surface.expolygon, unsupported_width));
+		                }
+		                surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices.expolygons), voids);
+	            	}
+	        }
+	    );
+	    m_print->throw_if_canceled();
+	    BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - end";
+	}
+
 	for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) {
         BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start";
         tbb::parallel_for(
             tbb::blocked_range<size_t>(0, m_layers.size()),
-            [this, region_id](const tbb::blocked_range<size_t>& range) {
+            [this, &surfaces_covered, region_id](const tbb::blocked_range<size_t>& range) {
                 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
                     m_print->throw_if_canceled();
                     // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z;
-                    m_layers[layer_idx]->get_region((int)region_id)->process_external_surfaces((layer_idx == 0) ? NULL : m_layers[layer_idx - 1]);
+                    m_layers[layer_idx]->get_region((int)region_id)->process_external_surfaces(
+                    	(layer_idx == 0) ? nullptr : m_layers[layer_idx - 1],
+                    	(layer_idx == 0 || surfaces_covered.empty() || surfaces_covered[layer_idx - 1].empty()) ? nullptr : &surfaces_covered[layer_idx - 1]);
                 }
             }
         );
diff --git a/src/libslic3r/Surface.cpp b/src/libslic3r/Surface.cpp
index acc19e631..58ac7294c 100644
--- a/src/libslic3r/Surface.cpp
+++ b/src/libslic3r/Surface.cpp
@@ -4,64 +4,6 @@
 
 namespace Slic3r {
 
-Surface::operator Polygons() const
-{
-    return this->expolygon;
-}
-
-double
-Surface::area() const
-{
-    return this->expolygon.area();
-}
-
-bool
-Surface::is_solid() const
-{
-    return this->surface_type == stTop
-        || this->surface_type == stBottom
-        || this->surface_type == stBottomBridge
-        || this->surface_type == stInternalSolid
-        || this->surface_type == stInternalBridge;
-}
-
-bool
-Surface::is_external() const
-{
-    return this->surface_type == stTop
-        || this->surface_type == stBottom
-        || this->surface_type == stBottomBridge;
-}
-
-bool
-Surface::is_internal() const
-{
-    return this->surface_type == stInternal
-        || this->surface_type == stInternalBridge
-        || this->surface_type == stInternalSolid
-        || this->surface_type == stInternalVoid;
-}
-
-bool
-Surface::is_top() const
-{
-    return this->surface_type == stTop;
-}
-
-bool
-Surface::is_bottom() const
-{
-    return this->surface_type == stBottom
-        || this->surface_type == stBottomBridge;
-}
-
-bool
-Surface::is_bridge() const
-{
-    return this->surface_type == stBottomBridge
-        || this->surface_type == stInternalBridge;
-}
-
 BoundingBox get_extents(const Surface &surface)
 {
     return get_extents(surface.expolygon.contour);
diff --git a/src/libslic3r/Surface.hpp b/src/libslic3r/Surface.hpp
index e01b6a938..fbebe5610 100644
--- a/src/libslic3r/Surface.hpp
+++ b/src/libslic3r/Surface.hpp
@@ -90,16 +90,18 @@ public:
         return *this;
     }
 
-    operator Polygons() const;
-    double area() const;
-    bool empty() const { return expolygon.empty(); }
-    void clear() { expolygon.clear(); }
-    bool is_solid() const;
-    bool is_external() const;
-    bool is_internal() const;
-    bool is_top() const;
-    bool is_bottom() const;
-    bool is_bridge() const;
+	operator Polygons()  const { return this->expolygon; }
+	double area() 		 const { return this->expolygon.area(); }
+    bool   empty() 		 const { return expolygon.empty(); }
+    void   clear() 			   { expolygon.clear(); }
+
+    // The following methods do not test for stPerimeter.
+	bool   is_top()      const { return this->surface_type == stTop; }
+	bool   is_bottom()   const { return this->surface_type == stBottom || this->surface_type == stBottomBridge; }
+	bool   is_bridge()   const { return this->surface_type == stBottomBridge || this->surface_type == stInternalBridge; }
+	bool   is_external() const { return this->is_top() || this->is_bottom(); }
+	bool   is_internal() const { return ! this->is_external(); }
+	bool   is_solid()    const { return this->is_external() || this->surface_type == stInternalSolid || this->surface_type == stInternalBridge; }
 };
 
 typedef std::vector<Surface> Surfaces;