Better and more robust implementation of infill_only_where_needed

This commit is contained in:
Alessandro Ranellucci 2015-02-23 00:44:51 +01:00
parent 69ea88473d
commit 2655f3f816
2 changed files with 71 additions and 36 deletions

View file

@ -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
}
}
}

View file

@ -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;