diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 28b9c6198..cc1e7ea79 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -711,61 +711,91 @@ sub clip_fill_surfaces { # We only want infill under ceilings; this is almost like an # internal support material. - my $additional_margin = scale 3*0; - - my $overhangs = []; # arrayref of polygons - for my $layer_id (reverse 0..($self->layer_count - 1)) { - my $layer = $self->get_layer($layer_id); - my @layer_internal = (); # arrayref of Surface objects - my @new_internal = (); # arrayref of Surface objects + # proceed top-down skipping bottom layer + my $upper_internal = []; + for my $layer_id (reverse 1..($self->layer_count - 1)) { + my $layer = $self->get_layer($layer_id); + my $lower_layer = $self->get_layer($layer_id-1); - # clip this layer's internal surfaces to @overhangs - foreach my $layerm (@{$layer->regions}) { + # detect things that we need to support + my $overhangs = []; # Polygons + + # we need to support any solid surface + push @$overhangs, map $_->p, + grep $_->is_solid, map @{$_->fill_surfaces}, @{$layer->regions}; + + # we also need to support perimeters when there's at least one full + # unsupported loop + { + # get perimeters area as the difference between slices and fill_surfaces + my $perimeters = diff( + [ map @$_, @{$layer->slices} ], + [ map $_->p, map @{$_->fill_surfaces}, @{$layer->regions} ], + ); + + # only consider the area that is not supported by lower perimeters + $perimeters = intersection( + $perimeters, + [ map $_->p, map @{$_->fill_surfaces}, @{$lower_layer->regions} ], + 1, + ); + + # only consider perimeter areas that are at least one extrusion width thick + my $pw = min(map $_->flow(FLOW_ROLE_PERIMETER)->scaled_width, @{$layer->regions}); + $perimeters = offset2($perimeters, -$pw, +$pw); + + # append such thick perimeters to the areas that need support + push @$overhangs, @$perimeters; + } + + # find new internal infill + $upper_internal = my $new_internal = intersection( + [ + @$overhangs, + @$upper_internal, + ], + [ + # our current internal fill boundaries + map $_->p, + grep $_->surface_type == S_TYPE_INTERNAL || $_->surface_type == S_TYPE_INTERNALVOID, + map @{$_->fill_surfaces}, @{$lower_layer->regions} + ], + ); + + # apply new internal infill to regions + foreach my $layerm (@{$lower_layer->regions}) { my (@internal, @other) = (); foreach my $surface (map $_->clone, @{$layerm->fill_surfaces}) { - if ($surface->surface_type == S_TYPE_INTERNAL) { + if ($surface->surface_type == S_TYPE_INTERNAL || $surface->surface_type == S_TYPE_INTERNALVOID) { push @internal, $surface; } else { push @other, $surface; } } - # keep all the original internal surfaces to detect overhangs in this layer - push @layer_internal, @internal; - - push @new_internal, my @new = map Slic3r::Surface->new( + my @new = map Slic3r::Surface->new( expolygon => $_, surface_type => S_TYPE_INTERNAL, ), @{intersection_ex( [ map $_->p, @internal ], - $overhangs, + $new_internal, + 1, )}; - push @new, map Slic3r::Surface->new( + push @other, map Slic3r::Surface->new( expolygon => $_, surface_type => S_TYPE_INTERNALVOID, ), @{diff_ex( [ map $_->p, @internal ], - $overhangs, + $new_internal, + 1, )}; - + $layerm->fill_surfaces->clear; $layerm->fill_surfaces->append($_) for (@new, @other); } - - # get this layer's overhangs defined as the full slice minus the internal infill - # (thus we also consider perimeters) - if ($layer_id > 0) { - my $solid = diff( - [ map $_->p, map @{$_->fill_surfaces}, @{$layer->regions} ], - [ map $_->p, @layer_internal ], - ); - $overhangs = offset($solid, +$additional_margin); - - push @$overhangs, map $_->p, @new_internal; # propagate upper overhangs - } } } diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index e988566a8..d09ea19c7 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -61,13 +61,18 @@ LayerRegion::prepare_fill_surfaces() the only meaningful information returned by psPerimeters. */ // if no solid layers are requested, turn top/bottom surfaces to internal - if (this->_region->config.top_solid_layers == 0) { + if (this->region()->config.top_solid_layers == 0) { for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { - if (surface->surface_type == stTop) - surface->surface_type = stInternal; + if (surface->surface_type == stTop) { + if (this->layer()->object()->config.infill_only_where_needed) { + surface->surface_type = stInternalVoid; + } else { + surface->surface_type = stInternal; + } + } } } - if (this->_region->config.bottom_solid_layers == 0) { + if (this->region()->config.bottom_solid_layers == 0) { for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { if (surface->surface_type == stBottom || surface->surface_type == stBottomBridge) surface->surface_type = stInternal; @@ -75,9 +80,9 @@ LayerRegion::prepare_fill_surfaces() } // turn too small internal regions into solid regions according to the user setting - if (this->_region->config.fill_density.value > 0) { + if (this->region()->config.fill_density.value > 0) { // scaling an area requires two calls! - double min_area = scale_(scale_(this->_region->config.solid_infill_below_area.value)); + double min_area = scale_(scale_(this->region()->config.solid_infill_below_area.value)); for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { if (surface->surface_type == stInternal && surface->area() <= min_area) surface->surface_type = stInternalSolid;