Cleaner logic for perimeters, thin walls and gaps. More correct results and faster processing

This commit is contained in:
Alessandro Ranellucci 2013-11-18 17:06:08 +01:00
parent d4d8045905
commit abe56f96da

View file

@ -145,8 +145,9 @@ sub _merge_loops {
sub make_perimeters { sub make_perimeters {
my $self = shift; my $self = shift;
my $perimeter_spacing = $self->perimeter_flow->scaled_spacing; my $pwidth = $self->perimeter_flow->scaled_width;
my $infill_spacing = $self->solid_infill_flow->scaled_spacing; my $pspacing = $self->perimeter_flow->scaled_spacing;
my $ispacing = $self->solid_infill_flow->scaled_spacing;
my $gap_area_threshold = $self->perimeter_flow->scaled_width ** 2; my $gap_area_threshold = $self->perimeter_flow->scaled_width ** 2;
$self->perimeters->clear; $self->perimeters->clear;
@ -155,7 +156,8 @@ sub make_perimeters {
my @contours = (); # array of Polygons with ccw orientation my @contours = (); # array of Polygons with ccw orientation
my @holes = (); # array of Polygons with cw orientation my @holes = (); # array of Polygons with cw orientation
my @gaps = (); # array of Polygons my @thin_walls = (); # array of ExPolygons
my @gaps = (); # array of ExPolygons
# we need to process each island separately because we might have different # we need to process each island separately because we might have different
# extra perimeters for each one # extra perimeters for each one
@ -163,44 +165,51 @@ sub make_perimeters {
# detect how many perimeters must be generated for this island # detect how many perimeters must be generated for this island
my $loop_number = $self->config->perimeters + ($surface->extra_perimeters || 0); my $loop_number = $self->config->perimeters + ($surface->extra_perimeters || 0);
# generate loops
# (one more than necessary so that we can detect gaps even after the desired
# number of perimeters has been generated)
my @last = @{$surface->expolygon}; my @last = @{$surface->expolygon};
my @this_gaps = (); my @last_gaps = ();
for my $i (0 .. $loop_number) { for my $i (1 .. $loop_number) { # outer loop is 1
# external loop only needs half inset distance my @offsets = ();
my $spacing = ($i == 0) if ($i == 1) {
? $perimeter_spacing / 2 # the minimum thickness of a single loop is:
: $perimeter_spacing; # width/2 + spacing/2 + spacing/2 + width/2
@offsets = @{offset2(\@last, -(0.5*$pwidth + 0.5*$pspacing - 1), +(0.5*$pspacing - 1))};
my @offsets = @{offset2_ex(\@last, -1.5*$spacing, +0.5*$spacing)};
# clone polygons because these ExPolygons will go out of scope very soon # look for thin walls
my @contours_offsets = map $_->contour->clone, @offsets; if ($self->config->thin_walls) {
my @holes_offsets = map $_->clone, map @{$_->holes}, @offsets; my $diff = diff_ex(
@offsets = map $_->clone, (@contours_offsets, @holes_offsets); # turn @offsets from ExPolygons to Polygons \@last,
offset(\@offsets, +0.5*$pwidth),
# where offset2() collapses the expolygon, then there's no room for an inner loop );
# and we can extract the gap for later processing push @thin_walls, grep abs($_->area) >= $gap_area_threshold, @$diff;
if ($Slic3r::Config->gap_fill_speed > 0 && $self->config->fill_density > 0) { }
my $diff = diff( } else {
offset(\@last, -0.5*$spacing), @offsets = @{offset2(\@last, -(1.5*$pspacing - 1), +(0.5*$pspacing - 1))};
# +2 on the offset here makes sure that Clipper float truncation
# won't shrink the clip polygon to be smaller than intended. # look for gaps
offset(\@offsets, +0.5*$spacing + 2), if ($Slic3r::Config->gap_fill_speed > 0 && $self->config->fill_density > 0) {
); my $diff = diff_ex(
push @gaps, (@this_gaps = grep abs($_->area) >= $gap_area_threshold, @$diff); offset(\@last, -0.5*$pspacing),
offset(\@offsets, +0.5*$pspacing),
);
push @gaps, @last_gaps = grep abs($_->area) >= $gap_area_threshold, @$diff;
}
} }
last if !@offsets || $i == $loop_number; last if !@offsets;
push @contours, @contours_offsets; # clone polygons because these ExPolygons will go out of scope very soon
push @holes, @holes_offsets;
@last = @offsets; @last = @offsets;
foreach my $polygon (@offsets) {
if ($polygon->is_counter_clockwise) {
push @contours, $polygon;
} else {
push @holes, $polygon;
}
}
} }
# make sure we don't infill narrow parts that are already gap-filled # make sure we don't infill narrow parts that are already gap-filled
# (we only consider this surface's gaps to reduce the diff() complexity) # (we only consider this surface's gaps to reduce the diff() complexity)
@last = @{diff(\@last, \@this_gaps)}; @last = @{diff(\@last, \@last_gaps)};
# create one more offset to be used as boundary for fill # create one more offset to be used as boundary for fill
# we offset by half the perimeter spacing (to get to the actual infill boundary) # we offset by half the perimeter spacing (to get to the actual infill boundary)
@ -209,8 +218,8 @@ sub make_perimeters {
$self->fill_surfaces->append( $self->fill_surfaces->append(
@{offset2_ex( @{offset2_ex(
[ map $_->simplify_as_polygons(&Slic3r::SCALED_RESOLUTION), @{union_ex(\@last)} ], [ map $_->simplify_as_polygons(&Slic3r::SCALED_RESOLUTION), @{union_ex(\@last)} ],
-($perimeter_spacing/2 + $infill_spacing/2), -($pspacing/2 + $ispacing/2),
+$infill_spacing/2, +$ispacing/2,
)} )}
); );
} }
@ -280,44 +289,33 @@ sub make_perimeters {
# detect thin walls by offsetting slices by half extrusion inwards # detect thin walls by offsetting slices by half extrusion inwards
# and add them as perimeters # and add them as perimeters
if ($self->config->thin_walls) { if (@thin_walls) {
# we use spacing here because there could be a case where my @p = map $_->medial_axis($pspacing), @thin_walls;
# the slice collapses with width but doesn't collapse with spacing, my @paths = ();
# thus causing both perimeters and medial axis to be generated for my $p (@p) {
my $width = $self->perimeter_flow->scaled_spacing; my %params = (
my $diff = diff_ex( role => EXTR_ROLE_EXTERNAL_PERIMETER,
[ map $_->p, @{$self->slices} ], flow_spacing => $self->perimeter_flow->spacing,
offset2([ map $_->p, @{$self->slices} ], -$width*0.5, +$width*0.5),
1,
);
my $area_threshold = $width ** 2;
if (@$diff = grep { $_->area > $area_threshold } @$diff) {
my @p = map $_->medial_axis($width), @$diff;
my @paths = ();
for my $p (@p) {
my %params = (
role => EXTR_ROLE_EXTERNAL_PERIMETER,
flow_spacing => $self->perimeter_flow->spacing,
);
push @paths, $p->isa('Slic3r::Polygon')
? Slic3r::ExtrusionLoop->new(polygon => $p, %params)
: Slic3r::ExtrusionPath->new(polyline => $p, %params);
}
$self->perimeters->append(
map $_->clone, @{Slic3r::ExtrusionPath::Collection->new(@paths)->chained_path(0)}
); );
Slic3r::debugf " %d thin walls detected\n", scalar(@paths) if $Slic3r::debug; push @paths, $p->isa('Slic3r::Polygon')
? Slic3r::ExtrusionLoop->new(polygon => $p, %params)
# in the mean time we subtract thin walls from the detected gaps so that we don't : Slic3r::ExtrusionPath->new(polyline => $p, %params);
# reprocess them, causing overlapping thin walls and zigzag.
@gaps = @{diff(
\@gaps,
[ map $_->grow($self->perimeter_flow->scaled_width), @p ],
1,
)};
} }
$self->perimeters->append(
map $_->clone, @{Slic3r::ExtrusionPath::Collection->new(@paths)->chained_path(0)}
);
Slic3r::debugf " %d thin walls detected\n", scalar(@paths) if $Slic3r::debug;
# in the mean time we subtract thin walls from the detected gaps so that we don't
# reprocess them, causing overlapping thin walls and zigzag.
# Note: this is probably not necessary anymore since we're detecting thin walls
# and gaps at the same time
@gaps = @{diff_ex(
[ map @$_, @gaps ],
[ map $_->grow($self->perimeter_flow->scaled_width), @p ],
1,
)};
} }
$self->_fill_gaps(\@gaps); $self->_fill_gaps(\@gaps);
@ -329,9 +327,6 @@ sub _fill_gaps {
return unless @$gaps; return unless @$gaps;
# turn gaps into ExPolygons
$gaps = union_ex($gaps);
my $filler = $self->layer->object->fill_maker->filler('rectilinear'); my $filler = $self->layer->object->fill_maker->filler('rectilinear');
$filler->layer_id($self->layer->id); $filler->layer_id($self->layer->id);