2012-04-29 10:51:20 +00:00
package Slic3r::Print::Object ;
2016-09-13 09:24:55 +00:00
# extends c++ class Slic3r::PrintObject (Print.xsp)
2014-06-10 14:01:57 +00:00
use strict ;
use warnings ;
2012-04-29 10:51:20 +00:00
2013-07-29 18:49:54 +00:00
use List::Util qw( min max sum first ) ;
2013-12-31 13:33:03 +00:00
use Slic3r::Flow ':roles' ;
2016-03-13 11:24:03 +00:00
use Slic3r::Geometry qw( X Y Z PI scale unscale chained_path epsilon ) ;
2013-07-29 18:49:54 +00:00
use Slic3r::Geometry::Clipper qw( diff diff_ex intersection intersection_ex union union_ex
2015-02-15 20:58:14 +00:00
offset offset_ex offset2 offset2_ex intersection_ppl CLIPPER_OFFSET_SCALE JT_MITER ) ;
2013-12-19 17:54:24 +00:00
use Slic3r::Print::State ':steps' ;
2012-05-19 14:04:33 +00:00
use Slic3r::Surface ':types' ;
2012-04-29 10:51:20 +00:00
2016-09-26 11:56:24 +00:00
# If enabled, phases of prepare_infill will be written into SVG files to an "out" directory.
our $ SLIC3R_DEBUG_SLICE_PROCESSING = 0 ;
2013-12-15 15:17:12 +00:00
2014-05-06 08:07:18 +00:00
sub region_volumes {
my $ self = shift ;
return [ map $ self - > get_region_volumes ( $ _ ) , 0 .. ( $ self - > region_count - 1 ) ] ;
}
2013-01-01 22:28:48 +00:00
2014-05-06 08:07:18 +00:00
sub layers {
2013-05-19 09:35:41 +00:00
my $ self = shift ;
2014-05-06 08:07:18 +00:00
return [ map $ self - > get_layer ( $ _ ) , 0 .. ( $ self - > layer_count - 1 ) ] ;
2013-05-19 09:35:41 +00:00
}
2014-05-06 08:07:18 +00:00
sub support_layers {
my $ self = shift ;
return [ map $ self - > get_support_layer ( $ _ ) , 0 .. ( $ self - > support_layer_count - 1 ) ] ;
2013-05-19 09:35:41 +00:00
}
2016-09-13 09:24:55 +00:00
# 1) Decides Z positions of the layers,
# 2) Initializes layers and their regions
# 3) Slices the object meshes
# 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
# 5) Applies size compensation (offsets the slices in XY plane)
# 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
# Resulting expolygons of layer regions are marked as Internal.
#
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 ;
2014-06-13 18:05:18 +00:00
return if $ self - > step_done ( STEP_SLICE ) ;
$ self - > set_step_started ( STEP_SLICE ) ;
$ self - > print - > status_cb - > ( 10 , "Processing triangulated mesh" ) ;
2012-04-30 12:56:01 +00:00
2013-12-15 15:17:12 +00:00
# init layers
{
2014-05-06 08:07:18 +00:00
$ self - > clear_layers ;
2013-12-15 15:17:12 +00:00
# make layers taking custom heights into account
2015-06-02 17:44:29 +00:00
my $ id = 0 ;
my $ print_z = 0 ;
my $ first_object_layer_height = - 1 ;
2014-06-11 17:58:11 +00:00
my $ first_object_layer_distance = - 1 ;
2013-12-15 15:17:12 +00:00
# add raft layers
if ( $ self - > config - > raft_layers > 0 ) {
2016-10-16 14:30:56 +00:00
# Reserve object layers for the raft. Last layer of the raft is the contact layer.
2014-01-11 22:26:48 +00:00
$ id += $ self - > config - > raft_layers ;
2016-10-16 14:30:56 +00:00
# Raise first object layer Z by the thickness of the raft itself
# plus the extra distance required by the support material logic.
#FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case.
2015-01-19 08:52:24 +00:00
my $ first_layer_height = $ self - > config - > get_value ( 'first_layer_height' ) ;
$ print_z += $ first_layer_height ;
2015-03-09 18:36:23 +00:00
2016-10-16 14:30:56 +00:00
# Use as large as possible layer height for the intermediate raft layers.
2015-03-09 18:36:23 +00:00
my $ support_material_layer_height ;
{
my @ nozzle_diameters = (
2015-03-09 19:00:55 +00:00
map $ self - > print - > config - > get_at ( 'nozzle_diameter' , $ _ ) ,
2015-06-02 17:44:29 +00:00
$ self - > config - > support_material_extruder - 1 ,
$ self - > config - > support_material_interface_extruder - 1 ,
2015-03-09 18:36:23 +00:00
) ;
$ support_material_layer_height = 0.75 * min ( @ nozzle_diameters ) ;
}
$ print_z += $ support_material_layer_height * ( $ self - > config - > raft_layers - 1 ) ;
2014-01-11 22:26:48 +00:00
2015-03-09 18:27:57 +00:00
# compute the average of all nozzles used for printing the object
2016-10-16 14:30:56 +00:00
#FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa?
2015-03-09 18:27:57 +00:00
my $ nozzle_diameter ;
{
my @ nozzle_diameters = (
map $ self - > print - > config - > get_at ( 'nozzle_diameter' , $ _ ) , @ { $ self - > print - > object_extruders }
) ;
$ nozzle_diameter = sum ( @ nozzle_diameters ) / @ nozzle_diameters ;
}
2015-06-02 17:44:29 +00:00
$ first_object_layer_distance = $ self - > _support_material - > contact_distance ( $ self - > config - > layer_height , $ nozzle_diameter ) ;
2014-01-11 22:26:48 +00:00
# force first layer print_z according to the contact distance
# (the loop below will raise print_z by such height)
2015-06-02 17:44:29 +00:00
$ first_object_layer_height = $ first_object_layer_distance - $ self - > config - > support_material_contact_distance ;
2013-12-15 15:17:12 +00:00
}
# loop until we have at least one layer and the max slice_z reaches the object height
2015-06-02 17:44:29 +00:00
my $ slice_z = 0 ;
my $ height = 0 ;
my $ max_z = unscale ( $ self - > size - > z ) ;
2014-01-11 22:26:48 +00:00
while ( ( $ slice_z - $ height ) <= $ max_z ) {
2013-12-15 15:17:12 +00:00
# assign the default height to the layer according to the general settings
$ height = ( $ id == 0 )
2013-12-24 10:29:31 +00:00
? $ self - > config - > get_value ( 'first_layer_height' )
: $ self - > config - > layer_height ;
2013-12-15 15:17:12 +00: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 ;
}
}
2014-01-11 22:26:48 +00:00
if ( $ first_object_layer_height != - 1 && ! @ { $ self - > layers } ) {
$ height = $ first_object_layer_height ;
2014-06-11 17:58:11 +00:00
$ print_z += ( $ first_object_layer_distance - $ height ) ;
2014-01-11 22:26:48 +00:00
}
2013-12-15 15:17:12 +00:00
$ 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;
2014-05-06 08:07:18 +00:00
$ self - > add_layer ( $ id , $ height , $ print_z , $ slice_z ) ;
2014-06-13 15:45:44 +00:00
if ( $ self - > layer_count >= 2 ) {
my $ lc = $ self - > layer_count ;
$ self - > get_layer ( $ lc - 2 ) - > set_upper_layer ( $ self - > get_layer ( $ lc - 1 ) ) ;
$ self - > get_layer ( $ lc - 1 ) - > set_lower_layer ( $ self - > get_layer ( $ lc - 2 ) ) ;
2013-12-15 15:17:12 +00:00
}
$ 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
2014-08-03 16:41:09 +00:00
my $ regions_count = $ self - > print - > region_count ;
2013-06-22 18:37:15 +00:00
foreach my $ layer ( @ { $ self - > layers } ) {
$ layer - > region ( $ _ ) for 0 .. ( $ regions_count - 1 ) ;
}
2014-01-07 14:40:38 +00:00
# get array of Z coordinates for slicing
my @ z = map $ _ - > slice_z , @ { $ self - > layers } ;
# slice all non-modifier volumes
2014-05-06 08:07:18 +00:00
for my $ region_id ( 0 .. ( $ self - > region_count - 1 ) ) {
2014-01-07 14:40:38 +00:00
my $ expolygons_by_layer = $ self - > _slice_region ( $ region_id , \ @ z , 0 ) ;
for my $ layer_id ( 0 .. $#$ expolygons_by_layer ) {
2014-06-13 15:45:44 +00:00
my $ layerm = $ self - > get_layer ( $ layer_id ) - > regions - > [ $ region_id ] ;
2014-01-07 14:40:38 +00:00
$ layerm - > slices - > clear ;
foreach my $ expolygon ( @ { $ expolygons_by_layer - > [ $ layer_id ] } ) {
$ layerm - > slices - > append ( Slic3r::Surface - > new (
expolygon = > $ expolygon ,
surface_type = > S_TYPE_INTERNAL ,
) ) ;
2013-12-17 23:13:41 +00:00
}
}
2014-01-07 14:40:38 +00:00
}
# then slice all modifier volumes
2014-05-06 08:07:18 +00:00
if ( $ self - > region_count > 1 ) {
for my $ region_id ( 0 .. $ self - > region_count ) {
2014-01-07 14:40:38 +00:00
my $ expolygons_by_layer = $ self - > _slice_region ( $ region_id , \ @ z , 1 ) ;
# loop through the other regions and 'steal' the slices belonging to this one
2014-05-06 08:07:18 +00:00
for my $ other_region_id ( 0 .. $ self - > region_count ) {
2014-01-07 14:40:38 +00:00
next if $ other_region_id == $ region_id ;
for my $ layer_id ( 0 .. $#$ expolygons_by_layer ) {
2014-06-13 15:45:44 +00:00
my $ layerm = $ self - > get_layer ( $ layer_id ) - > regions - > [ $ region_id ] ;
my $ other_layerm = $ self - > get_layer ( $ layer_id ) - > regions - > [ $ other_region_id ] ;
2014-07-12 08:38:19 +00:00
next if ! defined $ other_layerm ;
2014-01-07 14:40:38 +00:00
my $ other_slices = [ map $ _ - > p , @ { $ other_layerm - > slices } ] ; # Polygons
my $ my_parts = intersection_ex (
$ other_slices ,
[ map @$ _ , @ { $ expolygons_by_layer - > [ $ layer_id ] } ] ,
) ;
next if ! @$ my_parts ;
# append new parts to our region
foreach my $ expolygon ( @$ my_parts ) {
$ layerm - > slices - > append ( Slic3r::Surface - > new (
expolygon = > $ expolygon ,
surface_type = > S_TYPE_INTERNAL ,
) ) ;
}
# remove such parts from original region
$ other_layerm - > slices - > clear ;
2014-03-23 15:56:41 +00:00
$ other_layerm - > slices - > append ( Slic3r::Surface - > new (
expolygon = > $ _ ,
surface_type = > S_TYPE_INTERNAL ,
) ) for @ { diff_ex ( $ other_slices , [ map @$ _ , @$ my_parts ] ) } ;
2014-01-07 14:40:38 +00:00
}
2012-04-30 12:56:01 +00:00
}
2013-09-09 16:21:10 +00:00
}
2012-04-30 12:56:01 +00:00
}
2013-03-24 14:26:55 +00:00
# remove last layer(s) if empty
2014-05-06 08:07:18 +00:00
$ self - > delete_layer ( $ self - > layer_count - 1 )
2014-06-13 15:45:44 +00:00
while $ self - > layer_count && ( ! map @ { $ _ - > slices } , @ { $ self - > get_layer ( $ self - > layer_count - 1 ) - > regions } ) ;
2012-04-30 12:56:01 +00:00
foreach my $ layer ( @ { $ self - > layers } ) {
2014-06-10 11:28:57 +00:00
# apply size compensation
if ( $ self - > config - > xy_size_compensation != 0 ) {
my $ delta = scale ( $ self - > config - > xy_size_compensation ) ;
if ( @ { $ layer - > regions } == 1 ) {
# single region
my $ layerm = $ layer - > regions - > [ 0 ] ;
my $ slices = [ map $ _ - > p , @ { $ layerm - > slices } ] ;
$ layerm - > slices - > clear ;
$ layerm - > slices - > append ( Slic3r::Surface - > new (
expolygon = > $ _ ,
surface_type = > S_TYPE_INTERNAL ,
) ) for @ { offset_ex ( $ slices , $ delta ) } ;
} else {
if ( $ delta < 0 ) {
# multiple regions, shrinking
# we apply the offset to the combined shape, then intersect it
# with the original slices for each region
my $ slices = union ( [ map $ _ - > p , map @ { $ _ - > slices } , @ { $ layer - > regions } ] ) ;
$ slices = offset ( $ slices , $ delta ) ;
foreach my $ layerm ( @ { $ layer - > regions } ) {
my $ this_slices = intersection_ex (
$ slices ,
[ map $ _ - > p , @ { $ layerm - > slices } ] ,
) ;
$ layerm - > slices - > clear ;
$ layerm - > slices - > append ( Slic3r::Surface - > new (
expolygon = > $ _ ,
surface_type = > S_TYPE_INTERNAL ,
) ) for @$ this_slices ;
}
} else {
# multiple regions, growing
# this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap
# so we give priority to the first one and so on
for my $ i ( 0 .. $# { $ layer - > regions } ) {
my $ layerm = $ layer - > regions - > [ $ i ] ;
my $ slices = offset_ex ( [ map $ _ - > p , @ { $ layerm - > slices } ] , $ delta ) ;
if ( $ i > 0 ) {
$ slices = diff_ex (
[ map @$ _ , @$ slices ] ,
[ map $ _ - > p , map @ { $ _ - > slices } , map $ layer - > regions - > [ $ _ ] , 0 .. ( $ i - 1 ) ] , # slices of already processed regions
) ;
}
$ layerm - > slices - > clear ;
$ layerm - > slices - > append ( Slic3r::Surface - > new (
expolygon = > $ _ ,
surface_type = > S_TYPE_INTERNAL ,
) ) for @$ slices ;
}
}
}
}
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 ;
2014-05-06 08:07:18 +00:00
for my $ i ( 0 .. ( $ self - > layer_count - 1 ) ) {
2014-06-13 15:45:44 +00:00
my $ layer = $ self - > get_layer ( $ i ) ;
2012-04-30 12:56:01 +00:00
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 ;
2014-05-06 08:07:18 +00:00
foreach my $ region_id ( 0 .. ( $ layer - > region_count - 1 ) ) {
2012-09-23 00:52:31 +00:00
my $ layerm = $ layer - > region ( $ region_id ) ;
2012-09-22 17:04:36 +00:00
my ( @ upper_surfaces , @ lower_surfaces ) ;
2014-05-06 08:07:18 +00:00
for ( my $ j = $ i + 1 ; $ j < $ self - > layer_count ; $ j + + ) {
2014-06-13 15:45:44 +00:00
if ( ! $ self - > get_layer ( $ j ) - > slicing_errors ) {
@ upper_surfaces = @ { $ self - > get_layer ( $ 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 - - ) {
2014-06-13 15:45:44 +00:00
if ( ! $ self - > get_layer ( $ j ) - > slicing_errors ) {
@ lower_surfaces = @ { $ self - > get_layer ( $ 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 ;
2014-11-09 15:23:50 +00:00
$ layerm - > slices - > append ( $ _ )
for map Slic3r::Surface - > new
2013-07-14 13:03:45 +00:00
( expolygon = > $ _ , surface_type = > S_TYPE_INTERNAL ) ,
2014-11-09 15:23:50 +00:00
@$ 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
2014-06-13 15:45:44 +00:00
while ( @ { $ self - > layers } && ! @ { $ self - > get_layer ( 0 ) - > slices } ) {
2015-11-04 18:19:45 +00:00
$ self - > delete_layer ( 0 ) ;
2014-05-25 21:17:00 +00:00
for ( my $ i = 0 ; $ i <= $# { $ self - > layers } ; $ i + + ) {
2015-01-18 11:35:05 +00:00
$ self - > get_layer ( $ i ) - > set_id ( $ self - > get_layer ( $ i ) - > id - 1 ) ;
2012-04-30 12:56:01 +00:00
}
}
2013-12-19 14:23:10 +00:00
# simplify slices if required
2014-01-02 09:44:54 +00:00
if ( $ self - > print - > config - > resolution ) {
$ self - > _simplify_slices ( scale ( $ self - > print - > config - > resolution ) ) ;
2013-12-19 14:23:10 +00:00
}
2014-06-13 18:05:18 +00:00
2015-11-04 18:27:58 +00:00
die "No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"
2014-06-13 18:05:18 +00:00
if ! @ { $ self - > layers } ;
2014-07-15 17:07:38 +00:00
$ self - > set_typed_slices ( 0 ) ;
2014-06-13 18:05:18 +00:00
$ self - > set_step_done ( STEP_SLICE ) ;
2012-04-30 12:56:01 +00:00
}
2016-09-13 09:24:55 +00:00
# called from slice()
2014-01-07 14:40:38 +00:00
sub _slice_region {
my ( $ self , $ region_id , $ z , $ modifier ) = @ _ ;
2014-05-06 08:07:18 +00:00
return [] if ! @ { $ self - > get_region_volumes ( $ region_id ) } ;
2014-01-07 14:40:38 +00:00
# compose mesh
my $ mesh ;
2014-05-06 08:07:18 +00:00
foreach my $ volume_id ( @ { $ self - > get_region_volumes ( $ region_id ) } ) {
2014-01-07 14:40:38 +00:00
my $ volume = $ self - > model_object - > volumes - > [ $ volume_id ] ;
next if $ volume - > modifier && ! $ modifier ;
next if ! $ volume - > modifier && $ modifier ;
if ( defined $ mesh ) {
$ mesh - > merge ( $ volume - > mesh ) ;
} else {
$ mesh = $ volume - > mesh - > clone ;
}
}
2014-03-22 16:44:42 +00:00
return if ! defined $ mesh ;
2014-01-07 14:40:38 +00:00
# transform mesh
# we ignore the per-instance transformations currently and only
# consider the first one
$ self - > model_object - > instances - > [ 0 ] - > transform_mesh ( $ mesh , 1 ) ;
2014-12-12 21:43:04 +00:00
# align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
2014-01-07 14:40:38 +00:00
$ mesh - > translate ( ( map unscale ( - $ _ ) , @ { $ self - > _copies_shift } ) , - $ self - > model_object - > bounding_box - > z_min ) ;
# perform actual slicing
return $ mesh - > slice ( $ z ) ;
}
2012-05-05 14:36:10 +00:00
sub make_perimeters {
my $ self = shift ;
2014-06-13 18:05:18 +00:00
# prerequisites
$ self - > slice ;
return if $ self - > step_done ( STEP_PERIMETERS ) ;
$ self - > set_step_started ( STEP_PERIMETERS ) ;
$ self - > print - > status_cb - > ( 20 , "Generating perimeters" ) ;
2014-07-15 17:07:38 +00:00
# merge slices if they were split into types
if ( $ self - > typed_slices ) {
$ _ - > merge_slices for @ { $ self - > layers } ;
$ self - > set_typed_slices ( 0 ) ;
$ self - > invalidate_step ( STEP_PREPARE_INFILL ) ;
}
2012-05-05 14:36:10 +00:00
# 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
2014-08-03 16:41:09 +00:00
for my $ region_id ( 0 .. ( $ self - > print - > region_count - 1 ) ) {
2014-01-02 09:44:54 +00:00
my $ region = $ self - > print - > regions - > [ $ region_id ] ;
my $ region_perimeters = $ region - > config - > perimeters ;
2015-02-15 20:58:14 +00:00
next if ! $ region - > config - > extra_perimeters ;
next if $ region_perimeters == 0 ;
next if $ region - > config - > fill_density == 0 ;
for my $ i ( 0 .. ( $ self - > layer_count - 2 ) ) {
my $ layerm = $ self - > get_layer ( $ i ) - > get_region ( $ region_id ) ;
my $ upper_layerm = $ self - > get_layer ( $ i + 1 ) - > get_region ( $ region_id ) ;
2016-06-01 18:58:05 +00:00
my $ upper_layerm_polygons = [ map $ _ - > p , @ { $ upper_layerm - > slices } ] ;
# Filter upper layer polygons in intersection_ppl by their bounding boxes?
# my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
my $ total_loop_length = sum ( map $ _ - > length , @$ upper_layerm_polygons ) // 0 ;
2015-02-15 20:58:14 +00:00
my $ perimeter_spacing = $ layerm - > flow ( FLOW_ROLE_PERIMETER ) - > scaled_spacing ;
my $ ext_perimeter_flow = $ layerm - > flow ( FLOW_ROLE_EXTERNAL_PERIMETER ) ;
my $ ext_perimeter_width = $ ext_perimeter_flow - > scaled_width ;
my $ ext_perimeter_spacing = $ ext_perimeter_flow - > scaled_spacing ;
foreach my $ slice ( @ { $ layerm - > slices } ) {
while ( 1 ) {
# compute the total thickness of perimeters
my $ perimeters_thickness = $ ext_perimeter_width /2 + $ext_perimeter_spacing/ 2
+ ( $ region_perimeters - 1 + $ slice - > extra_perimeters ) * $ perimeter_spacing ;
# define a critical area where we don't want the upper slice to fall into
# (it should either lay over our perimeters or outside this area)
my $ critical_area_depth = $ perimeter_spacing * 1.5 ;
my $ critical_area = diff (
offset ( $ slice - > expolygon - > arrayref , - $ perimeters_thickness ) ,
offset ( $ slice - > expolygon - > arrayref , - ( $ perimeters_thickness + $ critical_area_depth ) ) ,
) ;
# check whether a portion of the upper slices falls inside the critical area
my $ intersection = intersection_ppl (
2016-06-01 18:58:05 +00:00
$ upper_layerm_polygons ,
2015-02-15 20:58:14 +00:00
$ critical_area ,
) ;
2015-02-23 23:34:43 +00:00
# only add an additional loop if at least 30% of the slice loop would benefit from it
my $ total_intersection_length = sum ( map $ _ - > length , @$ intersection ) // 0 ;
last unless $ total_intersection_length > $ total_loop_length * 0.3 ;
2015-02-15 20:58:14 +00:00
2015-02-23 23:34:43 +00:00
if ( 0 ) {
require "Slic3r/SVG.pm" ;
Slic3r::SVG:: output (
"extra.svg" ,
no_arrows = > 1 ,
expolygons = > union_ex ( $ critical_area ) ,
polylines = > [ map $ _ - > split_at_first_point , map $ _ - > p , @ { $ upper_layerm - > slices } ] ,
) ;
}
2015-02-15 20:58:14 +00:00
2015-02-23 23:34:43 +00:00
$ slice - > extra_perimeters ( $ slice - > extra_perimeters + 1 ) ;
2012-05-05 14:36:10 +00:00
}
2015-02-15 20:58:14 +00:00
Slic3r:: debugf " adding %d more perimeter(s) at layer %d\n" ,
2015-11-01 18:03:11 +00:00
$ slice - > extra_perimeters , $ layerm - > layer - > id
2015-02-15 20:58:14 +00:00
if $ slice - > extra_perimeters > 0 ;
2012-05-05 14:36:10 +00:00
}
}
}
2013-01-31 14:44:55 +00:00
Slic3r:: parallelize (
2014-01-02 09:44:54 +00:00
threads = > $ self - > print - > config - > threads ,
2014-05-06 08:07:18 +00:00
items = > sub { 0 .. ( $ self - > layer_count - 1 ) } ,
2013-01-31 14:44:55 +00:00
thread_cb = > sub {
my $ q = shift ;
2014-01-11 16:40:09 +00:00
while ( defined ( my $ i = $ q - > dequeue ) ) {
2014-06-13 15:45:44 +00:00
$ self - > get_layer ( $ i ) - > make_perimeters ;
2013-01-31 14:44:55 +00:00
}
} ,
no_threads_cb = > sub {
$ _ - > make_perimeters for @ { $ self - > layers } ;
} ,
) ;
2013-12-19 14:23:10 +00: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);
2014-06-13 18:05:18 +00:00
$ self - > set_step_done ( STEP_PERIMETERS ) ;
}
sub prepare_infill {
my ( $ self ) = @ _ ;
# prerequisites
$ self - > make_perimeters ;
return if $ self - > step_done ( STEP_PREPARE_INFILL ) ;
$ self - > set_step_started ( STEP_PREPARE_INFILL ) ;
$ self - > print - > status_cb - > ( 30 , "Preparing infill" ) ;
# this will assign a type (top/bottom/internal) to $layerm->slices
# and transform $layerm->fill_surfaces from expolygon
# to typed top/bottom/internal surfaces;
$ self - > detect_surfaces_type ;
2016-09-26 11:56:24 +00:00
# Mark the object to have the slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
2014-07-15 17:07:38 +00:00
$ self - > set_typed_slices ( 1 ) ;
2016-09-26 11:56:24 +00:00
# Decide what surfaces are to be filled.
# Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured.
# Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID.
2014-06-13 18:05:18 +00:00
$ _ - > prepare_fill_surfaces for map @ { $ _ - > regions } , @ { $ self - > layers } ;
# this will detect bridges and reverse bridges
# and rearrange top/bottom/internal surfaces
2016-09-26 11:56:24 +00:00
# It produces enlarged overlapping bridging areas.
#
# 1) S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is grown by 3mm and clipped by the total infill area. Bridges are detected. The areas may overlap.
# 2) S_TYPE_TOP is grown by 3mm and clipped by the grown bottom areas. The areas may overlap.
# 3) Clip the internal surfaces by the grown top/bottom surfaces.
# 4) Merge surfaces with the same style. This will mostly get rid of the overlaps.
#FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties.
2014-06-13 18:05:18 +00:00
$ self - > process_external_surfaces ;
2016-09-26 11:56:24 +00:00
# Add solid fills to ensure the shell vertical thickness.
$ self - > discover_vertical_shells ;
# Debugging output.
if ( $ SLIC3R_DEBUG_SLICE_PROCESSING ) {
for my $ region_id ( 0 .. ( $ self - > print - > region_count - 1 ) ) {
for ( my $ i = 0 ; $ i < $ self - > layer_count ; $ i + + ) {
my $ layerm = $ self - > get_layer ( $ i ) - > regions - > [ $ region_id ] ;
$ layerm - > export_region_slices_to_svg_debug ( "6_discover_vertical_shells-final" ) ;
$ layerm - > export_region_fill_surfaces_to_svg_debug ( "6_discover_vertical_shells-final" ) ;
} # for each layer
} # for each region
}
# Detect, which fill surfaces are near external layers.
# They will be split in internal and internal-solid surfaces.
# The purpose is to add a configurable number of solid layers to support the TOP surfaces
# and to add a configurable number of solid layers above the BOTTOM / BOTTOMBRIDGE surfaces
# to close these surfaces reliably.
#FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters?
2014-06-13 18:05:18 +00:00
$ self - > discover_horizontal_shells ;
2016-09-26 11:56:24 +00:00
if ( $ SLIC3R_DEBUG_SLICE_PROCESSING ) {
# Debugging output.
for my $ region_id ( 0 .. ( $ self - > print - > region_count - 1 ) ) {
for ( my $ i = 0 ; $ i < $ self - > layer_count ; $ i + + ) {
my $ layerm = $ self - > get_layer ( $ i ) - > regions - > [ $ region_id ] ;
$ layerm - > export_region_slices_to_svg_debug ( "7_discover_horizontal_shells-final" ) ;
$ layerm - > export_region_fill_surfaces_to_svg_debug ( "7_discover_horizontal_shells-final" ) ;
} # for each layer
} # for each region
}
# Only active if config->infill_only_where_needed. This step trims the sparse infill,
# so it acts as an internal support. It maintains all other infill types intact.
# Here the internal surfaces and perimeters have to be supported by the sparse infill.
#FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support.
# Likely the sparse infill will not be anchored correctly, so it will not work as intended.
# Also one wishes the perimeters to be supported by a full infill.
2014-06-13 18:05:18 +00:00
$ self - > clip_fill_surfaces ;
2016-09-26 11:56:24 +00:00
if ( $ SLIC3R_DEBUG_SLICE_PROCESSING ) {
# Debugging output.
for my $ region_id ( 0 .. ( $ self - > print - > region_count - 1 ) ) {
for ( my $ i = 0 ; $ i < $ self - > layer_count ; $ i + + ) {
my $ layerm = $ self - > get_layer ( $ i ) - > regions - > [ $ region_id ] ;
$ layerm - > export_region_slices_to_svg_debug ( "8_clip_surfaces-final" ) ;
$ layerm - > export_region_fill_surfaces_to_svg_debug ( "8_clip_surfaces-final" ) ;
} # for each layer
} # for each region
}
2014-06-13 18:05:18 +00:00
# the following step needs to be done before combination because it may need
# to remove only half of the combined infill
$ self - > bridge_over_infill ;
# combine fill surfaces to honor the "infill every N layers" option
$ self - > combine_infill ;
2016-09-26 11:56:24 +00:00
# Debugging output.
if ( $ SLIC3R_DEBUG_SLICE_PROCESSING ) {
for my $ region_id ( 0 .. ( $ self - > print - > region_count - 1 ) ) {
for ( my $ i = 0 ; $ i < $ self - > layer_count ; $ i + + ) {
my $ layerm = $ self - > get_layer ( $ i ) - > regions - > [ $ region_id ] ;
$ layerm - > export_region_slices_to_svg_debug ( "9_prepare_infill-final" ) ;
$ layerm - > export_region_fill_surfaces_to_svg_debug ( "9_prepare_infill-final" ) ;
} # for each layer
} # for each region
for ( my $ i = 0 ; $ i < $ self - > layer_count ; $ i + + ) {
my $ layer = $ self - > get_layer ( $ i ) ;
$ layer - > export_region_slices_to_svg_debug ( "9_prepare_infill-final" ) ;
$ layer - > export_region_fill_surfaces_to_svg_debug ( "9_prepare_infill-final" ) ;
} # for each layer
}
2014-06-13 18:05:18 +00:00
$ self - > set_step_done ( STEP_PREPARE_INFILL ) ;
}
sub infill {
my ( $ self ) = @ _ ;
# prerequisites
$ self - > prepare_infill ;
return if $ self - > step_done ( STEP_INFILL ) ;
$ self - > set_step_started ( STEP_INFILL ) ;
$ self - > print - > status_cb - > ( 70 , "Infilling layers" ) ;
Slic3r:: parallelize (
threads = > $ self - > print - > config - > threads ,
2016-02-20 19:50:40 +00:00
items = > sub { 0 .. $# { $ self - > layers } } ,
2014-06-13 18:05:18 +00:00
thread_cb = > sub {
my $ q = shift ;
2016-02-20 19:50:40 +00:00
while ( defined ( my $ i = $ q - > dequeue ) ) {
2016-11-02 09:47:00 +00:00
$ self - > get_layer ( $ i ) - > make_fills ;
2014-06-13 18:05:18 +00:00
}
} ,
no_threads_cb = > sub {
2016-02-20 19:50:40 +00:00
foreach my $ layer ( @ { $ self - > layers } ) {
2016-11-02 09:47:00 +00:00
$ layer - > make_fills ;
2014-06-13 18:05:18 +00:00
}
} ,
) ;
### we could free memory now, but this would make this step not idempotent
### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
$ self - > set_step_done ( STEP_INFILL ) ;
}
sub generate_support_material {
my $ self = shift ;
# prerequisites
$ self - > slice ;
return if $ self - > step_done ( STEP_SUPPORTMATERIAL ) ;
$ self - > set_step_started ( STEP_SUPPORTMATERIAL ) ;
$ self - > clear_support_layers ;
2014-06-13 18:18:34 +00:00
if ( ( ! $ self - > config - > support_material && $ self - > config - > raft_layers == 0 ) || scalar ( @ { $ self - > layers } ) < 2 ) {
$ self - > set_step_done ( STEP_SUPPORTMATERIAL ) ;
return ;
}
$ self - > print - > status_cb - > ( 85 , "Generating support material" ) ;
2014-06-13 18:05:18 +00:00
2015-01-19 08:52:24 +00:00
$ self - > _support_material - > generate ( $ self ) ;
$ self - > set_step_done ( STEP_SUPPORTMATERIAL ) ;
}
sub _support_material {
my ( $ self ) = @ _ ;
2014-06-13 18:05:18 +00:00
my $ first_layer_flow = Slic3r::Flow - > new_from_width (
2015-02-17 05:59:39 +00:00
width = > ( $ self - > print - > config - > first_layer_extrusion_width || $ self - > config - > support_material_extrusion_width ) ,
2014-06-13 18:05:18 +00:00
role = > FLOW_ROLE_SUPPORT_MATERIAL ,
nozzle_diameter = > $ self - > print - > config - > nozzle_diameter - > [ $ self - > config - > support_material_extruder - 1 ]
// $ self - > print - > config - > nozzle_diameter - > [ 0 ] ,
layer_height = > $ self - > config - > get_abs_value ( 'first_layer_height' ) ,
bridge_flow_ratio = > 0 ,
) ;
2016-10-16 14:30:56 +00:00
if ( 1 ) {
# Old supports, Perl implementation.
return Slic3r::Print::SupportMaterial - > new (
print_config = > $ self - > print - > config ,
object_config = > $ self - > config ,
first_layer_flow = > $ first_layer_flow ,
flow = > $ self - > support_material_flow ,
interface_flow = > $ self - > support_material_flow ( FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE ) ,
) ;
} else {
# New supports, C++ implementation.
2016-11-02 09:47:00 +00:00
return Slic3r::Print::SupportMaterial2 - > new ( $ self ) ;
2016-10-16 14:30:56 +00:00
}
2012-05-05 14:36:10 +00:00
}
2014-12-24 00:29:36 +00:00
# Idempotence of this method is guaranteed by the fact that we don't remove things from
# fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
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.
2015-02-22 23:44:51 +00:00
# proceed top-down skipping bottom layer
my $ upper_internal = [] ;
for my $ layer_id ( reverse 1 .. ( $ self - > layer_count - 1 ) ) {
my $ layer = $ self - > get_layer ( $ layer_id ) ;
my $ lower_layer = $ self - > get_layer ( $ layer_id - 1 ) ;
# detect things that we need to support
my $ overhangs = [] ; # Polygons
# we need to support any solid surface
push @$ overhangs , map $ _ - > p ,
grep $ _ - > is_solid , map @ { $ _ - > fill_surfaces } , @ { $ layer - > regions } ;
# we also need to support perimeters when there's at least one full
# unsupported loop
{
# get perimeters area as the difference between slices and fill_surfaces
my $ perimeters = diff (
[ map @$ _ , @ { $ layer - > slices } ] ,
[ map $ _ - > p , map @ { $ _ - > fill_surfaces } , @ { $ layer - > regions } ] ,
) ;
# only consider the area that is not supported by lower perimeters
$ perimeters = intersection (
$ perimeters ,
[ map $ _ - > p , map @ { $ _ - > fill_surfaces } , @ { $ lower_layer - > regions } ] ,
1 ,
) ;
# only consider perimeter areas that are at least one extrusion width thick
2016-09-26 11:56:24 +00:00
#FIXME Offset2 eats out from both sides, while the perimeters are create outside in.
#Should the $pw not be half of the current value?
2015-02-22 23:44:51 +00:00
my $ pw = min ( map $ _ - > flow ( FLOW_ROLE_PERIMETER ) - > scaled_width , @ { $ layer - > regions } ) ;
$ perimeters = offset2 ( $ perimeters , - $ pw , + $ pw ) ;
# append such thick perimeters to the areas that need support
push @$ overhangs , @$ perimeters ;
}
2013-02-09 22:36:32 +00:00
2015-02-22 23:44:51 +00:00
# find new internal infill
$ upper_internal = my $ new_internal = intersection (
[
@$ overhangs ,
@$ upper_internal ,
] ,
[
# our current internal fill boundaries
map $ _ - > p ,
grep $ _ - > surface_type == S_TYPE_INTERNAL || $ _ - > surface_type == S_TYPE_INTERNALVOID ,
map @ { $ _ - > fill_surfaces } , @ { $ lower_layer - > regions }
] ,
) ;
# apply new internal infill to regions
foreach my $ layerm ( @ { $ lower_layer - > regions } ) {
2013-09-06 22:02:58 +00:00
my ( @ internal , @ other ) = ( ) ;
foreach my $ surface ( map $ _ - > clone , @ { $ layerm - > fill_surfaces } ) {
2015-02-22 23:44:51 +00:00
if ( $ surface - > surface_type == S_TYPE_INTERNAL || $ surface - > surface_type == S_TYPE_INTERNALVOID ) {
2014-12-24 00:29:36 +00:00
push @ internal , $ surface ;
} else {
push @ other , $ surface ;
}
2013-09-06 22:02:58 +00:00
}
2015-02-22 23:44:51 +00:00
my @ new = map Slic3r::Surface - > new (
2013-09-06 22:02:58 +00:00
expolygon = > $ _ ,
surface_type = > S_TYPE_INTERNAL ,
) ,
2014-12-24 00:29:36 +00:00
@ { intersection_ex (
[ map $ _ - > p , @ internal ] ,
2015-02-22 23:44:51 +00:00
$ new_internal ,
1 ,
2014-12-24 00:29:36 +00:00
) } ;
2015-02-22 23:44:51 +00:00
push @ other , map Slic3r::Surface - > new (
2014-12-24 00:29:36 +00:00
expolygon = > $ _ ,
surface_type = > S_TYPE_INTERNALVOID ,
) ,
@ { diff_ex (
[ map $ _ - > p , @ internal ] ,
2015-02-22 23:44:51 +00:00
$ new_internal ,
1 ,
2014-12-24 00:29:36 +00:00
) } ;
2015-02-22 23:55:00 +00:00
# If there are voids it means that our internal infill is not adjacent to
# perimeters. In this case it would be nice to add a loop around infill to
# make it more robust and nicer. TODO.
2013-07-14 12:56:43 +00:00
$ layerm - > fill_surfaces - > clear ;
2014-11-09 15:23:50 +00:00
$ layerm - > fill_surfaces - > append ( $ _ ) for ( @ new , @ other ) ;
2016-09-26 11:56:24 +00:00
if ( $ SLIC3R_DEBUG_SLICE_PROCESSING ) {
$ layerm - > export_region_fill_surfaces_to_svg_debug ( "6_clip_fill_surfaces" ) ;
}
2013-02-09 22:36:32 +00:00
}
}
}
2012-04-29 10:51:20 +00:00
sub discover_horizontal_shells {
my $ self = shift ;
Slic3r:: debugf "==> DISCOVERING HORIZONTAL SHELLS\n" ;
2014-08-03 16:41:09 +00:00
for my $ region_id ( 0 .. ( $ self - > print - > region_count - 1 ) ) {
2014-05-06 08:07:18 +00:00
for ( my $ i = 0 ; $ i < $ self - > layer_count ; $ i + + ) {
2014-06-13 15:45:44 +00:00
my $ layerm = $ self - > get_layer ( $ i ) - > regions - > [ $ region_id ] ;
2012-09-28 13:46:29 +00:00
2015-10-26 22:23:03 +00:00
if ( $ layerm - > region - > config - > solid_infill_every_layers && $ layerm - > region - > config - > fill_density > 0
&& ( $ i % $ layerm - > region - > config - > solid_infill_every_layers ) == 0 ) {
2016-09-26 11:56:24 +00:00
# This is the layer to put the sparse infill in. Mark S_TYPE_INTERNAL surfaces as S_TYPE_INTERNALSOLID or S_TYPE_INTERNALBRIDGE.
# If the sparse infill is not active, the internal surfaces are of type S_TYPE_INTERNAL.
2015-12-19 11:48:48 +00:00
my $ type = $ layerm - > region - > config - > fill_density == 100 ? S_TYPE_INTERNALSOLID : S_TYPE_INTERNALBRIDGE ;
$ _ - > surface_type ( $ type ) for @ { $ layerm - > fill_surfaces - > filter_by_type ( S_TYPE_INTERNAL ) } ;
2012-09-28 13:46:29 +00:00
}
2016-11-10 18:23:01 +00:00
# If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells().
next if ( $ layerm - > region - > config - > ensure_vertical_shell_thickness ) ;
2012-09-28 13:46:29 +00:00
2014-03-25 00:11:28 +00:00
EXTERNAL: foreach my $ type ( S_TYPE_TOP , S_TYPE_BOTTOM , S_TYPE_BOTTOMBRIDGE ) {
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 = [
2016-11-09 09:24:45 +00:00
# Surfaces including the area of perimeters. Everything, that is visible from the top / bottom
# (not covered by a layer above / below).
# This does not contain the areas covered by perimeters!
2013-09-17 17:24:16 +00:00
( map $ _ - > p , @ { $ layerm - > slices - > filter_by_type ( $ type ) } ) ,
2016-11-09 09:24:45 +00:00
# Infill areas (slices without the perimeters).
2013-09-17 17:24:16 +00:00
( 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 )
2015-10-26 22:23:03 +00:00
? $ layerm - > region - > config - > top_solid_layers
: $ layerm - > region - > config - > bottom_solid_layers ;
2013-08-08 00:10:34 +00:00
NEIGHBOR: for ( my $ n = ( $ type == S_TYPE_TOP ) ? $ i - 1 : $ i + 1 ;
2016-09-26 11:56:24 +00:00
abs ( $ n - $ i ) < $ solid_layers ;
( $ type == S_TYPE_TOP ) ? $ n - - : $ n + + ) {
2012-09-22 17:04:36 +00:00
2014-05-06 08:07:18 +00:00
next if $ n < 0 || $ n >= $ self - > layer_count ;
2012-09-22 17:04:36 +00:00
Slic3r:: debugf " looking for neighbors on layer %d...\n" , $ n ;
2016-09-26 11:56:24 +00:00
# Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface.
2014-06-13 15:45:44 +00:00
my $ neighbor_layerm = $ self - > get_layer ( $ n ) - > regions - > [ $ region_id ] ;
2016-09-26 11:56:24 +00:00
# Reference to the neighbour fill surfaces.
2014-02-07 00:48:47 +00:00
my $ neighbor_fill_surfaces = $ neighbor_layerm - > fill_surfaces ;
2016-09-26 11:56:24 +00:00
# Clone because we will use these surfaces even after clearing the collection.
my @ neighbor_fill_surfaces = map $ _ - > clone , @$ neighbor_fill_surfaces ;
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
2016-09-26 11:56:24 +00:00
#FIXME How does it work for S_TYPE_INTERNALBRIDGE? This is set for sparse infill. Likely this does not work.
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
2015-10-26 22:23:03 +00:00
if ( $ layerm - > region - > config - > fill_density == 0 ) {
2014-02-07 00:48:47 +00:00
# if we're printing a hollow object we discard any solid shell thinner
# than a perimeter width, since it's probably just crossing a sloping wall
# and it's not wanted in a hollow print even if it would make sense when
# obeying the solid shell count option strictly (DWIM!)
2014-06-09 19:14:48 +00:00
my $ margin = $ neighbor_layerm - > flow ( FLOW_ROLE_EXTERNAL_PERIMETER ) - > scaled_width ;
2016-11-09 09:24:45 +00:00
my $ regularized = offset2 ( $ new_internal_solid , - $ margin , + $ margin , CLIPPER_OFFSET_SCALE , JT_MITER , 5 ) ;
2014-02-07 00:48:47 +00:00
my $ too_narrow = diff (
$ new_internal_solid ,
2016-11-09 09:24:45 +00:00
$ regularized ,
2014-02-07 00:48:47 +00:00
1 ,
) ;
2016-11-09 09:24:45 +00:00
# Trim the regularized region by the original region.
$ new_internal_solid = $ solid = intersection (
2014-02-07 00:48:47 +00:00
$ new_internal_solid ,
2016-11-09 09:24:45 +00:00
$ regularized ,
2014-02-07 00:48:47 +00:00
) if @$ too_narrow ;
}
# make sure the new internal solid is wide enough, as it might get collapsed
# when spacing is added in Fill.pm
2016-11-09 09:24:45 +00:00
if ( $ layerm - > region - > config - > ensure_vertical_shell_thickness ) {
# The possible thin sickles of top / bottom surfaces on steeply sloping surfaces touch
# the projections of top / bottom perimeters, therefore they will be sufficiently inflated by
# merging them with the projections of the top / bottom perimeters.
} else {
#FIXME Vojtech: Disable this and you will be sorry.
# https://github.com/prusa3d/Slic3r/issues/26 bottom
2014-02-07 00:48:47 +00:00
my $ margin = 3 * $ layerm - > flow ( FLOW_ROLE_SOLID_INFILL ) - > scaled_width ; # require at least this size
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-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
) ;
if ( @$ too_narrow ) {
2014-02-07 00:48:47 +00:00
# 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
my @ grown = @ { intersection (
offset ( $ too_narrow , + $ margin ) ,
2015-07-03 21:38:41 +00:00
# Discard bridges as they are grown for anchoring and we can't
# remove such anchors. (This may happen when a bridge is being
# anchored onto a wall where little space remains after the bridge
# is grown, and that little space is an internal solid shell so
# it triggers this too_narrow logic.)
[ map $ _ - > p , grep { $ _ - > is_internal && ! $ _ - > is_bridge } @ neighbor_fill_surfaces ] ,
2014-02-07 00:48:47 +00:00
) } ;
$ new_internal_solid = $ solid = [ @ grown , @$ new_internal_solid ] ;
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 ;
2014-11-09 15:23:50 +00:00
$ neighbor_fill_surfaces - > append ( $ _ )
for 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
2014-11-09 15:23:50 +00:00
$ neighbor_fill_surfaces - > append ( $ _ )
for 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
2014-03-25 00:11:28 +00:00
foreach my $ s ( @ { Slic3r::Surface::Collection - > new ( grep { ( $ _ - > surface_type == S_TYPE_TOP ) || $ _ - > is_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 ,
) ;
2014-11-09 15:23:50 +00:00
$ neighbor_fill_surfaces - > append ( $ _ )
for map $ s - > [ 0 ] - > clone ( expolygon = > $ _ ) , @$ solid_surfaces ;
2012-09-22 17:04:36 +00:00
}
2012-04-29 10:51:20 +00:00
}
2016-09-26 11:56:24 +00:00
} # foreach my $type (S_TYPE_TOP, S_TYPE_BOTTOM, S_TYPE_BOTTOMBRIDGE)
} # for each layer
} # for each region
# Debugging output.
if ( $ SLIC3R_DEBUG_SLICE_PROCESSING ) {
for my $ region_id ( 0 .. ( $ self - > print - > region_count - 1 ) ) {
for ( my $ i = 0 ; $ i < $ self - > layer_count ; $ i + + ) {
my $ layerm = $ self - > get_layer ( $ i ) - > regions - > [ $ region_id ] ;
$ layerm - > export_region_slices_to_svg_debug ( "5_discover_horizontal_shells" ) ;
$ layerm - > export_region_fill_surfaces_to_svg_debug ( "5_discover_horizontal_shells" ) ;
} # for each layer
} # for each region
2012-04-29 10:51:20 +00:00
}
}
2016-09-26 11:56:24 +00:00
# combine fill surfaces across layers to honor the "infill every N layers" option
2014-12-08 20:23:42 +00:00
# Idempotence of this method is guaranteed by the fact that we don't remove things from
# fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
2012-07-22 18:48:38 +00:00
sub combine_infill {
2012-04-29 10:51:20 +00:00
my $ self = shift ;
2014-01-02 09:44:54 +00:00
2014-12-08 20:23:42 +00:00
# define the type used for voids
my % voidtype = (
& S_TYPE_INTERNAL ( ) = > S_TYPE_INTERNALVOID ,
) ;
2014-12-08 19:14:04 +00:00
# work on each region separately
2014-08-03 16:41:09 +00:00
for my $ region_id ( 0 .. ( $ self - > print - > region_count - 1 ) ) {
2014-12-08 20:23:42 +00:00
my $ region = $ self - > print - > get_region ( $ region_id ) ;
2014-01-02 09:44:54 +00:00
my $ every = $ region - > config - > infill_every_layers ;
2014-12-08 19:14:04 +00:00
next unless $ every > 1 && $ region - > config - > fill_density > 0 ;
2014-01-02 09:44:54 +00:00
2013-02-22 15:24:24 +00:00
# limit the number of combined layers to the maximum height allowed by this regions' nozzle
2014-12-16 23:34:00 +00:00
my $ nozzle_diameter = min (
$ self - > print - > config - > get_at ( 'nozzle_diameter' , $ region - > config - > infill_extruder - 1 ) ,
$ self - > print - > config - > get_at ( 'nozzle_diameter' , $ region - > config - > solid_infill_extruder - 1 ) ,
) ;
2013-03-10 11:08:18 +00:00
# define the combinations
2014-12-08 19:14:04 +00:00
my % combine = ( ) ; # layer_idx => number of additional combined lower layers
2013-03-10 11:08:18 +00:00
{
my $ current_height = my $ layers = 0 ;
2014-12-08 19:14:04 +00:00
for my $ layer_idx ( 0 .. ( $ self - > layer_count - 1 ) ) {
my $ layer = $ self - > get_layer ( $ layer_idx ) ;
next if $ layer - > id == 0 ; # skip first print layer (which may not be first layer in array because of raft)
my $ height = $ layer - > height ;
2013-03-10 11:08:18 +00:00
2014-12-08 19:14:04 +00:00
# check whether the combination of this layer with the lower layers' buffer
# would exceed max layer height or max combined layer count
2016-03-13 11:24:03 +00:00
if ( $ current_height + $ height >= $ nozzle_diameter + epsilon || $ layers >= $ every ) {
2014-12-08 19:14:04 +00:00
# append combination to lower layer
$ combine { $ layer_idx - 1 } = $ layers ;
2013-03-10 11:08:18 +00:00
$ current_height = $ layers = 0 ;
}
$ current_height += $ height ;
$ layers + + ;
}
2014-12-08 19:14:04 +00:00
# append lower layers (if any) to uppermost layer
$ combine { $ self - > layer_count - 1 } = $ layers ;
2013-03-10 11:08:18 +00:00
}
2013-02-22 15:24:24 +00:00
2014-12-08 19:14:04 +00:00
# loop through layers to which we have assigned layers to combine
for my $ layer_idx ( sort keys % combine ) {
next unless $ combine { $ layer_idx } > 1 ;
# get all the LayerRegion objects to be combined
2014-12-08 20:23:42 +00:00
my @ layerms = map $ self - > get_layer ( $ _ ) - > get_region ( $ region_id ) ,
2014-12-08 19:14:04 +00:00
( $ layer_idx - ( $ combine { $ layer_idx } - 1 ) .. $ layer_idx ) ;
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' ) ,
2014-12-08 19:14:04 +00:00
$ layer_idx - ( $ every - 1 ) , $ layer_idx ;
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 (
2014-01-04 23:36:33 +00:00
$ layerms [ - 1 ] - > flow ( FLOW_ROLE_SOLID_INFILL ) - > scaled_width / 2
+ $ layerms [ - 1 ] - > flow ( FLOW_ROLE_PERIMETER ) - > scaled_width / 2
2013-03-16 23:57:58 +00:00
# Because fill areas for rectilinear and honeycomb are grown
# later to overlap perimeters, we need to counteract that too.
2015-11-01 18:03:11 +00:00
+ ( ( $ type == S_TYPE_INTERNALSOLID || $ region - > config - > fill_pattern =~ /(rectilinear|grid|line|honeycomb)/ )
2015-01-13 19:51:31 +00:00
? $ layerms [ - 1 ] - > flow ( FLOW_ROLE_SOLID_INFILL ) - > scaled_width
2013-03-16 23:57:58 +00:00
: 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
2015-10-26 22:23:03 +00:00
if ( $ layerm - > layer - > id == $ self - > get_layer ( $ layer_idx ) - > 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 ,
2015-10-26 22:23:03 +00:00
thickness = > sum ( map $ _ - > layer - > height , @ layerms ) ,
2013-03-17 00:10:40 +00:00
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
2014-12-08 20:23:42 +00:00
push @ new_this_type ,
map Slic3r::Surface - > new ( expolygon = > $ _ , surface_type = > $ voidtype { $ type } ) ,
2013-03-13 00:03:54 +00:00
@ { 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 ;
2014-11-09 15:23:50 +00:00
$ layerm - > fill_surfaces - > append ( $ _ ) for ( @ new_this_type , @ other_types ) ;
2012-04-29 10:51:20 +00:00
}
}
}
}
}
2016-09-13 09:24:55 +00:00
# Simplify the sliced model, if "resolution" configuration parameter > 0.
# The simplification is problematic, because it simplifies the slices independent from each other,
# which makes the simplified discretization visible on the object surface.
2013-12-19 14:23:10 +00:00
sub _simplify_slices {
my ( $ self , $ distance ) = @ _ ;
foreach my $ layer ( @ { $ self - > layers } ) {
$ layer - > slices - > simplify ( $ distance ) ;
$ _ - > slices - > simplify ( $ distance ) for @ { $ layer - > regions } ;
}
}
2013-12-31 13:33:03 +00:00
sub support_material_flow {
my ( $ self , $ role ) = @ _ ;
$ role // = FLOW_ROLE_SUPPORT_MATERIAL ;
my $ extruder = ( $ role == FLOW_ROLE_SUPPORT_MATERIAL )
? $ self - > config - > support_material_extruder
: $ self - > config - > support_material_interface_extruder ;
# we use a bogus layer_height because we use the same flow for all
# support material layers
2014-01-03 17:27:46 +00:00
return Slic3r::Flow - > new_from_width (
2014-04-25 17:39:27 +00:00
width = > $ self - > config - > support_material_extrusion_width || $ self - > config - > extrusion_width ,
2013-12-31 13:33:03 +00:00
role = > $ role ,
2014-01-04 23:36:33 +00:00
nozzle_diameter = > $ self - > print - > config - > nozzle_diameter - > [ $ extruder - 1 ] // $ self - > print - > config - > nozzle_diameter - > [ 0 ] ,
2013-12-31 13:33:03 +00:00
layer_height = > $ self - > config - > layer_height ,
bridge_flow_ratio = > 0 ,
) ;
}
2012-04-29 10:51:20 +00:00
1 ;