2012-04-29 10:51:20 +00:00
package Slic3r::Print::Object ;
use Moo ;
2013-07-29 18:49:54 +00:00
use List::Util qw( min max sum first ) ;
2013-11-23 20:42:01 +00:00
use Slic3r::Geometry qw( X Y Z PI scale unscale deg2rad rad2deg scaled_epsilon chained_path ) ;
2013-07-29 18:49:54 +00:00
use Slic3r::Geometry::Clipper qw( diff diff_ex intersection intersection_ex union union_ex
2013-09-16 10:36:04 +00:00
offset offset_ex offset2 offset2_ex CLIPPER_OFFSET_SCALE JT_MITER ) ;
2012-05-19 14:04:33 +00:00
use Slic3r::Surface ':types' ;
2012-04-29 10:51:20 +00:00
2012-09-22 17:04:36 +00:00
has 'print' = > ( is = > 'ro' , weak_ref = > 1 , required = > 1 ) ;
2013-12-17 23:13:41 +00:00
has 'model_object' = > ( is = > 'ro' , required = > 1 ) ;
has 'region_volumes' = > ( is = > 'rw' , default = > sub { [] } ) ; # by region_id
2013-12-15 15:17:12 +00:00
has 'copies' = > ( is = > 'ro' ) ; # Slic3r::Point objects in scaled G-code coordinates
2013-08-25 12:37:50 +00:00
has 'config_overrides' = > ( is = > 'rw' , default = > sub { Slic3r::Config - > new } ) ;
has 'config' = > ( is = > 'rw' ) ;
2013-03-10 13:58:49 +00:00
has 'layer_height_ranges' = > ( is = > 'rw' , default = > sub { [] } ) ; # [ z_min, z_max, layer_height ]
2013-12-15 15:17:12 +00: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 09:35:41 +00:00
has 'fill_maker' = > ( is = > 'lazy' ) ;
2012-04-29 10:51:20 +00:00
2013-01-01 22:28:48 +00:00
sub BUILD {
my $ self = shift ;
2013-08-25 12:37:50 +00:00
$ self - > init_config ;
2013-12-15 15:17:12 +00:00
# translate meshes so that we work with smaller coordinates
{
# compute the bounding box of the supplied meshes
2013-12-17 23:13:41 +00:00
my @ meshes = map $ self - > model_object - > volumes - > [ $ _ ] - > mesh ,
map @$ _ ,
grep defined $ _ ,
@ { $ self - > region_volumes } ;
2013-12-15 15:17:12 +00: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 22:28:48 +00:00
}
2013-05-19 09:35:41 +00:00
sub _build_fill_maker {
my $ self = shift ;
2013-11-26 17:46:48 +00:00
return Slic3r::Fill - > new ( bounding_box = > $ self - > bounding_box ) ;
2013-05-19 09:35:41 +00:00
}
2013-03-16 19:56:14 +00:00
sub _trigger_copies {
my $ self = shift ;
2013-12-15 15:17:12 +00: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 ) } ]
] ) ;
}
# 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 19:56:14 +00:00
}
2013-12-18 17:54:11 +00:00
sub delete_all_copies {
my ( $ self ) = @ _ ;
@ { $ self - > copies } = ( ) ;
$ self - > _trigger_copies ;
}
2013-08-25 12:37:50 +00:00
sub init_config {
my $ self = shift ;
$ self - > config ( Slic3r::Config - > merge ( $ self - > print - > config , $ self - > config_overrides ) ) ;
}
2012-04-29 10:51:20 +00:00
sub layer_count {
my $ self = shift ;
return scalar @ { $ self - > layers } ;
}
2013-05-19 09:35:41 +00:00
sub bounding_box {
my $ self = shift ;
# since the object is aligned to origin, bounding box coincides with size
2013-08-26 21:27:51 +00:00
return Slic3r::Geometry::BoundingBox - > new_from_points ( [ map Slic3r::Point - > new ( @$ _ [ X , Y ] ) , [ 0 , 0 ] , $ self - > size ] ) ;
2013-05-19 09:35:41 +00:00
}
2013-12-15 15:17:12 +00:00
# this should be idempotent
2012-04-30 12:56:01 +00:00
sub slice {
my $ self = shift ;
2012-05-28 17:58:27 +00:00
my % params = @ _ ;
2012-04-30 12:56:01 +00:00
2013-12-15 15:17:12 +00: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 ) {
$ print_z += $ Slic3r:: Config - > get_value ( 'first_layer_height' ) ;
$ print_z += $ Slic3r:: Config - > layer_height * ( $ self - > config - > raft_layers - 1 ) ;
$ 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 )
? $ Slic3r:: Config - > get_value ( 'first_layer_height' )
: $ Slic3r:: Config - > layer_height ;
# 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 18:37:15 +00: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 12:56:01 +00:00
# process facets
2013-12-17 23:13:41 +00: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 15:34:31 +00:00
$ mesh - > translate ( ( map unscale ( - $ _ ) , @ { $ self - > _copies_shift } ) , - $ self - > model_object - > bounding_box - > z_min ) ;
2013-12-13 11:18:30 +00:00
2013-09-09 16:21:10 +00:00
{
2013-09-11 09:55:08 +00:00
my $ loops = $ mesh - > slice ( [ map $ _ - > slice_z , @ { $ self - > layers } ] ) ;
for my $ layer_id ( 0 .. $#$ loops ) {
2013-09-09 16:21:10 +00:00
my $ layerm = $ self - > layers - > [ $ layer_id ] - > regions - > [ $ region_id ] ;
2013-09-11 09:55:08 +00:00
$ layerm - > make_surfaces ( $ loops - > [ $ layer_id ] ) ;
2012-04-30 12:56:01 +00:00
}
2013-09-09 16:21:10 +00:00
# TODO: read slicing_errors
}
2012-04-30 12:56:01 +00:00
}
2013-03-24 14:26:55 +00:00
# remove last layer(s) if empty
2013-06-23 10:26:40 +00:00
pop @ { $ self - > layers } while @ { $ self - > layers } && ( ! map @ { $ _ - > slices } , @ { $ self - > layers - > [ - 1 ] - > regions } ) ;
2012-04-30 12:56:01 +00:00
foreach my $ layer ( @ { $ self - > layers } ) {
2012-09-23 00:52:31 +00:00
# merge all regions' slices to get islands
2012-09-22 17:04:36 +00:00
$ layer - > make_slices ;
2012-04-30 12:56:01 +00: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 00:52:31 +00:00
foreach my $ region_id ( 0 .. $# { $ layer - > regions } ) {
my $ layerm = $ layer - > region ( $ region_id ) ;
2012-09-22 17:04:36 +00:00
my ( @ upper_surfaces , @ lower_surfaces ) ;
for ( my $ j = $ i + 1 ; $ j <= $# { $ self - > layers } ; $ j + + ) {
if ( ! $ self - > layers - > [ $ j ] - > slicing_errors ) {
2012-09-23 00:52:31 +00:00
@ upper_surfaces = @ { $ self - > layers - > [ $ j ] - > region ( $ region_id ) - > slices } ;
2012-09-22 17:04:36 +00:00
last ;
}
2012-04-30 12:56:01 +00:00
}
2012-09-22 17:04:36 +00:00
for ( my $ j = $ i - 1 ; $ j >= 0 ; $ j - - ) {
if ( ! $ self - > layers - > [ $ j ] - > slicing_errors ) {
2012-09-23 00:52:31 +00:00
@ lower_surfaces = @ { $ self - > layers - > [ $ j ] - > region ( $ region_id ) - > slices } ;
2012-09-22 17:04:36 +00:00
last ;
}
2012-04-30 12:56:01 +00:00
}
2012-09-22 17:04:36 +00:00
my $ union = union_ex ( [
map $ _ - > expolygon - > contour , @ upper_surfaces , @ lower_surfaces ,
] ) ;
my $ diff = diff_ex (
[ map @$ _ , @$ union ] ,
2013-08-26 21:09:18 +00:00
[ map @ { $ _ - > expolygon - > holes } , @ upper_surfaces , @ lower_surfaces , ] ,
2012-09-22 17:04:36 +00:00
) ;
2013-07-14 13:03:45 +00:00
$ layerm - > slices - > clear ;
$ layerm - > slices - > append (
map Slic3r::Surface - > new
( expolygon = > $ _ , surface_type = > S_TYPE_INTERNAL ) ,
@$ diff
) ;
2012-04-30 12:56:01 +00:00
}
2012-09-22 17:04:36 +00:00
2012-09-23 00:52:31 +00:00
# update layer slices after repairing the single regions
2012-09-22 17:04:36 +00:00
$ layer - > make_slices ;
2012-04-30 12:56:01 +00:00
}
# remove empty layers from bottom
2013-08-25 12:37:50 +00:00
my $ first_object_layer_id = $ self - > config - > raft_layers ;
2013-01-28 13:12:01 +00:00
while ( @ { $ self - > layers } && ! @ { $ self - > layers - > [ $ first_object_layer_id ] - > slices } && ! map @ { $ _ - > thin_walls } , @ { $ self - > layers - > [ $ first_object_layer_id ] - > regions } ) {
splice @ { $ self - > layers } , $ first_object_layer_id , 1 ;
for ( my $ i = $ first_object_layer_id ; $ i <= $# { $ self - > layers } ; $ i + + ) {
2012-04-30 12:56:01 +00:00
$ self - > layers - > [ $ i ] - > id ( $ i ) ;
}
}
}
2012-05-05 14:36:10 +00: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 10:37:34 +00: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 12:37:50 +00:00
if ( $ self - > config - > extra_perimeters && $ self - > config - > perimeters > 0 && $ self - > config - > fill_density > 0 ) {
2012-09-23 00:52:31 +00:00
for my $ region_id ( 0 .. ( $ self - > print - > regions_count - 1 ) ) {
2012-09-22 17:04:36 +00:00
for my $ layer_id ( 0 .. $ self - > layer_count - 2 ) {
2012-09-23 01:03:08 +00:00
my $ layerm = $ self - > layers - > [ $ layer_id ] - > regions - > [ $ region_id ] ;
my $ upper_layerm = $ self - > layers - > [ $ layer_id + 1 ] - > regions - > [ $ region_id ] ;
2013-02-24 15:40:14 +00:00
my $ perimeter_spacing = $ layerm - > perimeter_flow - > scaled_spacing ;
2012-09-22 17:04:36 +00:00
2013-02-24 15:40:14 +00:00
my $ overlap = $ perimeter_spacing ; # one perimeter
2012-09-22 17:04:36 +00:00
2013-05-31 12:30:07 +00:00
my $ diff = diff (
2013-08-25 17:59:42 +00:00
offset ( [ map @ { $ _ - > expolygon } , @ { $ layerm - > slices } ] , - ( $ self - > config - > perimeters * $ perimeter_spacing ) ) ,
2013-07-16 22:48:29 +00:00
offset ( [ map @ { $ _ - > expolygon } , @ { $ upper_layerm - > slices } ] , - $ overlap ) ,
2012-06-23 15:10:30 +00:00
) ;
2013-03-10 18:08:36 +00:00
next if ! @$ diff ;
# if we need more perimeters, $diff should contain a narrow region that we can collapse
2013-09-17 18:18:54 +00: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 12:30:07 +00:00
$ diff = diff (
$ diff ,
2013-09-17 18:18:54 +00:00
offset2 ( $ diff , - $ perimeter_spacing , + $ perimeter_spacing , CLIPPER_OFFSET_SCALE , JT_MITER , 5 ) ,
2013-05-31 12:23:42 +00:00
1 ,
2013-03-10 18:08:36 +00:00
) ;
next if ! @$ diff ;
# diff contains the collapsed area
2012-09-22 17:04:36 +00:00
foreach my $ slice ( @ { $ layerm - > slices } ) {
2013-03-11 13:23:16 +00:00
my $ extra_perimeters = 0 ;
2012-09-22 17:04:36 +00:00
CYCLE: while ( 1 ) {
# compute polygons representing the thickness of the hypotetical new internal perimeter
# of our slice
2013-03-11 13:23:16 +00:00
$ extra_perimeters + + ;
2013-05-31 12:30:07 +00:00
my $ hypothetical_perimeter = diff (
2013-08-25 17:59:42 +00: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 18:08:36 +00:00
) ;
last CYCLE if ! @$ hypothetical_perimeter ; # no extra perimeter is possible
2013-02-26 19:00:05 +00:00
2013-03-10 18:08:36 +00:00
# only add the perimeter if there's an intersection with the collapsed area
2013-05-31 12:30:07 +00:00
last CYCLE if ! @ { intersection ( $ diff , $ hypothetical_perimeter ) } ;
2013-11-20 14:38:51 +00:00
2012-09-22 17:04:36 +00:00
Slic3r:: debugf " adding one more perimeter at layer %d\n" , $ layer_id ;
2013-03-11 13:23:16 +00:00
$ slice - > extra_perimeters ( $ extra_perimeters ) ;
2012-06-23 15:10:30 +00:00
}
2012-05-05 14:36:10 +00:00
}
}
}
}
2013-01-31 14:44:55 +00:00
Slic3r:: parallelize (
items = > sub { 0 .. ( $ self - > layer_count - 1 ) } ,
thread_cb = > sub {
my $ q = shift ;
while ( defined ( my $ layer_id = $ q - > dequeue ) ) {
2013-07-18 20:29:12 +00:00
$ self - > layers - > [ $ layer_id ] - > make_perimeters ;
2013-01-31 14:44:55 +00:00
}
} ,
2013-07-18 20:29:12 +00:00
collect_cb = > sub { } ,
2013-01-31 14:44:55 +00:00
no_threads_cb = > sub {
$ _ - > make_perimeters for @ { $ self - > layers } ;
} ,
) ;
2012-05-05 14:36:10 +00:00
}
2012-04-29 10:51:20 +00:00
sub detect_surfaces_type {
my $ self = shift ;
Slic3r:: debugf "Detecting solid surfaces...\n" ;
2012-09-23 00:52:31 +00:00
for my $ region_id ( 0 .. ( $ self - > print - > regions_count - 1 ) ) {
2013-02-26 19:52:13 +00:00
for my $ i ( 0 .. ( $ self - > layer_count - 1 ) ) {
2012-09-23 00:52:31 +00:00
my $ layerm = $ self - > layers - > [ $ i ] - > regions - > [ $ region_id ] ;
2013-09-16 10:36:04 +00: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 17:04:36 +00:00
2012-09-23 00:52:31 +00:00
# comparison happens against the *full* slices (considering all regions)
2012-09-22 17:04:36 +00: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-06 22:42:56 +00:00
@ top = $ difference - > (
2013-02-26 19:52:13 +00:00
[ map $ _ - > expolygon , @ { $ layerm - > slices } ] ,
$ upper_layer - > slices ,
S_TYPE_TOP ,
) ;
2012-09-22 17:04:36 +00:00
} else {
# if no upper layer, all surfaces of this one are solid
2013-07-15 13:26:56 +00:00
# we clone surfaces because we're going to clear the slices collection
@ top = map $ _ - > clone , @ { $ layerm - > slices } ;
2012-09-22 17:04:36 +00: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 19:52:13 +00:00
# lower layer's slices are already Surface objects
2013-09-06 22:42:56 +00:00
@ bottom = $ difference - > (
2013-02-26 19:52:13 +00:00
[ map $ _ - > expolygon , @ { $ layerm - > slices } ] ,
$ lower_layer - > slices ,
S_TYPE_BOTTOM ,
) ;
2012-09-22 17:04:36 +00:00
} else {
# if no lower layer, all surfaces of this one are solid
2013-07-15 13:26:56 +00:00
# we clone surfaces because we're going to clear the slices collection
@ bottom = map $ _ - > clone , @ { $ layerm - > slices } ;
2012-09-22 17:04:36 +00: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 10:36:04 +00:00
Slic3r:: debugf " layer %d contains %d membrane(s)\n" , $ layerm - > id , scalar ( @$ overlapping )
if $ Slic3r:: debug ;
2013-09-06 22:42:56 +00:00
@ top = $ difference - > ( [ map $ _ - > expolygon , @ top ] , $ overlapping , S_TYPE_TOP ) ;
2012-09-22 17:04:36 +00:00
}
# find internal surfaces (difference between top/bottom surfaces and others)
2013-09-06 22:42:56 +00:00
@ internal = $ difference - > (
2013-02-26 19:52:13 +00:00
[ map $ _ - > expolygon , @ { $ layerm - > slices } ] ,
[ map $ _ - > expolygon , @ top , @ bottom ] ,
S_TYPE_INTERNAL ,
) ;
2012-09-22 17:04:36 +00:00
# save surfaces to layer
2013-07-14 13:03:45 +00:00
$ layerm - > slices - > clear ;
$ layerm - > slices - > append ( @ bottom , @ top , @ internal ) ;
2012-09-22 17:04:36 +00:00
Slic3r:: debugf " layer %d has %d bottom, %d top and %d internal surfaces\n" ,
2013-09-16 10:36:04 +00:00
$ layerm - > id , scalar ( @ bottom ) , scalar ( @ top ) , scalar ( @ internal ) if $ Slic3r:: debug ;
2012-04-29 10:51:20 +00:00
}
2012-09-22 17:04:36 +00:00
# clip surfaces to the fill boundaries
foreach my $ layer ( @ { $ self - > layers } ) {
2012-09-23 00:52:31 +00:00
my $ layerm = $ layer - > regions - > [ $ region_id ] ;
2013-07-15 13:26:56 +00:00
my $ fill_boundaries = [ map $ _ - > clone - > p , @ { $ layerm - > fill_surfaces } ] ;
2013-07-14 12:56:43 +00:00
$ layerm - > fill_surfaces - > clear ;
2012-09-22 17:04:36 +00:00
foreach my $ surface ( @ { $ layerm - > slices } ) {
my $ intersection = intersection_ex (
[ $ surface - > p ] ,
$ fill_boundaries ,
) ;
2013-07-14 12:56:43 +00:00
$ layerm - > fill_surfaces - > append ( map Slic3r::Surface - > new
2012-09-22 17:04:36 +00:00
( expolygon = > $ _ , surface_type = > $ surface - > surface_type ) ,
2013-07-14 12:56:43 +00:00
@$ intersection ) ;
2012-09-22 17:04:36 +00:00
}
2012-04-29 10:51:20 +00:00
}
}
}
2013-02-09 22:36:32 +00:00
sub clip_fill_surfaces {
my $ self = shift ;
2013-08-25 12:37:50 +00:00
return unless $ self - > config - > infill_only_where_needed ;
2013-02-09 22:36:32 +00:00
# We only want infill under ceilings; this is almost like an
# internal support material.
my $ additional_margin = scale 3 ;
2013-09-06 22:02:58 +00:00
my $ overhangs = [] ; # arrayref of polygons
2013-02-09 22:36:32 +00:00
for my $ layer_id ( reverse 0 .. $# { $ self - > layers } ) {
my $ layer = $ self - > layers - > [ $ layer_id ] ;
2013-09-06 22:02:58 +00:00
my @ layer_internal = ( ) ;
my @ new_internal = ( ) ;
2013-02-09 22:36:32 +00:00
# clip this layer's internal surfaces to @overhangs
foreach my $ layerm ( @ { $ layer - > regions } ) {
2013-09-06 22:02:58 +00: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 12:56:43 +00:00
$ layerm - > fill_surfaces - > clear ;
2013-09-06 22:02:58 +00:00
$ layerm - > fill_surfaces - > append ( @ new , @ other ) ;
2013-02-09 22:36:32 +00:00
}
2013-09-06 22:02:58 +00:00
# get this layer's overhangs defined as the full slice minus the internal infill
# (thus we also consider perimeters)
2013-02-09 22:36:32 +00:00
if ( $ layer_id > 0 ) {
2013-09-06 22:02:58 +00:00
my $ solid = diff (
[ map @$ _ , @ { $ layer - > slices } ] ,
\ @ layer_internal ,
) ;
$ overhangs = offset ( $ solid , + $ additional_margin ) ;
push @$ overhangs , @ new_internal ; # propagate upper overhangs
2013-02-09 22:36:32 +00:00
}
}
}
2013-02-23 20:39:13 +00:00
sub bridge_over_infill {
my $ self = shift ;
2013-08-25 12:37:50 +00:00
return if $ self - > config - > fill_density == 1 ;
2013-02-23 20:39:13 +00: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 16:36:38 +00: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 20:39:13 +00: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 13:26:56 +00:00
my @ new_surfaces = map $ _ - > clone , grep $ _ - > surface_type != S_TYPE_INTERNALSOLID , @ { $ layerm - > fill_surfaces } ;
2013-02-23 20:39:13 +00: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 18:33:06 +00:00
1 ,
2013-02-23 20:39:13 +00:00
) } ;
2013-07-14 12:56:43 +00:00
$ layerm - > fill_surfaces - > clear ;
$ layerm - > fill_surfaces - > append ( @ new_surfaces ) ;
2013-02-23 20:39:13 +00:00
}
# exclude infill from the layers below if needed
# see discussion at https://github.com/alexrj/Slic3r/issues/240
2013-03-18 12:32:19 +00:00
# Update: do not exclude any infill. Sparse infill is able to absorb the excess material.
if ( 0 ) {
2013-02-27 09:43:50 +00:00
my $ excess = $ layerm - > extruders - > { infill } - > bridge_flow - > width - $ layerm - > height ;
2013-02-23 20:39:13 +00: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 17:15:59 +00:00
foreach my $ group ( @ { $ lower_layerm - > fill_surfaces - > group } ) {
2013-03-29 18:18:06 +00:00
push @ new_surfaces , map $ group - > [ 0 ] - > clone ( expolygon = > $ _ ) ,
@ { diff_ex (
[ map $ _ - > p , @$ group ] ,
[ map @$ _ , @$ to_bridge ] ,
) } ;
2013-03-13 00:03:54 +00: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 20:39:13 +00:00
}
2013-07-14 12:56:43 +00:00
$ lower_layerm - > fill_surfaces - > clear ;
$ lower_layerm - > fill_surfaces - > append ( @ new_surfaces ) ;
2013-02-23 20:39:13 +00:00
}
$ excess -= $ self - > layers - > [ $ i ] - > height ;
}
}
}
}
}
2013-11-11 19:37:06 +00: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 10:51:20 +00:00
sub discover_horizontal_shells {
my $ self = shift ;
Slic3r:: debugf "==> DISCOVERING HORIZONTAL SHELLS\n" ;
2012-09-23 00:52:31 +00:00
for my $ region_id ( 0 .. ( $ self - > print - > regions_count - 1 ) ) {
2012-09-22 17:04:36 +00:00
for ( my $ i = 0 ; $ i < $ self - > layer_count ; $ i + + ) {
2012-09-23 00:52:31 +00:00
my $ layerm = $ self - > layers - > [ $ i ] - > regions - > [ $ region_id ] ;
2012-09-28 13:46:29 +00:00
2013-08-25 12:37:50 +00:00
if ( $ self - > config - > solid_infill_every_layers && $ self - > config - > fill_density > 0
&& ( $ i % $ self - > config - > solid_infill_every_layers ) == 0 ) {
2013-09-06 16:36:38 +00:00
$ _ - > surface_type ( S_TYPE_INTERNALSOLID ) for @ { $ layerm - > fill_surfaces - > filter_by_type ( S_TYPE_INTERNAL ) } ;
2012-09-28 13:46:29 +00:00
}
2013-07-28 08:56:41 +00:00
EXTERNAL: foreach my $ type ( S_TYPE_TOP , S_TYPE_BOTTOM ) {
2012-12-21 19:25:48 +00:00
# find slices of current type for current layer
2013-08-13 07:45:33 +00: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 17:24:16 +00: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 17:37:01 +00:00
next if ! @$ solid ;
2013-07-15 10:14:22 +00:00
Slic3r:: debugf "Layer %d has %s surfaces\n" , $ i , ( $ type == S_TYPE_TOP ) ? 'top' : 'bottom' ;
2012-04-29 10:51:20 +00:00
2012-10-28 11:52:44 +00:00
my $ solid_layers = ( $ type == S_TYPE_TOP )
2013-08-25 12:37:50 +00:00
? $ self - > config - > top_solid_layers
: $ self - > config - > bottom_solid_layers ;
2013-08-08 00:10:34 +00:00
NEIGHBOR: for ( my $ n = ( $ type == S_TYPE_TOP ) ? $ i - 1 : $ i + 1 ;
2012-10-25 10:21:04 +00:00
abs ( $ n - $ i ) <= $ solid_layers - 1 ;
2013-07-15 10:14:22 +00:00
( $ type == S_TYPE_TOP ) ? $ n - - : $ n + + ) {
2012-09-22 17:04:36 +00:00
next if $ n < 0 || $ n >= $ self - > layer_count ;
Slic3r:: debugf " looking for neighbors on layer %d...\n" , $ n ;
2013-09-09 15:10:54 +00: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 17:04:36 +00:00
# find intersection between neighbor and current layer's surfaces
# intersections have contours and holes
2013-07-27 17:41:36 +00: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 17:21:38 +00:00
my $ new_internal_solid = $ solid = intersection (
$ solid ,
2013-07-15 10:14:22 +00:00
[ map $ _ - > p , grep { ( $ _ - > surface_type == S_TYPE_INTERNAL ) || ( $ _ - > surface_type == S_TYPE_INTERNALSOLID ) } @ neighbor_fill_surfaces ] ,
2013-07-16 22:29:09 +00:00
1 ,
2012-09-22 17:04:36 +00:00
) ;
2013-07-28 08:56:41 +00:00
next EXTERNAL if ! @$ new_internal_solid ;
2012-09-22 17:04:36 +00:00
2013-03-11 17:37:01 +00: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 15:43:40 +00: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-16 23:02:31 +00:00
my $ margin = 3 * $ layerm - > solid_infill_flow - > scaled_width ; # require at least this size
2013-09-06 17:21:38 +00:00
my $ too_narrow = diff (
$ new_internal_solid ,
offset2 ( $ new_internal_solid , - $ margin , + $ margin , CLIPPER_OFFSET_SCALE , JT_MITER , 5 ) ,
2013-03-18 16:55:16 +00:00
1 ,
2013-03-11 17:37:01 +00:00
) ;
2013-07-28 08:56:41 +00:00
# if some parts are going to collapse, use a different strategy according to fill density
2013-03-11 17:37:01 +00:00
if ( @$ too_narrow ) {
2013-08-25 12:37:50 +00:00
if ( $ self - > config - > fill_density > 0 ) {
2013-07-28 08:56:41 +00: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 17:26:58 +00:00
my @ grown = @ { intersection (
2013-09-06 17:21:38 +00:00
offset ( $ too_narrow , + $ margin ) ,
2013-07-28 08:56:41 +00:00
[ map $ _ - > p , @ neighbor_fill_surfaces ] ,
) } ;
2013-09-06 22:28:53 +00:00
$ new_internal_solid = $ solid = [ @ grown , @$ new_internal_solid ] ;
2013-07-28 08:56:41 +00:00
} else {
# if we're printing a hollow object, we discard such small parts
2013-09-06 17:21:38 +00:00
$ new_internal_solid = $ solid = diff (
$ new_internal_solid ,
$ too_narrow ,
2013-07-28 08:56:41 +00:00
) ;
}
2013-03-11 17:37:01 +00:00
}
}
2012-09-22 17:04:36 +00: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 17:21:38 +00:00
@$ new_internal_solid ,
2012-09-22 17:04:36 +00:00
] ) ;
2013-03-07 14:47:32 +00:00
# subtract intersections from layer surfaces to get resulting internal surfaces
2012-09-22 17:04:36 +00:00
my $ internal = diff_ex (
[ map $ _ - > p , grep $ _ - > surface_type == S_TYPE_INTERNAL , @ neighbor_fill_surfaces ] ,
[ map @$ _ , @$ internal_solid ] ,
2012-05-01 16:51:47 +00:00
1 ,
2012-04-29 10:51:20 +00:00
) ;
2012-09-22 17:04:36 +00:00
Slic3r:: debugf " %d internal-solid and %d internal surfaces found\n" ,
scalar ( @$ internal_solid ) , scalar ( @$ internal ) ;
2013-03-07 14:47:32 +00:00
# assign resulting internal surfaces to layer
2013-07-15 10:14:22 +00:00
$ neighbor_fill_surfaces - > clear ;
$ neighbor_fill_surfaces - > append ( map Slic3r::Surface - > new
( expolygon = > $ _ , surface_type = > S_TYPE_INTERNAL ) , @$ internal ) ;
2012-09-22 17:04:36 +00:00
# assign new internal-solid surfaces to layer
2013-07-15 10:14:22 +00:00
$ neighbor_fill_surfaces - > append ( map Slic3r::Surface - > new
( expolygon = > $ _ , surface_type = > S_TYPE_INTERNALSOLID ) , @$ internal_solid ) ;
2012-09-22 17:04:36 +00:00
# assign top and bottom surfaces to layer
2013-11-23 17:15:59 +00: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 17:04:36 +00:00
my $ solid_surfaces = diff_ex (
[ map $ _ - > p , @$ s ] ,
[ map @$ _ , @$ internal_solid , @$ internal ] ,
1 ,
) ;
2013-07-15 10:14:22 +00:00
$ neighbor_fill_surfaces - > append ( map $ s - > [ 0 ] - > clone ( expolygon = > $ _ ) , @$ solid_surfaces ) ;
2012-09-22 17:04:36 +00:00
}
2012-04-29 10:51:20 +00:00
}
}
2012-12-23 19:20:17 +00:00
}
2012-04-29 10:51:20 +00:00
}
}
# combine fill surfaces across layers
2012-07-22 18:48:38 +00:00
sub combine_infill {
2012-04-29 10:51:20 +00:00
my $ self = shift ;
2013-08-25 12:37:50 +00:00
return unless $ self - > config - > infill_every_layers > 1 && $ self - > config - > fill_density > 0 ;
my $ every = $ self - > config - > infill_every_layers ;
2012-04-29 10:51:20 +00:00
2013-02-10 11:40:43 +00:00
my $ layer_count = $ self - > layer_count ;
2013-03-10 11:08:18 +00:00
my @ layer_heights = map $ self - > layers - > [ $ _ ] - > height , 0 .. $ layer_count - 1 ;
2012-09-23 00:52:31 +00:00
for my $ region_id ( 0 .. ( $ self - > print - > regions_count - 1 ) ) {
2013-02-22 15:24:24 +00:00
# limit the number of combined layers to the maximum height allowed by this regions' nozzle
2013-03-10 11:08:18 +00:00
my $ nozzle_diameter = $ self - > print - > regions - > [ $ region_id ] - > extruders - > { infill } - > nozzle_diameter ;
# define the combinations
2013-03-17 00:10:40 +00:00
my @ combine = ( ) ; # layer_id => thickness in layers
2013-03-10 11:08:18 +00: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 15:24:24 +00:00
2013-02-10 11:40:43 +00:00
# skip bottom layer
2013-03-10 11:08:18 +00:00
for my $ layer_id ( 1 .. $# combine ) {
next unless ( $ combine [ $ layer_id ] // 1 ) > 1 ;
2013-02-10 11:40:43 +00:00
my @ layerms = map $ self - > layers - > [ $ _ ] - > regions - > [ $ region_id ] ,
2013-03-10 11:08:18 +00:00
( $ layer_id - ( $ combine [ $ layer_id ] - 1 ) .. $ layer_id ) ;
2012-04-29 10:51:20 +00:00
2013-06-23 16:21:47 +00:00
# only combine internal infill
for my $ type ( S_TYPE_INTERNAL ) {
2013-02-10 11:40:43 +00:00
# we need to perform a multi-layer intersection, so let's split it in pairs
2012-09-22 17:04:36 +00:00
2013-02-10 11:40:43 +00:00
# initialize the intersection with the candidates of the lowest layer
2013-09-06 16:36:38 +00:00
my $ intersection = [ map $ _ - > expolygon , @ { $ layerms [ 0 ] - > fill_surfaces - > filter_by_type ( $ type ) } ] ;
2012-09-22 17:04:36 +00:00
2013-02-10 11:40:43 +00: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 16:36:38 +00:00
[ map @ { $ _ - > expolygon } , @ { $ layerm - > fill_surfaces - > filter_by_type ( $ type ) } ] ,
2013-02-10 11:40:43 +00:00
) ;
}
2012-09-22 17:04:36 +00:00
2013-02-18 10:52:47 +00:00
my $ area_threshold = $ layerms [ 0 ] - > infill_area_threshold ;
2012-09-22 17:04:36 +00:00
@$ intersection = grep $ _ - > area > $ area_threshold , @$ intersection ;
next if ! @$ intersection ;
2013-02-10 11:40:43 +00:00
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 ;
2012-04-29 10:51:20 +00:00
2013-02-10 11:40:43 +00:00
# $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 15:53:47 +00:00
2013-07-16 22:48:29 +00:00
my @ intersection_with_clearance = map @ { $ _ - > offset (
2013-03-16 23:57:58 +00: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 12:37:50 +00:00
+ ( ( $ type == S_TYPE_INTERNALSOLID || $ self - > config - > fill_pattern =~ /(rectilinear|honeycomb)/ )
2013-03-16 23:57:58 +00:00
? $ layerms [ - 1 ] - > solid_infill_flow - > scaled_width * & Slic3r:: INFILL_OVERLAP_OVER_SPACING
: 0 )
2013-07-16 22:48:29 +00:00
) } , @$ intersection ;
2013-03-16 23:57:58 +00:00
2013-02-16 15:53:47 +00:00
2013-02-10 11:40:43 +00:00
foreach my $ layerm ( @ layerms ) {
2013-09-06 16:36:38 +00:00
my @ this_type = @ { $ layerm - > fill_surfaces - > filter_by_type ( $ type ) } ;
2013-07-15 13:26:56 +00:00
my @ other_types = map $ _ - > clone , grep $ _ - > surface_type != $ type , @ { $ layerm - > fill_surfaces } ;
2012-09-22 17:04:36 +00:00
2013-03-13 00:03:54 +00:00
my @ new_this_type = map Slic3r::Surface - > new ( expolygon = > $ _ , surface_type = > $ type ) ,
2013-02-10 11:40:43 +00:00
@ { diff_ex (
2013-09-06 16:36:38 +00:00
[ map $ _ - > p , @ this_type ] ,
2013-02-16 15:53:47 +00:00
[ @ intersection_with_clearance ] ,
2013-02-10 11:40:43 +00:00
) } ;
# apply surfaces back with adjusted depth to the uppermost layer
if ( $ layerm - > id == $ layer_id ) {
2013-03-13 00:03:54 +00:00
push @ new_this_type ,
2013-03-17 00:10:40 +00:00
map Slic3r::Surface - > new (
expolygon = > $ _ ,
surface_type = > $ type ,
thickness = > sum ( map $ _ - > height , @ layerms ) ,
thickness_layers = > scalar ( @ layerms ) ,
) ,
2013-02-10 11:40:43 +00:00
@$ intersection ;
2013-03-13 00:03:54 +00: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 17:04:36 +00:00
}
2013-02-10 11:40:43 +00:00
2013-07-14 12:56:43 +00:00
$ layerm - > fill_surfaces - > clear ;
$ layerm - > fill_surfaces - > append ( @ new_this_type , @ other_types ) ;
2012-04-29 10:51:20 +00:00
}
}
}
}
}
sub generate_support_material {
my $ self = shift ;
2013-09-17 21:51:30 +00:00
return unless ( $ self - > config - > support_material || $ self - > config - > raft_layers > 0 )
&& $ self - > layer_count >= 2 ;
2012-04-29 10:51:20 +00:00
2013-10-27 09:19:26 +00:00
Slic3r::Print:: SupportMaterial
- > new ( config = > $ self - > config , flow = > $ self - > print - > support_material_flow )
- > generate ( $ self ) ;
2012-04-29 10:51:20 +00:00
}
1 ;