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 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 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(0, m_layers.size() - 1), + [this, &surfaces_covered, &layer_expansions_and_voids, unsupported_width](const tbb::blocked_range& 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(0, m_layers.size()), - [this, region_id](const tbb::blocked_range& range) { + [this, &surfaces_covered, region_id](const tbb::blocked_range& 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 Surfaces;