2012-04-29 12:51:20 +02:00
package Slic3r::Print::Object ;
use Moo ;
2013-07-29 20:49:54 +02:00
use List::Util qw(min max sum first) ;
2013-11-23 21:42:01 +01:00
use Slic3r::Geometry qw(X Y Z PI scale unscale deg2rad rad2deg scaled_epsilon chained_path) ;
2013-07-29 20:49:54 +02:00
use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex
2013-09-16 12:36:04 +02:00
offset offset_ex offset2 offset2_ex CLIPPER_OFFSET_SCALE JT_MITER) ;
2013-12-19 18:54:24 +01:00
use Slic3r::Print::State ':steps' ;
2012-05-19 16:04:33 +02:00
use Slic3r::Surface ':types' ;
2012-04-29 12:51:20 +02:00
2012-09-22 19:04:36 +02:00
has 'print' => ( is => 'ro' , weak_ref => 1 , required => 1 );
2013-12-18 00:13:41 +01:00
has 'model_object' => ( is => 'ro' , required => 1 );
has 'region_volumes' => ( is => 'rw' , default => sub { [] }); # by region_id
2013-12-15 16:17:12 +01:00
has 'copies' => ( is => 'ro' ); # Slic3r::Point objects in scaled G-code coordinates
2013-08-25 14:37:50 +02:00
has 'config_overrides' => ( is => 'rw' , default => sub { Slic3r::Config -> new });
has 'config' => ( is => 'rw' );
2013-03-10 14:58:49 +01:00
has 'layer_height_ranges' => ( is => 'rw' , default => sub { [] }); # [ z_min, z_max, layer_height ]
2013-12-15 16:17:12 +01:00
has 'size' => ( is => 'rw' ); # XYZ in scaled coordinates
has '_copies_shift' => ( is => 'rw' ); # scaled coordinates to add to copies (to compensate for the alignment operated when creating the object but still preserving a coherent API for external callers)
has '_shifted_copies' => ( is => 'rw' ); # Slic3r::Point objects in scaled G-code coordinates in our coordinates
has 'layers' => ( is => 'rw' , default => sub { [] });
has 'support_layers' => ( is => 'rw' , default => sub { [] });
2013-05-19 11:35:41 +02:00
has 'fill_maker' => ( is => 'lazy' );
2013-12-19 18:54:24 +01:00
has '_state' => ( is => 'ro' , default => sub { Slic3r::Print::State -> new });
2012-04-29 12:51:20 +02:00
2013-01-01 23:28:48 +01:00
sub BUILD {
my $self = shift ;
2013-08-25 14:37:50 +02:00
$self -> init_config ;
2013-12-15 16:17:12 +01:00
# translate meshes so that we work with smaller coordinates
{
# compute the bounding box of the supplied meshes
2013-12-18 00:13:41 +01:00
my @meshes = map $self -> model_object -> volumes -> [ $_ ] -> mesh ,
map @$_ ,
grep defined $_ ,
@ { $self -> region_volumes };
2013-12-15 16:17:12 +01:00
my $bb = Slic3r::Geometry::BoundingBox -> merge ( map $_ -> bounding_box , @meshes );
# Translate meshes so that our toolpath generation algorithms work with smaller
# XY coordinates; this translation is an optimization and not strictly required.
# However, this also aligns object to Z = 0, which on the contrary is required
# since we don't assume input is already aligned.
# We store the XY translation so that we can place copies correctly in the output G-code
# (copies are expressed in G-code coordinates and this translation is not publicly exposed).
$self -> _copies_shift ( Slic3r::Point -> new_scale ( $bb -> x_min , $bb -> y_min ));
$self -> _trigger_copies ;
# Scale the object size and store it
my $scaled_bb = $bb -> clone ;
$scaled_bb -> scale ( 1 / & Slic3r:: SCALING_FACTOR );
$self -> size ( $scaled_bb -> size );
}
2013-01-01 23:28:48 +01:00
}
2013-05-19 11:35:41 +02:00
sub _build_fill_maker {
my $self = shift ;
2013-11-26 18:46:48 +01:00
return Slic3r::Fill -> new ( bounding_box => $self -> bounding_box );
2013-05-19 11:35:41 +02:00
}
2013-03-16 20:56:14 +01:00
sub _trigger_copies {
my $self = shift ;
2013-12-15 16:17:12 +01:00
return if ! defined $self -> _copies_shift ;
# order copies with a nearest neighbor search and translate them by _copies_shift
$self -> _shifted_copies ([
map {
my $c = $_ -> clone ;
$c -> translate ( @ { $self -> _copies_shift });
$c ;
} @ { $self -> copies }[ @ { chained_path ( $self -> copies )}]
]);
2013-12-19 18:54:24 +01:00
$self -> print -> _state -> invalidate ( STEP_SKIRT );
$self -> print -> _state -> invalidate ( STEP_BRIM );
2013-12-15 16:17:12 +01:00
}
# in unscaled coordinates
sub add_copy {
my ( $self , $x , $y ) = @_ ;
push @ { $self -> copies }, Slic3r::Point -> new_scale ( $x , $y );
$self -> _trigger_copies ;
}
sub delete_last_copy {
my ( $self ) = @_ ;
pop @ { $self -> copies };
$self -> _trigger_copies ;
2013-03-16 20:56:14 +01:00
}
2013-12-18 18:54:11 +01:00
sub delete_all_copies {
my ( $self ) = @_ ;
@ { $self -> copies } = ();
$self -> _trigger_copies ;
2013-03-16 20:56:14 +01:00
}
2013-08-25 14:37:50 +02:00
sub init_config {
my $self = shift ;
$self -> config ( Slic3r::Config -> merge ( $self -> print -> config , $self -> config_overrides ));
}
2012-04-29 12:51:20 +02:00
sub layer_count {
my $self = shift ;
return scalar @ { $self -> layers };
}
2013-05-19 11:35:41 +02:00
sub bounding_box {
my $self = shift ;
# since the object is aligned to origin, bounding box coincides with size
2013-08-26 23:27:51 +02:00
return Slic3r::Geometry::BoundingBox -> new_from_points ([ map Slic3r::Point -> new ( @$_ [ X , Y ]), [ 0 , 0 ], $self -> size ]);
2013-05-19 11:35:41 +02:00
}
2013-12-15 16:17:12 +01:00
# this should be idempotent
2012-04-30 14:56:01 +02:00
sub slice {
my $self = shift ;
2012-05-28 19:58:27 +02:00
my %params = @_ ;
2012-04-30 14:56:01 +02:00
2013-12-15 16:17:12 +01:00
# init layers
{
@ { $self -> layers } = ();
# make layers taking custom heights into account
my $print_z = my $slice_z = my $height = my $id = 0 ;
# add raft layers
if ( $self -> config -> raft_layers > 0 ) {
2013-12-24 11:29:31 +01:00
$print_z += $self -> config -> get_value ( 'first_layer_height' );
$print_z += $self -> config -> layer_height * ( $self -> config -> raft_layers - 1 );
2013-12-15 16:17:12 +01:00
$id += $self -> config -> raft_layers ;
}
# loop until we have at least one layer and the max slice_z reaches the object height
my $max_z = unscale $self -> size -> [ Z ];
while ( ! @ { $self -> layers } || ( $slice_z - $height ) <= $max_z ) {
# assign the default height to the layer according to the general settings
$height = ( $id == 0 )
2013-12-24 11:29:31 +01:00
? $self -> config -> get_value ( 'first_layer_height' )
: $self -> config -> layer_height ;
2013-12-15 16:17:12 +01:00
# look for an applicable custom range
if ( my $range = first { $_ -> [ 0 ] <= $slice_z && $_ -> [ 1 ] > $slice_z } @ { $self -> layer_height_ranges }) {
$height = $range -> [ 2 ];
# if user set custom height to zero we should just skip the range and resume slicing over it
if ( $height == 0 ) {
$slice_z += $range -> [ 1 ] - $range -> [ 0 ];
next ;
}
}
$print_z += $height ;
$slice_z += $height / 2 ;
### Slic3r::debugf "Layer %d: height = %s; slice_z = %s; print_z = %s\n", $id, $height, $slice_z, $print_z;
push @ { $self -> layers }, Slic3r::Layer -> new (
object => $self ,
id => $id ,
height => $height ,
print_z => $print_z ,
slice_z => $slice_z ,
);
if ( @ { $self -> layers } >= 2 ) {
$self -> layers -> [ - 2 ] -> upper_layer ( $self -> layers -> [ - 1 ]);
}
$id ++ ;
$slice_z += $height / 2 ; # add the other half layer
}
}
2013-06-22 20:37:15 +02:00
# make sure all layers contain layer region objects for all regions
my $regions_count = $self -> print -> regions_count ;
foreach my $layer ( @ { $self -> layers }) {
$layer -> region ( $_ ) for 0 .. ( $regions_count - 1 );
}
2012-04-30 14:56:01 +02:00
# process facets
2013-12-18 00:13:41 +01:00
for my $region_id ( 0 .. $# { $self -> region_volumes }) {
next if ! defined $self -> region_volumes -> [ $region_id ];
# compose mesh
my $mesh ;
foreach my $volume_id ( @ { $self -> region_volumes -> [ $region_id ]}) {
if ( defined $mesh ) {
$mesh -> merge ( $self -> model_object -> volumes -> [ $volume_id ] -> mesh );
} else {
$mesh = $self -> model_object -> volumes -> [ $volume_id ] -> mesh -> clone ;
}
}
# transform mesh
# we ignore the per-instance transformations currently and only
# consider the first one
$self -> model_object -> instances -> [ 0 ] -> transform_mesh ( $mesh , 1 );
# align mesh to Z = 0 and apply XY shift
2013-12-18 16:34:31 +01:00
$mesh -> translate (( map unscale ( - $_ ), @ { $self -> _copies_shift }), - $self -> model_object -> bounding_box -> z_min );
2013-12-13 12:18:30 +01:00
2013-09-09 18:21:10 +02:00
{
2013-09-11 11:55:08 +02:00
my $loops = $mesh -> slice ([ map $_ -> slice_z , @ { $self -> layers } ]);
for my $layer_id ( 0 .. $#$loops ) {
2013-09-09 18:21:10 +02:00
my $layerm = $self -> layers -> [ $layer_id ] -> regions -> [ $region_id ];
2013-09-11 11:55:08 +02:00
$layerm -> make_surfaces ( $loops -> [ $layer_id ]);
2012-04-30 14:56:01 +02:00
}
2013-09-09 18:21:10 +02:00
# TODO: read slicing_errors
}
2012-04-30 14:56:01 +02:00
}
2013-03-24 15:26:55 +01:00
# remove last layer(s) if empty
2013-06-23 12:26:40 +02:00
pop @ { $self -> layers } while @ { $self -> layers } && ( ! map @ { $_ -> slices }, @ { $self -> layers -> [ - 1 ] -> regions });
2012-04-30 14:56:01 +02:00
foreach my $layer ( @ { $self -> layers }) {
2012-09-23 02:52:31 +02:00
# merge all regions' slices to get islands
2012-09-22 19:04:36 +02:00
$layer -> make_slices ;
2012-04-30 14:56:01 +02:00
}
# detect slicing errors
my $warning_thrown = 0 ;
for my $i ( 0 .. $# { $self -> layers }) {
my $layer = $self -> layers -> [ $i ];
next unless $layer -> slicing_errors ;
if ( ! $warning_thrown ) {
warn "The model has overlapping or self-intersecting facets. I tried to repair it, "
. "however you might want to check the results or repair the input file and retry.\n" ;
$warning_thrown = 1 ;
}
# try to repair the layer surfaces by merging all contours and all holes from
# neighbor layers
Slic3r:: debugf "Attempting to repair layer %d\n" , $i ;
2012-09-23 02:52:31 +02:00
foreach my $region_id ( 0 .. $# { $layer -> regions }) {
my $layerm = $layer -> region ( $region_id );
2012-09-22 19:04:36 +02:00
my ( @upper_surfaces , @lower_surfaces );
for ( my $j = $i + 1 ; $j <= $# { $self -> layers }; $j ++ ) {
if ( ! $self -> layers -> [ $j ] -> slicing_errors ) {
2012-09-23 02:52:31 +02:00
@upper_surfaces = @ { $self -> layers -> [ $j ] -> region ( $region_id ) -> slices };
2012-09-22 19:04:36 +02:00
last ;
}
2012-04-30 14:56:01 +02:00
}
2012-09-22 19:04:36 +02:00
for ( my $j = $i - 1 ; $j >= 0 ; $j -- ) {
if ( ! $self -> layers -> [ $j ] -> slicing_errors ) {
2012-09-23 02:52:31 +02:00
@lower_surfaces = @ { $self -> layers -> [ $j ] -> region ( $region_id ) -> slices };
2012-09-22 19:04:36 +02:00
last ;
}
2012-04-30 14:56:01 +02:00
}
2012-09-22 19:04:36 +02:00
my $union = union_ex ([
map $_ -> expolygon -> contour , @upper_surfaces , @lower_surfaces ,
]);
my $diff = diff_ex (
[ map @$_ , @$union ],
2013-08-26 23:09:18 +02:00
[ map @ { $_ -> expolygon -> holes }, @upper_surfaces , @lower_surfaces , ],
2012-09-22 19:04:36 +02:00
);
2013-07-14 15:03:45 +02:00
$layerm -> slices -> clear ;
$layerm -> slices -> append (
map Slic3r::Surface -> new
( expolygon => $_ , surface_type => S_TYPE_INTERNAL ),
@$diff
);
2012-04-30 14:56:01 +02:00
}
2012-09-22 19:04:36 +02:00
2012-09-23 02:52:31 +02:00
# update layer slices after repairing the single regions
2012-09-22 19:04:36 +02:00
$layer -> make_slices ;
2012-04-30 14:56:01 +02:00
}
# remove empty layers from bottom
2013-08-25 14:37:50 +02:00
my $first_object_layer_id = $self -> config -> raft_layers ;
2013-12-18 10:20:36 +01:00
while ( @ { $self -> layers } && ! @ { $self -> layers -> [ $first_object_layer_id ] -> slices }) {
2013-01-28 14:12:01 +01:00
splice @ { $self -> layers }, $first_object_layer_id , 1 ;
for ( my $i = $first_object_layer_id ; $i <= $# { $self -> layers }; $i ++ ) {
2012-04-30 14:56:01 +02:00
$self -> layers -> [ $i ] -> id ( $i );
}
}
2013-12-19 15:23:10 +01:00
# simplify slices if required
if ( $self -> config -> resolution ) {
$self -> _simplify_slices ( scale ( $self -> config -> resolution ));
}
2012-04-30 14:56:01 +02:00
}
2012-05-05 16:36:10 +02:00
sub make_perimeters {
my $self = shift ;
# compare each layer to the one below, and mark those slices needing
# one additional inner perimeter, like the top of domed objects-
2013-02-18 11:37:34 +01:00
# this algorithm makes sure that at least one perimeter is overlapping
# but we don't generate any extra perimeter if fill density is zero, as they would be floating
# inside the object - infill_only_where_needed should be the method of choice for printing
# hollow objects
2013-08-25 14:37:50 +02:00
if ( $self -> config -> extra_perimeters && $self -> config -> perimeters > 0 && $self -> config -> fill_density > 0 ) {
2012-09-23 02:52:31 +02:00
for my $region_id ( 0 .. ( $self -> print -> regions_count - 1 )) {
2012-09-22 19:04:36 +02:00
for my $layer_id ( 0 .. $self -> layer_count - 2 ) {
2012-09-23 03:03:08 +02:00
my $layerm = $self -> layers -> [ $layer_id ] -> regions -> [ $region_id ];
my $upper_layerm = $self -> layers -> [ $layer_id + 1 ] -> regions -> [ $region_id ];
2013-02-24 16:40:14 +01:00
my $perimeter_spacing = $layerm -> perimeter_flow -> scaled_spacing ;
2012-09-22 19:04:36 +02:00
2013-02-24 16:40:14 +01:00
my $overlap = $perimeter_spacing ; # one perimeter
2012-09-22 19:04:36 +02:00
2013-05-31 14:30:07 +02:00
my $diff = diff (
2013-08-25 19:59:42 +02:00
offset ([ map @ { $_ -> expolygon }, @ { $layerm -> slices } ], - ( $self -> config -> perimeters * $perimeter_spacing )),
2013-07-17 00:48:29 +02:00
offset ([ map @ { $_ -> expolygon }, @ { $upper_layerm -> slices } ], - $overlap ),
2012-06-23 17:10:30 +02:00
);
2013-03-10 19:08:36 +01:00
next if ! @$diff ;
# if we need more perimeters, $diff should contain a narrow region that we can collapse
2013-09-17 20:18:54 +02:00
# we use a higher miterLimit here to handle areas with acute angles
# in those cases, the default miterLimit would cut the corner and we'd
# get a triangle that would trigger a non-needed extra perimeter
2013-05-31 14:30:07 +02:00
$diff = diff (
$diff ,
2013-09-17 20:18:54 +02:00
offset2 ( $diff , - $perimeter_spacing , + $perimeter_spacing , CLIPPER_OFFSET_SCALE , JT_MITER , 5 ),
2013-05-31 14:23:42 +02:00
1 ,
2013-03-10 19:08:36 +01:00
);
next if ! @$diff ;
# diff contains the collapsed area
2012-09-22 19:04:36 +02:00
foreach my $slice ( @ { $layerm -> slices }) {
2013-03-11 14:23:16 +01:00
my $extra_perimeters = 0 ;
2012-09-22 19:04:36 +02:00
CYCLE: while ( 1 ) {
# compute polygons representing the thickness of the hypotetical new internal perimeter
# of our slice
2013-03-11 14:23:16 +01:00
$extra_perimeters ++ ;
2013-05-31 14:30:07 +02:00
my $hypothetical_perimeter = diff (
2013-08-25 19:59:42 +02:00
offset ( $slice -> expolygon -> arrayref , - ( $perimeter_spacing * ( $self -> config -> perimeters + $extra_perimeters - 1 ))),
offset ( $slice -> expolygon -> arrayref , - ( $perimeter_spacing * ( $self -> config -> perimeters + $extra_perimeters ))),
2013-03-10 19:08:36 +01:00
);
last CYCLE if ! @$hypothetical_perimeter ; # no extra perimeter is possible
2012-09-22 19:04:36 +02:00
2013-03-10 19:08:36 +01:00
# only add the perimeter if there's an intersection with the collapsed area
2013-05-31 14:30:07 +02:00
last CYCLE if ! @ { intersection ( $diff , $hypothetical_perimeter ) };
2013-11-20 15:38:51 +01:00
2012-09-22 19:04:36 +02:00
Slic3r:: debugf " adding one more perimeter at layer %d\n" , $layer_id ;
2013-03-11 14:23:16 +01:00
$slice -> extra_perimeters ( $extra_perimeters );
2012-06-23 17:10:30 +02:00
}
2012-05-05 16:36:10 +02:00
}
}
}
}
2013-01-31 15:44:55 +01:00
Slic3r:: parallelize (
2013-12-24 11:29:31 +01:00
threads => $self -> config -> threads ,
2013-01-31 15:44:55 +01:00
items => sub { 0 .. ( $self -> layer_count - 1 ) },
thread_cb => sub {
my $q = shift ;
while ( defined ( my $layer_id = $q -> dequeue )) {
2013-07-18 22:29:12 +02:00
$self -> layers -> [ $layer_id ] -> make_perimeters ;
2013-01-31 15:44:55 +01:00
}
},
2013-07-18 22:29:12 +02:00
collect_cb => sub {},
2013-01-31 15:44:55 +01:00
no_threads_cb => sub {
$_ -> make_perimeters for @ { $self -> layers };
},
);
2013-12-19 15:23:10 +01:00
# simplify slices (both layer and region slices),
# we only need the max resolution for perimeters
### This makes this method not-idempotent, so we keep it disabled for now.
###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
2012-05-05 16:36:10 +02:00
}
2012-04-29 12:51:20 +02:00
sub detect_surfaces_type {
my $self = shift ;
Slic3r:: debugf "Detecting solid surfaces...\n" ;
2012-09-23 02:52:31 +02:00
for my $region_id ( 0 .. ( $self -> print -> regions_count - 1 )) {
2013-02-26 20:52:13 +01:00
for my $i ( 0 .. ( $self -> layer_count - 1 )) {
2012-09-23 02:52:31 +02:00
my $layerm = $self -> layers -> [ $i ] -> regions -> [ $region_id ];
2013-09-16 12:36:04 +02:00
# prepare a reusable subroutine to make surface differences
my $difference = sub {
my ( $subject , $clip , $result_type ) = @_ ;
my $diff = diff (
[ map @$_ , @$subject ],
[ map @$_ , @$clip ],
);
# collapse very narrow parts (using the safety offset in the diff is not enough)
my $offset = $layerm -> perimeter_flow -> scaled_width / 10 ;
return map Slic3r::Surface -> new ( expolygon => $_ , surface_type => $result_type ),
@ { offset2_ex ( $diff , - $offset , + $offset ) };
};
2012-09-22 19:04:36 +02:00
2012-09-23 02:52:31 +02:00
# comparison happens against the *full* slices (considering all regions)
2012-09-22 19:04:36 +02:00
my $upper_layer = $self -> layers -> [ $i + 1 ];
my $lower_layer = $i > 0 ? $self -> layers -> [ $i - 1 ] : undef ;
my ( @bottom , @top , @internal ) = ();
# find top surfaces (difference between current surfaces
# of current layer and upper one)
if ( $upper_layer ) {
2013-09-07 00:42:56 +02:00
@top = $difference -> (
2013-02-26 20:52:13 +01:00
[ map $_ -> expolygon , @ { $layerm -> slices } ],
$upper_layer -> slices ,
S_TYPE_TOP ,
);
2012-09-22 19:04:36 +02:00
} else {
# if no upper layer, all surfaces of this one are solid
2013-07-15 15:26:56 +02:00
# we clone surfaces because we're going to clear the slices collection
@top = map $_ -> clone , @ { $layerm -> slices };
2012-09-22 19:04:36 +02:00
$_ -> surface_type ( S_TYPE_TOP ) for @top ;
}
# find bottom surfaces (difference between current surfaces
# of current layer and lower one)
if ( $lower_layer ) {
2013-02-26 20:52:13 +01:00
# lower layer's slices are already Surface objects
2013-09-07 00:42:56 +02:00
@bottom = $difference -> (
2013-02-26 20:52:13 +01:00
[ map $_ -> expolygon , @ { $layerm -> slices } ],
$lower_layer -> slices ,
S_TYPE_BOTTOM ,
);
2012-09-22 19:04:36 +02:00
} else {
# if no lower layer, all surfaces of this one are solid
2013-07-15 15:26:56 +02:00
# we clone surfaces because we're going to clear the slices collection
@bottom = map $_ -> clone , @ { $layerm -> slices };
2012-09-22 19:04:36 +02:00
$_ -> surface_type ( S_TYPE_BOTTOM ) for @bottom ;
}
# now, if the object contained a thin membrane, we could have overlapping bottom
# and top surfaces; let's do an intersection to discover them and consider them
# as bottom surfaces (to allow for bridge detection)
if ( @top && @bottom ) {
my $overlapping = intersection_ex ([ map $_ -> p , @top ], [ map $_ -> p , @bottom ]);
2013-09-16 12:36:04 +02:00
Slic3r:: debugf " layer %d contains %d membrane(s)\n" , $layerm -> id , scalar ( @$overlapping )
if $ Slic3r:: debug ;
2013-09-07 00:42:56 +02:00
@top = $difference -> ([ map $_ -> expolygon , @top ], $overlapping , S_TYPE_TOP );
2012-09-22 19:04:36 +02:00
}
# find internal surfaces (difference between top/bottom surfaces and others)
2013-09-07 00:42:56 +02:00
@internal = $difference -> (
2013-02-26 20:52:13 +01:00
[ map $_ -> expolygon , @ { $layerm -> slices } ],
[ map $_ -> expolygon , @top , @bottom ],
S_TYPE_INTERNAL ,
);
2012-09-22 19:04:36 +02:00
# save surfaces to layer
2013-07-14 15:03:45 +02:00
$layerm -> slices -> clear ;
$layerm -> slices -> append ( @bottom , @top , @internal );
2012-09-22 19:04:36 +02:00
Slic3r:: debugf " layer %d has %d bottom, %d top and %d internal surfaces\n" ,
2013-09-16 12:36:04 +02:00
$layerm -> id , scalar ( @bottom ), scalar ( @top ), scalar ( @internal ) if $ Slic3r:: debug ;
2012-04-29 12:51:20 +02:00
}
2012-09-22 19:04:36 +02:00
# clip surfaces to the fill boundaries
foreach my $layer ( @ { $self -> layers }) {
2012-09-23 02:52:31 +02:00
my $layerm = $layer -> regions -> [ $region_id ];
2013-07-15 15:26:56 +02:00
my $fill_boundaries = [ map $_ -> clone -> p , @ { $layerm -> fill_surfaces } ];
2013-07-14 14:56:43 +02:00
$layerm -> fill_surfaces -> clear ;
2012-09-22 19:04:36 +02:00
foreach my $surface ( @ { $layerm -> slices }) {
my $intersection = intersection_ex (
[ $surface -> p ],
$fill_boundaries ,
);
2013-07-14 14:56:43 +02:00
$layerm -> fill_surfaces -> append ( map Slic3r::Surface -> new
2012-09-22 19:04:36 +02:00
( expolygon => $_ , surface_type => $surface -> surface_type ),
2013-07-14 14:56:43 +02:00
@$intersection );
2012-09-22 19:04:36 +02:00
}
2012-04-29 12:51:20 +02:00
}
}
}
2013-02-09 23:36:32 +01:00
sub clip_fill_surfaces {
my $self = shift ;
2013-08-25 14:37:50 +02:00
return unless $self -> config -> infill_only_where_needed ;
2013-02-09 23:36:32 +01:00
# We only want infill under ceilings; this is almost like an
# internal support material.
my $additional_margin = scale 3 ;
2013-09-07 00:02:58 +02:00
my $overhangs = [] ; # arrayref of polygons
2013-02-09 23:36:32 +01:00
for my $layer_id ( reverse 0 .. $# { $self -> layers }) {
my $layer = $self -> layers -> [ $layer_id ];
2013-09-07 00:02:58 +02:00
my @layer_internal = ();
my @new_internal = ();
2013-02-09 23:36:32 +01:00
# clip this layer's internal surfaces to @overhangs
foreach my $layerm ( @ { $layer -> regions }) {
2013-09-07 00:02:58 +02:00
# we assume that this step is run before bridge_over_infill() and combine_infill()
# so these are the only internal types we might have
my ( @internal , @other ) = ();
foreach my $surface ( map $_ -> clone , @ { $layerm -> fill_surfaces }) {
$surface -> surface_type == S_TYPE_INTERNAL
? push @internal , $surface
: 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 (
expolygon => $_ ,
surface_type => S_TYPE_INTERNAL ,
),
@ { intersection_ex (
$overhangs ,
[ map $_ -> p , @internal ],
)};
2013-07-14 14:56:43 +02:00
$layerm -> fill_surfaces -> clear ;
2013-09-07 00:02:58 +02:00
$layerm -> fill_surfaces -> append ( @new , @other );
2013-02-09 23:36:32 +01:00
}
2013-09-07 00:02:58 +02:00
# get this layer's overhangs defined as the full slice minus the internal infill
# (thus we also consider perimeters)
2013-02-09 23:36:32 +01:00
if ( $layer_id > 0 ) {
2013-09-07 00:02:58 +02:00
my $solid = diff (
[ map @$_ , @ { $layer -> slices } ],
\ @layer_internal ,
);
$overhangs = offset ( $solid , + $additional_margin );
push @$overhangs , @new_internal ; # propagate upper overhangs
2013-02-09 23:36:32 +01:00
}
}
}
2013-02-23 21:39:13 +01:00
sub bridge_over_infill {
my $self = shift ;
2013-08-25 14:37:50 +02:00
return if $self -> config -> fill_density == 1 ;
2013-02-23 21:39:13 +01:00
for my $layer_id ( 1 .. $# { $self -> layers }) {
my $layer = $self -> layers -> [ $layer_id ];
my $lower_layer = $self -> layers -> [ $layer_id - 1 ];
foreach my $layerm ( @ { $layer -> regions }) {
# compute the areas needing bridge math
2013-09-06 18:36:38 +02:00
my @internal_solid = @ { $layerm -> fill_surfaces -> filter_by_type ( S_TYPE_INTERNALSOLID )};
my @lower_internal = map @ { $_ -> fill_surfaces -> filter_by_type ( S_TYPE_INTERNAL )}, @ { $lower_layer -> regions };
2013-02-23 21:39:13 +01:00
my $to_bridge = intersection_ex (
[ map $_ -> p , @internal_solid ],
[ map $_ -> p , @lower_internal ],
);
next unless @$to_bridge ;
Slic3r:: debugf "Bridging %d internal areas at layer %d\n" , scalar ( @$to_bridge ), $layer_id ;
# build the new collection of fill_surfaces
{
2013-07-15 15:26:56 +02:00
my @new_surfaces = map $_ -> clone , grep $_ -> surface_type != S_TYPE_INTERNALSOLID , @ { $layerm -> fill_surfaces };
2013-02-23 21:39:13 +01:00
push @new_surfaces , map Slic3r::Surface -> new (
expolygon => $_ ,
surface_type => S_TYPE_INTERNALBRIDGE ,
), @$to_bridge ;
push @new_surfaces , map Slic3r::Surface -> new (
expolygon => $_ ,
surface_type => S_TYPE_INTERNALSOLID ,
), @ { diff_ex (
[ map $_ -> p , @internal_solid ],
[ map @$_ , @$to_bridge ],
2013-03-05 19:33:06 +01:00
1 ,
2013-02-23 21:39:13 +01:00
)};
2013-07-14 14:56:43 +02:00
$layerm -> fill_surfaces -> clear ;
$layerm -> fill_surfaces -> append ( @new_surfaces );
2013-02-23 21:39:13 +01:00
}
# exclude infill from the layers below if needed
# see discussion at https://github.com/alexrj/Slic3r/issues/240
2013-03-18 13:32:19 +01:00
# Update: do not exclude any infill. Sparse infill is able to absorb the excess material.
if ( 0 ) {
2013-02-27 10:43:50 +01:00
my $excess = $layerm -> extruders -> { infill } -> bridge_flow -> width - $layerm -> height ;
2013-02-23 21:39:13 +01:00
for ( my $i = $layer_id - 1 ; $excess >= $self -> layers -> [ $i ] -> height ; $i -- ) {
Slic3r:: debugf " skipping infill below those areas at layer %d\n" , $i ;
foreach my $lower_layerm ( @ { $self -> layers -> [ $i ] -> regions }) {
my @new_surfaces = ();
# subtract the area from all types of surfaces
2013-11-23 18:15:59 +01:00
foreach my $group ( @ { $lower_layerm -> fill_surfaces -> group }) {
2013-03-29 19:18:06 +01:00
push @new_surfaces , map $group -> [ 0 ] -> clone ( expolygon => $_ ),
@ { diff_ex (
[ map $_ -> p , @$group ],
[ map @$_ , @$to_bridge ],
)};
2013-03-13 01:03:54 +01:00
push @new_surfaces , map Slic3r::Surface -> new (
expolygon => $_ ,
surface_type => S_TYPE_INTERNALVOID ,
), @ { intersection_ex (
[ map $_ -> p , @$group ],
[ map @$_ , @$to_bridge ],
)};
2013-02-23 21:39:13 +01:00
}
2013-07-14 14:56:43 +02:00
$lower_layerm -> fill_surfaces -> clear ;
$lower_layerm -> fill_surfaces -> append ( @new_surfaces );
2013-02-23 21:39:13 +01:00
}
$excess -= $self -> layers -> [ $i ] -> height ;
}
}
}
}
}
2013-11-11 20:37:06 +01:00
sub process_external_surfaces {
my ( $self ) = @_ ;
for my $region_id ( 0 .. ( $self -> print -> regions_count - 1 )) {
$self -> layers -> [ 0 ] -> regions -> [ $region_id ] -> process_external_surfaces ( undef );
for my $layer_id ( 1 .. ( $self -> layer_count - 1 )) {
$self -> layers -> [ $layer_id ] -> regions -> [ $region_id ] -> process_external_surfaces ( $self -> layers -> [ $layer_id - 1 ]);
}
}
}
2012-04-29 12:51:20 +02:00
sub discover_horizontal_shells {
my $self = shift ;
Slic3r:: debugf "==> DISCOVERING HORIZONTAL SHELLS\n" ;
2012-09-23 02:52:31 +02:00
for my $region_id ( 0 .. ( $self -> print -> regions_count - 1 )) {
2012-09-22 19:04:36 +02:00
for ( my $i = 0 ; $i < $self -> layer_count ; $i ++ ) {
2012-09-23 02:52:31 +02:00
my $layerm = $self -> layers -> [ $i ] -> regions -> [ $region_id ];
2012-09-28 15:46:29 +02:00
2013-08-25 14:37:50 +02:00
if ( $self -> config -> solid_infill_every_layers && $self -> config -> fill_density > 0
&& ( $i % $ self -> config -> solid_infill_every_layers ) == 0 ) {
2013-09-06 18:36:38 +02:00
$_ -> surface_type ( S_TYPE_INTERNALSOLID ) for @ { $layerm -> fill_surfaces -> filter_by_type ( S_TYPE_INTERNAL )};
2012-09-28 15:46:29 +02:00
}
2013-07-28 10:56:41 +02:00
EXTERNAL: foreach my $type ( S_TYPE_TOP , S_TYPE_BOTTOM ) {
2012-12-21 20:25:48 +01:00
# find slices of current type for current layer
2013-08-13 09:45:33 +02:00
# use slices instead of fill_surfaces because they also include the perimeter area
# which needs to be propagated in shells; we need to grow slices like we did for
# fill_surfaces though. Using both ungrown slices and grown fill_surfaces will
# not work in some situations, as there won't be any grown region in the perimeter
# area (this was seen in a model where the top layer had one extra perimeter, thus
2013-09-17 19:24:16 +02:00
# its fill_surfaces were thinner than the lower layer's infill), however it's the best
# solution so far. Growing the external slices by EXTERNAL_INFILL_MARGIN will put
# too much solid infill inside nearly-vertical slopes.
my $solid = [
( map $_ -> p , @ { $layerm -> slices -> filter_by_type ( $type )}),
( map $_ -> p , @ { $layerm -> fill_surfaces -> filter_by_type ( $type )}),
];
2013-03-11 18:37:01 +01:00
next if ! @$solid ;
2013-07-15 12:14:22 +02:00
Slic3r:: debugf "Layer %d has %s surfaces\n" , $i , ( $type == S_TYPE_TOP ) ? 'top' : 'bottom' ;
2012-04-29 12:51:20 +02:00
2012-10-28 12:52:44 +01:00
my $solid_layers = ( $type == S_TYPE_TOP )
2013-08-25 14:37:50 +02:00
? $self -> config -> top_solid_layers
: $self -> config -> bottom_solid_layers ;
2013-08-08 02:10:34 +02:00
NEIGHBOR: for ( my $n = ( $type == S_TYPE_TOP ) ? $i - 1 : $i + 1 ;
2012-10-25 12:21:04 +02:00
abs ( $n - $i ) <= $solid_layers - 1 ;
2013-07-15 12:14:22 +02:00
( $type == S_TYPE_TOP ) ? $n -- : $n ++ ) {
2012-09-22 19:04:36 +02:00
next if $n < 0 || $n >= $self -> layer_count ;
Slic3r:: debugf " looking for neighbors on layer %d...\n" , $n ;
2013-09-09 17:10:54 +02:00
my $neighbor_fill_surfaces = $self -> layers -> [ $n ] -> regions -> [ $region_id ] -> fill_surfaces ;
my @neighbor_fill_surfaces = map $_ -> clone , @$neighbor_fill_surfaces ; # clone because we will use these surfaces even after clearing the collection
2012-09-22 19:04:36 +02:00
# find intersection between neighbor and current layer's surfaces
# intersections have contours and holes
2013-07-27 19:41:36 +02:00
# we update $solid so that we limit the next neighbor layer to the areas that were
# found on this one - in other words, solid shells on one layer (for a given external surface)
# are always a subset of the shells found on the previous shell layer
# this approach allows for DWIM in hollow sloping vases, where we want bottom
# shells to be generated in the base but not in the walls (where there are many
# narrow bottom surfaces): reassigning $solid will consider the 'shadow' of the
# upper perimeter as an obstacle and shell will not be propagated to more upper layers
2013-09-06 19:21:38 +02:00
my $new_internal_solid = $solid = intersection (
$solid ,
2013-07-15 12:14:22 +02:00
[ map $_ -> p , grep { ( $_ -> surface_type == S_TYPE_INTERNAL ) || ( $_ -> surface_type == S_TYPE_INTERNALSOLID ) } @neighbor_fill_surfaces ],
2013-07-17 00:29:09 +02:00
1 ,
2012-09-22 19:04:36 +02:00
);
2013-07-28 10:56:41 +02:00
next EXTERNAL if ! @$new_internal_solid ;
2012-09-22 19:04:36 +02:00
2013-03-11 18:37:01 +01:00
# make sure the new internal solid is wide enough, as it might get collapsed when
# spacing is added in Fill.pm
{
2013-09-06 17:43:40 +02:00
# we use a higher miterLimit here to handle areas with acute angles
# in those cases, the default miterLimit would cut the corner and we'd
# get a triangle in $too_narrow; if we grow it below then the shell
# would have a different shape from the external surface and we'd still
# have the same angle, so the next shell would be grown even more and so on.
2013-03-17 00:02:31 +01:00
my $margin = 3 * $layerm -> solid_infill_flow -> scaled_width ; # require at least this size
2013-09-06 19:21:38 +02:00
my $too_narrow = diff (
$new_internal_solid ,
offset2 ( $new_internal_solid , - $margin , + $margin , CLIPPER_OFFSET_SCALE , JT_MITER , 5 ),
2013-03-18 17:55:16 +01:00
1 ,
2013-03-11 18:37:01 +01:00
);
2013-07-28 10:56:41 +02:00
# if some parts are going to collapse, use a different strategy according to fill density
2013-03-11 18:37:01 +01:00
if ( @$too_narrow ) {
2013-08-25 14:37:50 +02:00
if ( $self -> config -> fill_density > 0 ) {
2013-07-28 10:56:41 +02:00
# if we have internal infill, grow the collapsing parts and add the extra area to
# the neighbor layer as well as to our original surfaces so that we support this
# additional area in the next shell too
# make sure our grown surfaces don't exceed the fill area
2013-09-03 19:26:58 +02:00
my @grown = @ { intersection (
2013-09-06 19:21:38 +02:00
offset ( $too_narrow , + $margin ),
2013-07-28 10:56:41 +02:00
[ map $_ -> p , @neighbor_fill_surfaces ],
)};
2013-09-07 00:28:53 +02:00
$new_internal_solid = $solid = [ @grown , @$new_internal_solid ];
2013-07-28 10:56:41 +02:00
} else {
# if we're printing a hollow object, we discard such small parts
2013-09-06 19:21:38 +02:00
$new_internal_solid = $solid = diff (
$new_internal_solid ,
$too_narrow ,
2013-07-28 10:56:41 +02:00
);
}
2013-03-11 18:37:01 +01:00
}
}
2012-09-22 19:04:36 +02:00
# internal-solid are the union of the existing internal-solid surfaces
# and new ones
my $internal_solid = union_ex ([
( map $_ -> p , grep $_ -> surface_type == S_TYPE_INTERNALSOLID , @neighbor_fill_surfaces ),
2013-09-06 19:21:38 +02:00
@$new_internal_solid ,
2012-09-22 19:04:36 +02:00
]);
2013-03-07 15:47:32 +01:00
# subtract intersections from layer surfaces to get resulting internal surfaces
2012-09-22 19:04:36 +02:00
my $internal = diff_ex (
[ map $_ -> p , grep $_ -> surface_type == S_TYPE_INTERNAL , @neighbor_fill_surfaces ],
[ map @$_ , @$internal_solid ],
2012-05-01 18:51:47 +02:00
1 ,
2012-04-29 12:51:20 +02:00
);
2012-09-22 19:04:36 +02:00
Slic3r:: debugf " %d internal-solid and %d internal surfaces found\n" ,
scalar ( @$internal_solid ), scalar ( @$internal );
2013-03-07 15:47:32 +01:00
# assign resulting internal surfaces to layer
2013-07-15 12:14:22 +02:00
$neighbor_fill_surfaces -> clear ;
$neighbor_fill_surfaces -> append ( map Slic3r::Surface -> new
( expolygon => $_ , surface_type => S_TYPE_INTERNAL ), @$internal );
2012-09-22 19:04:36 +02:00
# assign new internal-solid surfaces to layer
2013-07-15 12:14:22 +02:00
$neighbor_fill_surfaces -> append ( map Slic3r::Surface -> new
( expolygon => $_ , surface_type => S_TYPE_INTERNALSOLID ), @$internal_solid );
2012-09-22 19:04:36 +02:00
# assign top and bottom surfaces to layer
2013-11-23 18:15:59 +01:00
foreach my $s ( @ { Slic3r::Surface::Collection -> new ( grep { ( $_ -> surface_type == S_TYPE_TOP ) || ( $_ -> surface_type == S_TYPE_BOTTOM ) } @neighbor_fill_surfaces ) -> group }) {
2012-09-22 19:04:36 +02:00
my $solid_surfaces = diff_ex (
[ map $_ -> p , @$s ],
[ map @$_ , @$internal_solid , @$internal ],
1 ,
);
2013-07-15 12:14:22 +02:00
$neighbor_fill_surfaces -> append ( map $s -> [ 0 ] -> clone ( expolygon => $_ ), @$solid_surfaces );
2012-09-22 19:04:36 +02:00
}
2012-04-29 12:51:20 +02:00
}
}
2012-12-23 20:20:17 +01:00
}
2012-04-29 12:51:20 +02:00
}
}
# combine fill surfaces across layers
2012-07-22 20:48:38 +02:00
sub combine_infill {
2012-04-29 12:51:20 +02:00
my $self = shift ;
2013-08-25 14:37:50 +02:00
return unless $self -> config -> infill_every_layers > 1 && $self -> config -> fill_density > 0 ;
my $every = $self -> config -> infill_every_layers ;
2012-04-29 12:51:20 +02:00
2013-02-10 12:40:43 +01:00
my $layer_count = $self -> layer_count ;
2013-03-10 12:08:18 +01:00
my @layer_heights = map $self -> layers -> [ $_ ] -> height , 0 .. $layer_count - 1 ;
2012-09-23 02:52:31 +02:00
for my $region_id ( 0 .. ( $self -> print -> regions_count - 1 )) {
2013-02-22 16:24:24 +01:00
# limit the number of combined layers to the maximum height allowed by this regions' nozzle
2013-03-10 12:08:18 +01:00
my $nozzle_diameter = $self -> print -> regions -> [ $region_id ] -> extruders -> { infill } -> nozzle_diameter ;
# define the combinations
2013-03-17 01:10:40 +01:00
my @combine = (); # layer_id => thickness in layers
2013-03-10 12:08:18 +01:00
{
my $current_height = my $layers = 0 ;
for my $layer_id ( 1 .. $#layer_heights ) {
my $height = $self -> layers -> [ $layer_id ] -> height ;
if ( $current_height + $height >= $nozzle_diameter || $layers >= $every ) {
$combine [ $layer_id - 1 ] = $layers ;
$current_height = $layers = 0 ;
}
$current_height += $height ;
$layers ++ ;
}
}
2013-02-22 16:24:24 +01:00
2013-02-10 12:40:43 +01:00
# skip bottom layer
2013-03-10 12:08:18 +01:00
for my $layer_id ( 1 .. $#combine ) {
next unless ( $combine [ $layer_id ] // 1 ) > 1 ;
2013-02-10 12:40:43 +01:00
my @layerms = map $self -> layers -> [ $_ ] -> regions -> [ $region_id ],
2013-03-10 12:08:18 +01:00
( $layer_id - ( $combine [ $layer_id ] - 1 ) .. $layer_id );
2012-04-29 12:51:20 +02:00
2013-06-23 18:21:47 +02:00
# only combine internal infill
for my $type ( S_TYPE_INTERNAL ) {
2013-02-10 12:40:43 +01:00
# we need to perform a multi-layer intersection, so let's split it in pairs
2012-04-29 12:51:20 +02:00
2013-02-10 12:40:43 +01:00
# initialize the intersection with the candidates of the lowest layer
2013-09-06 18:36:38 +02:00
my $intersection = [ map $_ -> expolygon , @ { $layerms [ 0 ] -> fill_surfaces -> filter_by_type ( $type )} ];
2012-09-22 19:04:36 +02:00
2013-02-10 12:40:43 +01:00
# start looping from the second layer and intersect the current intersection with it
for my $layerm ( @layerms [ 1 .. $#layerms ]) {
$intersection = intersection_ex (
[ map @$_ , @$intersection ],
2013-09-06 18:36:38 +02:00
[ map @ { $_ -> expolygon }, @ { $layerm -> fill_surfaces -> filter_by_type ( $type )} ],
2013-02-10 12:40:43 +01:00
);
2012-04-29 12:51:20 +02:00
}
2012-09-22 19:04:36 +02:00
2013-02-18 11:52:47 +01:00
my $area_threshold = $layerms [ 0 ] -> infill_area_threshold ;
2013-02-10 12:40:43 +01:00
@$intersection = grep $_ -> area > $area_threshold , @$intersection ;
next if ! @$intersection ;
Slic3r:: debugf " combining %d %s regions from layers %d-%d\n" ,
scalar ( @$intersection ),
( $type == S_TYPE_INTERNAL ? 'internal' : 'internal-solid' ),
$layer_id - ( $every - 1 ), $layer_id ;
# $intersection now contains the regions that can be combined across the full amount of layers
# so let's remove those areas from all layers
2013-02-16 07:53:47 -08:00
2013-07-17 00:48:29 +02:00
my @intersection_with_clearance = map @ { $_ -> offset (
2013-03-17 00:57:58 +01:00
$layerms [ - 1 ] -> solid_infill_flow -> scaled_width / 2
+ $layerms [ - 1 ] -> perimeter_flow -> scaled_width / 2
# Because fill areas for rectilinear and honeycomb are grown
# later to overlap perimeters, we need to counteract that too.
2013-08-25 14:37:50 +02:00
+ (( $type == S_TYPE_INTERNALSOLID || $self -> config -> fill_pattern =~ /(rectilinear|honeycomb)/ )
2013-03-17 00:57:58 +01:00
? $layerms [ - 1 ] -> solid_infill_flow -> scaled_width * & Slic3r:: INFILL_OVERLAP_OVER_SPACING
: 0 )
2013-07-17 00:48:29 +02:00
)}, @$intersection ;
2013-03-17 00:57:58 +01:00
2013-02-16 07:53:47 -08:00
2013-02-10 12:40:43 +01:00
foreach my $layerm ( @layerms ) {
2013-09-06 18:36:38 +02:00
my @this_type = @ { $layerm -> fill_surfaces -> filter_by_type ( $type )};
2013-07-15 15:26:56 +02:00
my @other_types = map $_ -> clone , grep $_ -> surface_type != $type , @ { $layerm -> fill_surfaces };
2013-02-10 12:40:43 +01:00
2013-03-13 01:03:54 +01:00
my @new_this_type = map Slic3r::Surface -> new ( expolygon => $_ , surface_type => $type ),
2013-02-10 12:40:43 +01:00
@ { diff_ex (
2013-09-06 18:36:38 +02:00
[ map $_ -> p , @this_type ],
2013-02-16 07:53:47 -08:00
[ @intersection_with_clearance ],
2013-02-10 12:40:43 +01:00
)};
# apply surfaces back with adjusted depth to the uppermost layer
if ( $layerm -> id == $layer_id ) {
2013-03-13 01:03:54 +01:00
push @new_this_type ,
2013-03-17 01:10:40 +01:00
map Slic3r::Surface -> new (
expolygon => $_ ,
surface_type => $type ,
thickness => sum ( map $_ -> height , @layerms ),
thickness_layers => scalar ( @layerms ),
),
2013-02-10 12:40:43 +01:00
@$intersection ;
2013-03-13 01:03:54 +01:00
} else {
# save void surfaces
push @this_type ,
map Slic3r::Surface -> new ( expolygon => $_ , surface_type => S_TYPE_INTERNALVOID ),
@ { intersection_ex (
[ map @ { $_ -> expolygon }, @this_type ],
[ @intersection_with_clearance ],
)};
2012-09-22 19:04:36 +02:00
}
2013-02-10 12:40:43 +01:00
2013-07-14 14:56:43 +02:00
$layerm -> fill_surfaces -> clear ;
$layerm -> fill_surfaces -> append ( @new_this_type , @other_types );
2012-04-29 12:51:20 +02:00
}
}
}
}
}
sub generate_support_material {
my $self = shift ;
2013-09-17 23:51:30 +02:00
return unless ( $self -> config -> support_material || $self -> config -> raft_layers > 0 )
&& $self -> layer_count >= 2 ;
2012-04-29 12:51:20 +02:00
2013-12-30 18:28:41 +01:00
my $s = Slic3r::Print::SupportMaterial -> new (
config => $self -> config ,
flow => $self -> print -> support_material_flow ,
);
$s -> generate ( $self );
2012-04-29 12:51:20 +02:00
}
2013-12-19 15:23:10 +01:00
sub _simplify_slices {
my ( $self , $distance ) = @_ ;
foreach my $layer ( @ { $self -> layers }) {
$layer -> slices -> simplify ( $distance );
$_ -> slices -> simplify ( $distance ) for @ { $layer -> regions };
}
}
2012-04-29 12:51:20 +02:00
1 ;