Merge branch 'master' into grow-narrow

This commit is contained in:
Alessandro Ranellucci 2013-03-16 20:09:11 +01:00
commit 38b79c6bf7
18 changed files with 152 additions and 66 deletions

View file

@ -197,6 +197,7 @@ The author of the Silk icon set is Mark James.
--extra-perimeters Add more perimeters when needed (default: yes) --extra-perimeters Add more perimeters when needed (default: yes)
--randomize-start Randomize starting point across layers (default: yes) --randomize-start Randomize starting point across layers (default: yes)
--avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no) --avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no)
--external-perimeters-first Reverse perimeter order. (default: no)
--only-retract-when-crossing-perimeters --only-retract-when-crossing-perimeters
Disable retraction when travelling between infill paths inside the same island. Disable retraction when travelling between infill paths inside the same island.
(default: yes) (default: yes)
@ -288,6 +289,7 @@ The author of the Silk icon set is Mark James.
Miscellaneous options: Miscellaneous options:
--notes Notes to be added as comments to the output file --notes Notes to be added as comments to the output file
--resolution Minimum detail resolution (mm, set zero for full resolution, default: 0)
Flow options (advanced): Flow options (advanced):
--extrusion-width Set extrusion width manually; it accepts either an absolute value in mm --extrusion-width Set extrusion width manually; it accepts either an absolute value in mm

View file

@ -37,6 +37,15 @@ our $Options = {
default => $Slic3r::have_threads ? 2 : 1, default => $Slic3r::have_threads ? 2 : 1,
readonly => !$Slic3r::have_threads, readonly => !$Slic3r::have_threads,
}, },
'resolution' => {
label => 'Resolution',
tooltip => 'Minimum detail resolution, used to simplify the input file for speeding up the slicing job and reducing memory usage. High-resolution models often carry more detail than printers can render. Set to zero to disable any simplification and use full resolution from input.',
sidetext => 'mm',
cli => 'resolution=f',
type => 'f',
min => 0,
default => 0,
},
# output options # output options
'output_filename_format' => { 'output_filename_format' => {
@ -562,6 +571,13 @@ our $Options = {
type => 'bool', type => 'bool',
default => 0, default => 0,
}, },
'external_perimeters_first' => {
label => 'External perimeters first',
tooltip => 'Print contour perimeters from the outermost one to the innermost one instead of the default inverse order.',
cli => 'external-perimeters-first!',
type => 'bool',
default => 0,
},
'only_retract_when_crossing_perimeters' => { 'only_retract_when_crossing_perimeters' => {
label => 'Only retract when crossing perimeters', label => 'Only retract when crossing perimeters',
tooltip => 'Disables retraction when travelling between infill paths inside the same island.', tooltip => 'Disables retraction when travelling between infill paths inside the same island.',

View file

@ -5,6 +5,7 @@ use warnings;
# an ExPolygon is a polygon with holes # an ExPolygon is a polygon with holes
use Boost::Geometry::Utils; use Boost::Geometry::Utils;
use List::Util qw(first);
use Math::Geometry::Voronoi; use Math::Geometry::Voronoi;
use Slic3r::Geometry qw(X Y A B point_in_polygon same_line line_length epsilon); use Slic3r::Geometry qw(X Y A B point_in_polygon same_line line_length epsilon);
use Slic3r::Geometry::Clipper qw(union_ex JT_MITER); use Slic3r::Geometry::Clipper qw(union_ex JT_MITER);
@ -54,6 +55,13 @@ sub clipper_expolygon {
}; };
} }
sub is_valid {
my $self = shift;
return (!first { !$_->is_valid } @$self)
&& $self->contour->is_counter_clockwise
&& (!first { $_->is_counter_clockwise } $self->holes);
}
sub boost_polygon { sub boost_polygon {
my $self = shift; my $self = shift;
return Boost::Geometry::Utils::polygon(@$self); return Boost::Geometry::Utils::polygon(@$self);
@ -164,8 +172,13 @@ sub clip_line {
sub simplify { sub simplify {
my $self = shift; my $self = shift;
$_->simplify(@_) for @$self; my ($tolerance) = @_;
$self;
# it would be nice to have a multilinestring_simplify method in B::G::U
my @simplified = Slic3r::Geometry::Clipper::simplify_polygons(
[ map Boost::Geometry::Utils::linestring_simplify($_, $tolerance), @$self ],
);
return @{ Slic3r::Geometry::Clipper::union_ex([ @simplified ]) };
} }
sub scale { sub scale {

View file

@ -81,6 +81,11 @@ sub clip_with_expolygon {
return @paths; return @paths;
} }
sub simplify {
my $self = shift;
$self->polyline($self->polyline->simplify(@_));
}
sub points { sub points {
my $self = shift; my $self = shift;
return $self->polyline; return $self->polyline;

View file

@ -56,22 +56,20 @@ sub BUILD {
} }
}; };
# simplify islands
@{$self->islands} = map $_->simplify($self->_inner_margin), @{$self->islands};
# process individual islands # process individual islands
for my $i (0 .. $#{$self->islands}) { for my $i (0 .. $#{$self->islands}) {
# simplify the island's contours
$self->islands->[$i]->simplify($self->_inner_margin);
# offset the island inwards to make the boundaries for internal movements # offset the island inwards to make the boundaries for internal movements
# so that no motion along external perimeters happens # so that no motion along external perimeters happens
$self->_inner->[$i] = [ $self->islands->[$i]->offset_ex(-$self->_inner_margin) ] $self->_inner->[$i] = $self->no_internal
if !$self->no_internal; ? []
: [ $self->islands->[$i]->offset_ex(-$self->_inner_margin) ];
# offset the island outwards to make the boundaries for external movements # offset the island outwards to make the boundaries for external movements
$self->_outer->[$i] = [ $self->islands->[$i]->contour->offset($self->_outer_margin) ]; $self->_outer->[$i] = [ $self->islands->[$i]->contour->offset($self->_outer_margin) ];
# further simplification (isn't this a duplication of the one above?)
$_->simplify($self->_inner_margin) for @{$self->_inner->[$i]}, @{$self->_outer->[$i]};
# if internal motion is enabled, build a set of utility expolygons representing # if internal motion is enabled, build a set of utility expolygons representing
# the outer boundaries (as contours) and the inner boundaries (as holes). whenever # the outer boundaries (as contours) and the inner boundaries (as holes). whenever
# we jump from a hole to a contour or viceversa, we know we're crossing a perimeter # we jump from a hole to a contour or viceversa, we know we're crossing a perimeter

View file

@ -1137,9 +1137,8 @@ sub make_thumbnail {
for (map @$_, map @$_, @{$thumbnail->expolygons}) { for (map @$_, map @$_, @{$thumbnail->expolygons}) {
@$_ = map $_ * $self->thumbnail_scaling_factor, @$_; @$_ = map $_ * $self->thumbnail_scaling_factor, @$_;
} }
@{$thumbnail->expolygons} = map $_->simplify(0.5), grep $_->area >= 1, @{$thumbnail->expolygons};
foreach my $expolygon (@{$thumbnail->expolygons}) { foreach my $expolygon (@{$thumbnail->expolygons}) {
@$expolygon = grep $_->area >= 1, @$expolygon;
$expolygon->simplify(0.5);
$expolygon->rotate(Slic3r::Geometry::deg2rad($self->rotate)); $expolygon->rotate(Slic3r::Geometry::deg2rad($self->rotate));
$expolygon->scale($self->scale); $expolygon->scale($self->scale);
} }

View file

@ -407,7 +407,7 @@ sub build {
}, },
{ {
title => 'Advanced', title => 'Advanced',
options => [qw(avoid_crossing_perimeters)], options => [qw(avoid_crossing_perimeters external_perimeters_first)],
}, },
]); ]);
@ -517,10 +517,10 @@ sub build {
title => 'Flow', title => 'Flow',
options => [qw(bridge_flow_ratio)], options => [qw(bridge_flow_ratio)],
}, },
$Slic3r::have_threads ? { {
title => 'Other', title => 'Other',
options => [qw(threads)], options => [($Slic3r::have_threads ? qw(threads) : ()), qw(resolution)],
} : (), },
]); ]);
} }

View file

@ -12,7 +12,7 @@ our @EXPORT_OK = qw(
point_is_on_left_of_segment polyline_lines polygon_lines nearest_point point_is_on_left_of_segment polyline_lines polygon_lines nearest_point
point_along_segment polygon_segment_having_point polygon_has_subsegment point_along_segment polygon_segment_having_point polygon_has_subsegment
polygon_has_vertex polyline_length can_connect_points deg2rad rad2deg polygon_has_vertex polyline_length can_connect_points deg2rad rad2deg
rotate_points move_points remove_coinciding_points clip_segment_polygon rotate_points move_points clip_segment_polygon
sum_vectors multiply_vector subtract_vectors dot perp polygon_points_visibility sum_vectors multiply_vector subtract_vectors dot perp polygon_points_visibility
line_intersection bounding_box bounding_box_intersect same_point same_line line_intersection bounding_box bounding_box_intersect same_point same_line
longest_segment angle3points three_points_aligned line_direction longest_segment angle3points three_points_aligned line_direction
@ -375,15 +375,6 @@ sub move_points {
return map Slic3r::Point->new($shift->[X] + $_->[X], $shift->[Y] + $_->[Y]), @points; return map Slic3r::Point->new($shift->[X] + $_->[X], $shift->[Y] + $_->[Y]), @points;
} }
# preserves order
sub remove_coinciding_points {
my ($points) = @_;
my %p = map { sprintf('%f,%f', @$_) => "$_" } @$points;
%p = reverse %p;
@$points = grep $p{"$_"}, @$points;
}
# implementation of Liang-Barsky algorithm # implementation of Liang-Barsky algorithm
# polygon must be convex and ccw # polygon must be convex and ccw
sub clip_segment_polygon { sub clip_segment_polygon {

View file

@ -4,7 +4,7 @@ use warnings;
require Exporter; require Exporter;
our @ISA = qw(Exporter); our @ISA = qw(Exporter);
our @EXPORT_OK = qw(safety_offset offset offset_ex our @EXPORT_OK = qw(safety_offset offset offset_ex collapse_ex
diff_ex diff union_ex intersection_ex xor_ex PFT_EVENODD JT_MITER JT_ROUND diff_ex diff union_ex intersection_ex xor_ex PFT_EVENODD JT_MITER JT_ROUND
JT_SQUARE is_counter_clockwise); JT_SQUARE is_counter_clockwise);
@ -91,4 +91,23 @@ sub xor_ex {
]; ];
} }
sub collapse_ex {
my ($polygons, $width) = @_;
my @result = offset(
[ offset($polygons, -$width/2,) ],
+$width/2,
);
return union_ex([@result]);
}
sub simplify_polygon {
my ($polygon, $pft) = @_;
return @{ Math::Clipper::simplify_polygon($polygon, $pft // PFT_NONZERO) };
}
sub simplify_polygons {
my ($polygons, $pft) = @_;
return @{ Math::Clipper::simplify_polygons($polygons, $pft // PFT_NONZERO) };
}
1; 1;

View file

@ -263,14 +263,14 @@ sub make_perimeters {
# 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)
# and then we offset back and forth by the infill spacing to only consider the # and then we offset back and forth by the infill spacing to only consider the
# non-collapsing regions # non-collapsing regions
my @fill_boundaries = @{union_ex([ push @{ $self->fill_surfaces },
map $_->simplify(&Slic3r::SCALED_RESOLUTION),
@{union_ex([
Slic3r::Geometry::Clipper::offset( Slic3r::Geometry::Clipper::offset(
[Slic3r::Geometry::Clipper::offset([ map @$_, @last_offsets ], -($perimeter_spacing/2 + $infill_spacing))], [Slic3r::Geometry::Clipper::offset([ map @$_, @last_offsets ], -($perimeter_spacing/2 + $infill_spacing))],
+$infill_spacing, +$infill_spacing,
), ),
])}; ])};
$_->simplify(&Slic3r::SCALED_RESOLUTION) for @fill_boundaries;
push @{ $self->fill_surfaces }, @fill_boundaries;
} }
# fill gaps # fill gaps
@ -404,10 +404,14 @@ sub make_perimeters {
} }
} }
# do holes, then contours starting from innermost one # first do holes
$self->_add_perimeter($holes[$_], $is_external{$_} ? EXTR_ROLE_EXTERNAL_PERIMETER : undef) $self->_add_perimeter($holes[$_], $is_external{$_} ? EXTR_ROLE_EXTERNAL_PERIMETER : undef)
for reverse 0 .. $#holes; for reverse 0 .. $#holes;
for my $depth (reverse 0 .. $#$island) {
# then do contours according to the user settings
my @contour_order = 0 .. $#$island;
@contour_order = reverse @contour_order if !$Slic3r::Config->external_perimeters_first;
for my $depth (@contour_order) {
my $role = $depth == $#$island ? EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER my $role = $depth == $#$island ? EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER
: $depth == 0 ? EXTR_ROLE_EXTERNAL_PERIMETER : $depth == 0 ? EXTR_ROLE_EXTERNAL_PERIMETER
: EXTR_ROLE_PERIMETER; : EXTR_ROLE_PERIMETER;

View file

@ -97,6 +97,11 @@ sub grow {
return $self->split_at_first_point->grow(@_); return $self->split_at_first_point->grow(@_);
} }
sub simplify {
my $self = shift;
return Slic3r::Geometry::Clipper::simplify_polygon( $self->SUPER::simplify(@_) );
}
# this method subdivides the polygon segments to that no one of them # this method subdivides the polygon segments to that no one of them
# is longer than the length provided # is longer than the length provided
sub subdivide { sub subdivide {

View file

@ -68,8 +68,8 @@ sub simplify {
my $self = shift; my $self = shift;
my $tolerance = shift || 10; my $tolerance = shift || 10;
@$self = @{ Boost::Geometry::Utils::linestring_simplify($self, $tolerance) }; my $simplified = Boost::Geometry::Utils::linestring_simplify($self, $tolerance);
bless $_, 'Slic3r::Point' for @$self; return (ref $self)->new($simplified);
} }
sub reverse { sub reverse {

View file

@ -313,6 +313,16 @@ sub size {
return [ $bb[X2] - $bb[X1], $bb[Y2] - $bb[Y1] ]; return [ $bb[X2] - $bb[X1], $bb[Y2] - $bb[Y1] ];
} }
sub _simplify_slices {
my $self = shift;
my ($distance) = @_;
foreach my $layer (map @{$_->layers}, @{$self->objects}) {
@$_ = map $_->simplify($distance), @$_
for $layer->slices, (map $_->slices, @{$layer->regions});
}
}
sub export_gcode { sub export_gcode {
my $self = shift; my $self = shift;
my %params = @_; my %params = @_;
@ -324,7 +334,12 @@ sub export_gcode {
# skein the STL into layers # skein the STL into layers
# each layer has surfaces with holes # each layer has surfaces with holes
$status_cb->(10, "Processing triangulated mesh"); $status_cb->(10, "Processing triangulated mesh");
$_->slice(keep_meshes => $params{keep_meshes}) for @{$self->objects}; $_->slice for @{$self->objects};
if ($Slic3r::Config->resolution) {
$status_cb->(15, "Simplifying input");
$self->_simplify_slices(scale $Slic3r::Config->resolution);
}
# make perimeters # make perimeters
# this will add a set of extrusion loops to each layer # this will add a set of extrusion loops to each layer
@ -334,10 +349,7 @@ sub export_gcode {
# simplify slices (both layer and region slices), # simplify slices (both layer and region slices),
# we only need the max resolution for perimeters # we only need the max resolution for perimeters
foreach my $layer (map @{$_->layers}, @{$self->objects}) { $self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
$_->simplify(&Slic3r::SCALED_RESOLUTION)
for @{$layer->slices}, (map $_->expolygon, map @{$_->slices}, @{$layer->regions});
}
# this will assign a type (top/bottom/internal) to $layerm->slices # this will assign a type (top/bottom/internal) to $layerm->slices
# and transform $layerm->fill_surfaces from expolygon # and transform $layerm->fill_surfaces from expolygon
@ -477,7 +489,7 @@ sub export_svg {
# calls ->perimeter_flow # calls ->perimeter_flow
$self->init_extruders; $self->init_extruders;
$_->slice(keep_meshes => $params{keep_meshes}) for @{$self->objects}; $_->slice for @{$self->objects};
$self->arrange_objects; $self->arrange_objects;
my $output_file = $self->expanded_output_filepath($params{output_file}); my $output_file = $self->expanded_output_filepath($params{output_file});

View file

@ -4,7 +4,7 @@ use Moo;
use List::Util qw(min sum first); use List::Util qw(min sum first);
use Slic3r::ExtrusionPath ':roles'; use Slic3r::ExtrusionPath ':roles';
use Slic3r::Geometry qw(Z PI scale unscale deg2rad rad2deg scaled_epsilon); use Slic3r::Geometry qw(Z PI scale unscale deg2rad rad2deg scaled_epsilon);
use Slic3r::Geometry::Clipper qw(diff_ex intersection_ex union_ex offset); use Slic3r::Geometry::Clipper qw(diff_ex intersection_ex union_ex offset collapse_ex);
use Slic3r::Surface ':types'; use Slic3r::Surface ':types';
has 'print' => (is => 'ro', weak_ref => 1, required => 1); has 'print' => (is => 'ro', weak_ref => 1, required => 1);
@ -121,7 +121,7 @@ sub slice {
die "Invalid input file\n" if !@{$self->layers}; die "Invalid input file\n" if !@{$self->layers};
# free memory # free memory
$self->meshes(undef) unless $params{keep_meshes}; $self->meshes(undef);
# remove last layer if empty # remove last layer if empty
# (we might have created it because of the $max_layer = ... + 1 code in TriangleMesh) # (we might have created it because of the $max_layer = ... + 1 code in TriangleMesh)
@ -789,7 +789,10 @@ sub generate_support_material {
[ map @$_, @{ $upper_layers_overhangs[-1] || [] } ], [ map @$_, @{ $upper_layers_overhangs[-1] || [] } ],
[ map @$_, @current_layer_offsetted_slices ], [ map @$_, @current_layer_offsetted_slices ],
); );
$_->simplify($flow->scaled_spacing) for @{$layers_contact_areas{$i}}; $layers_contact_areas{$i} = [
map $_->simplify($flow->scaled_spacing),
@{collapse_ex([ map @$_, @{$layers_contact_areas{$i}} ], $flow->scaled_width)},
];
# to define interface regions of this layer we consider the overhangs of all the upper layers # to define interface regions of this layer we consider the overhangs of all the upper layers
# minus the first one # minus the first one
@ -800,7 +803,10 @@ sub generate_support_material {
(map @$_, @{ $layers_contact_areas{$i} }), (map @$_, @{ $layers_contact_areas{$i} }),
], ],
); );
$_->simplify($flow->scaled_spacing) for @{$layers_interfaces{$i}}; $layers_interfaces{$i} = [
map $_->simplify($flow->scaled_spacing),
@{collapse_ex([ map @$_, @{$layers_interfaces{$i}} ], $flow->scaled_width)},
];
# generate support material in current layer (for upper layers) # generate support material in current layer (for upper layers)
@current_support_regions = @{diff_ex( @current_support_regions = @{diff_ex(
@ -819,7 +825,10 @@ sub generate_support_material {
(map @$_, @{ $layers_interfaces{$i} }), (map @$_, @{ $layers_interfaces{$i} }),
], ],
); );
$_->simplify($flow->scaled_spacing) for @{$layers{$i}}; $layers{$i} = [
map $_->simplify($flow->scaled_spacing),
@{collapse_ex([ map @$_, @{$layers{$i}} ], $flow->scaled_width)},
];
# get layer overhangs and put them into queue for adding support inside lower layers; # get layer overhangs and put them into queue for adding support inside lower layers;
# we need an angle threshold for this # we need an angle threshold for this

View file

@ -82,12 +82,22 @@ sub group {
sub offset { sub offset {
my $self = shift; my $self = shift;
return map { return map $self->_inflate_expolygon($_), $self->expolygon->offset_ex(@_);
(ref $self)->new( }
expolygon => $_,
sub simplify {
my $self = shift;
return map $self->_inflate_expolygon($_), $self->expolygon->simplify(@_);
}
sub _inflate_expolygon {
my $self = shift;
my ($expolygon) = @_;
return (ref $self)->new(
expolygon => $expolygon,
map { $_ => $self->$_ } qw(surface_type depth_layers bridge_angle), map { $_ => $self->$_ } qw(surface_type depth_layers bridge_angle),
) );
} $self->expolygon->offset_ex(@_);
} }
sub p { sub p {

View file

@ -250,6 +250,7 @@ $j
--extra-perimeters Add more perimeters when needed (default: yes) --extra-perimeters Add more perimeters when needed (default: yes)
--randomize-start Randomize starting point across layers (default: yes) --randomize-start Randomize starting point across layers (default: yes)
--avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no) --avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no)
--external-perimeters-first Reverse perimeter order. (default: no)
--only-retract-when-crossing-perimeters --only-retract-when-crossing-perimeters
Disable retraction when travelling between infill paths inside the same island. Disable retraction when travelling between infill paths inside the same island.
(default: no) (default: no)
@ -341,6 +342,7 @@ $j
Miscellaneous options: Miscellaneous options:
--notes Notes to be added as comments to the output file --notes Notes to be added as comments to the output file
--resolution Minimum detail resolution (mm, set zero for full resolution, default: $config->{resolution})
Flow options (advanced): Flow options (advanced):
--extrusion-width Set extrusion width manually; it accepts either an absolute value in mm --extrusion-width Set extrusion width manually; it accepts either an absolute value in mm

View file

@ -2,7 +2,7 @@ use Test::More;
use strict; use strict;
use warnings; use warnings;
plan tests => 6; plan tests => 7;
BEGIN { BEGIN {
use FindBin; use FindBin;
@ -24,7 +24,7 @@ use Slic3r;
my $polyline = Slic3r::Polyline->new([ my $polyline = Slic3r::Polyline->new([
[0,0],[1,0],[2,0],[2,1],[2,2],[1,2],[0,2],[0,1],[0,0], [0,0],[1,0],[2,0],[2,1],[2,2],[1,2],[0,2],[0,1],[0,0],
]); ]);
$polyline->simplify(1); $polyline = $polyline->simplify(1);
is_deeply $polyline, [ [0, 0], [2, 0], [2, 2], [0, 2], [0, 0] ], 'Douglas-Peucker'; is_deeply $polyline, [ [0, 0], [2, 0], [2, 2], [0, 2], [0, 0] ], 'Douglas-Peucker';
} }
@ -33,7 +33,7 @@ use Slic3r;
[0,0],[0.5,0.5],[1,0],[1.25,-0.25],[1.5,.5], [0,0],[0.5,0.5],[1,0],[1.25,-0.25],[1.5,.5],
]); ]);
$polyline->scale(100); $polyline->scale(100);
$polyline->simplify(25); $polyline = $polyline->simplify(25);
is_deeply $polyline, [ [0, 0], [50, 50], [125, -25], [150, 50] ], 'Douglas-Peucker'; is_deeply $polyline, [ [0, 0], [50, 50], [125, -25], [150, 50] ], 'Douglas-Peucker';
} }
@ -75,9 +75,10 @@ use Slic3r;
ok @$polygon < @$gear, 'gear was simplified using merge_continuous_lines'; ok @$polygon < @$gear, 'gear was simplified using merge_continuous_lines';
my $num_points = scalar @$polygon; my $num_points = scalar @$polygon;
$polygon->simplify; my @simplified = $polygon->simplify;
note sprintf "original points: %d\nnew points: %d", $num_points, scalar(@$polygon); ok @simplified == 1, 'gear simplified to a single polygon';
ok @$polygon < $num_points, 'gear was further simplified using Douglas-Peucker'; note sprintf "original points: %d\nnew points: %d", $num_points, scalar(@{$simplified[0]});
ok @{$simplified[0]} < $num_points, 'gear was further simplified using Douglas-Peucker';
} }
{ {

View file

@ -40,11 +40,11 @@ use Slic3r::Test;
# copy of Print::export_gcode() up to the point # copy of Print::export_gcode() up to the point
# after fill surfaces are combined # after fill surfaces are combined
$self->init_extruders; $self->init_extruders;
$_->slice(keep_meshes => 1) for @{$self->objects}; $_->slice for @{$self->objects};
$_->make_perimeters for @{$self->objects}; $_->make_perimeters for @{$self->objects};
foreach my $layer (map @{$_->layers}, @{$self->objects}) { foreach my $layer (map @{$_->layers}, @{$self->objects}) {
$_->simplify(&Slic3r::SCALED_RESOLUTION) @$_ = map $_->simplify(&Slic3r::SCALED_RESOLUTION), @$_
for @{$layer->slices}, (map $_->expolygon, map @{$_->slices}, @{$layer->regions}); for $layer->slices, (map $_->slices, @{$layer->regions});
} }
$_->detect_surfaces_type for @{$self->objects}; $_->detect_surfaces_type for @{$self->objects};
$_->prepare_fill_surfaces for map @{$_->regions}, map @{$_->layers}, @{$self->objects}; $_->prepare_fill_surfaces for map @{$_->regions}, map @{$_->layers}, @{$self->objects};