2016-09-13 09:24:55 +00:00
# The slicing work horse.
# Extends C++ class Slic3r::Print
2011-09-01 19:06:28 +00:00
package Slic3r::Print ;
2014-06-10 14:01:57 +00:00
use strict ;
use warnings ;
2011-09-01 19:06:28 +00:00
2012-04-30 12:56:01 +00:00
use File::Basename qw( basename fileparse ) ;
2012-08-07 21:37:16 +00:00
use File::Spec ;
2014-06-05 14:24:47 +00:00
use List::Util qw( min max first sum ) ;
2015-12-21 14:02:39 +00:00
use Slic3r::ExtrusionLoop ':roles' ;
2012-05-19 13:40:11 +00:00
use Slic3r::ExtrusionPath ':roles' ;
2014-03-24 16:52:14 +00:00
use Slic3r::Flow ':roles' ;
2017-07-19 08:45:39 +00:00
use Slic3r::Geometry qw( X Y unscale ) ;
2014-10-25 09:15:12 +00:00
use Slic3r::Geometry::Clipper qw( diff_ex union_ex intersection_ex intersection offset
2017-07-19 08:45:39 +00:00
union JT_ROUND JT_SQUARE ) ;
2014-03-24 16:52:14 +00:00
use Slic3r::Print::State ':steps' ;
2011-09-25 20:11:56 +00:00
2014-05-06 08:07:18 +00:00
our $ status_cb ;
2011-09-01 19:06:28 +00:00
2014-05-06 08:07:18 +00:00
sub set_status_cb {
my ( $ class , $ cb ) = @ _ ;
$ status_cb = $ cb ;
}
sub status_cb {
2014-06-13 22:06:49 +00:00
return $ status_cb // sub { } ;
2014-05-06 08:07:18 +00:00
}
2012-06-23 19:31:29 +00:00
2012-04-30 12:56:01 +00:00
sub size {
my $ self = shift ;
2013-06-16 10:21:25 +00:00
return $ self - > bounding_box - > size ;
2012-04-30 12:56:01 +00:00
}
2012-03-06 03:55:21 +00:00
2017-02-07 17:28:53 +00:00
# Slicing process, running at a background thread.
2014-03-24 16:52:14 +00:00
sub process {
my ( $ self ) = @ _ ;
2017-03-03 11:53:05 +00:00
Slic3r:: trace ( 3 , "Staring the slicing process." ) ;
2014-06-13 18:05:18 +00:00
$ _ - > make_perimeters for @ { $ self - > objects } ;
2016-11-26 11:28:39 +00:00
$ self - > status_cb - > ( 70 , "Infilling layers" ) ;
2014-06-13 18:05:18 +00:00
$ _ - > infill for @ { $ self - > objects } ;
2016-11-26 11:28:39 +00:00
2014-06-13 18:05:18 +00:00
$ _ - > generate_support_material for @ { $ self - > objects } ;
$ self - > make_skirt ;
$ self - > make_brim ; # must come after make_skirt
2017-05-25 20:27:53 +00:00
$ self - > make_wipe_tower ;
2012-04-30 12:56:01 +00:00
2013-02-27 10:26:52 +00:00
# time to make some statistics
if ( 0 ) {
eval "use Devel::Size" ;
print "MEMORY USAGE:\n" ;
printf " meshes = %.1fMb\n" , List::Util:: sum ( map Devel::Size:: total_size ( $ _ - > meshes ) , @ { $ self - > objects } ) /1024/ 1024 ;
printf " layer slices = %.1fMb\n" , List::Util:: sum ( map Devel::Size:: total_size ( $ _ - > slices ) , map @ { $ _ - > layers } , @ { $ self - > objects } ) /1024/ 1024 ;
printf " region slices = %.1fMb\n" , List::Util:: sum ( map Devel::Size:: total_size ( $ _ - > slices ) , map @ { $ _ - > regions } , map @ { $ _ - > layers } , @ { $ self - > objects } ) /1024/ 1024 ;
printf " perimeters = %.1fMb\n" , List::Util:: sum ( map Devel::Size:: total_size ( $ _ - > perimeters ) , map @ { $ _ - > regions } , map @ { $ _ - > layers } , @ { $ self - > objects } ) /1024/ 1024 ;
printf " fills = %.1fMb\n" , List::Util:: sum ( map Devel::Size:: total_size ( $ _ - > fills ) , map @ { $ _ - > regions } , map @ { $ _ - > layers } , @ { $ self - > objects } ) /1024/ 1024 ;
printf " print object = %.1fMb\n" , Devel::Size:: total_size ( $ self ) /1024/ 1024 ;
}
2013-04-27 13:02:13 +00:00
if ( 0 ) {
eval "use Slic3r::Test::SectionCut" ;
Slic3r::Test::SectionCut - > new ( print = > $ self ) - > export_svg ( "section_cut.svg" ) ;
}
2017-03-03 11:53:05 +00:00
Slic3r:: trace ( 3 , "Slicing process finished." )
2014-03-24 16:52:14 +00:00
}
2017-08-02 14:05:18 +00:00
# G-code export process, running at a background thread.
2017-12-05 14:54:24 +00:00
# The export_gcode may die for various reasons (fails to process output_filename_format,
# write error into the G-code, cannot execute post-processing scripts).
# It is up to the caller to show an error message.
2014-03-24 16:52:14 +00:00
sub export_gcode {
my $ self = shift ;
my % params = @ _ ;
2014-06-13 18:18:34 +00:00
# prerequisites
$ self - > process ;
2013-02-27 10:26:52 +00:00
2012-04-30 12:56:01 +00:00
# output everything to a G-code file
2017-12-05 14:54:24 +00:00
# The following call may die if the output_filename_format template substitution fails.
2016-12-20 18:01:51 +00:00
my $ output_file = $ self - > output_filepath ( $ params { output_file } // '' ) ;
2014-06-13 18:18:34 +00:00
$ self - > status_cb - > ( 90 , "Exporting G-code" . ( $ output_file ? " to $output_file" : "" ) ) ;
2017-08-03 15:31:31 +00:00
2017-12-05 14:54:24 +00:00
# The following line may die for multiple reasons.
2018-02-14 20:57:46 +00:00
my $ gcode = Slic3r::GCode - > new ;
if ( defined $ params { gcode_preview_data } ) {
$ gcode - > do_export_w_preview ( $ self , $ output_file , $ params { gcode_preview_data } ) ;
} else {
$ gcode - > do_export ( $ self , $ output_file ) ;
}
2012-04-30 12:56:01 +00:00
# run post-processing scripts
2014-03-24 16:52:14 +00:00
if ( @ { $ self - > config - > post_process } ) {
2014-06-13 18:18:34 +00:00
$ self - > status_cb - > ( 95 , "Running post-processing scripts" ) ;
2014-03-24 16:52:14 +00:00
$ self - > config - > setenv ;
2015-01-17 09:50:34 +00:00
for my $ script ( @ { $ self - > config - > post_process } ) {
2018-01-21 20:08:30 +00:00
# Ignore empty post processing script lines.
next if $ script =~ /^\s*$/ ;
2015-01-17 09:50:34 +00:00
Slic3r:: debugf " '%s' '%s'\n" , $ script , $ output_file ;
2015-03-02 20:48:29 +00:00
# -x doesn't return true on Windows except for .exe files
if ( ( $^O eq 'MSWin32' ) ? ! ( - e $ script ) : ! ( - x $ script ) ) {
2015-01-17 09:50:34 +00:00
die "The configured post-processing script is not executable: check permissions. ($script)\n" ;
}
2017-10-03 15:05:47 +00:00
if ( $^O eq 'MSWin32' && $ script =~ /\.[pP][lL]/ ) {
system ( $^X , $ script , $ output_file ) ;
} else {
system ( $ script , $ output_file ) ;
}
2012-02-19 09:48:58 +00:00
}
}
}
2018-05-15 16:01:47 +00:00
sub export_png {
my $ self = shift ;
my % params = @ _ ;
$ _ - > slice for @ { $ self - > objects } ;
my $ fh = $ params { output_file } ;
$ self - > print_to_png ( $ fh ) ;
}
2016-09-13 09:24:55 +00:00
# Export SVG slices for the offline SLA printing.
2017-12-05 14:54:24 +00:00
# The export_svg is expected to be executed inside an eval block.
2012-04-30 12:56:01 +00:00
sub export_svg {
2011-09-18 17:28:12 +00:00
my $ self = shift ;
2012-04-30 12:56:01 +00:00
my % params = @ _ ;
2013-03-16 18:39:00 +00:00
$ _ - > slice for @ { $ self - > objects } ;
2012-04-30 12:56:01 +00:00
2013-06-07 10:00:03 +00:00
my $ fh = $ params { output_fh } ;
2013-10-13 09:45:22 +00:00
if ( ! $ fh ) {
2017-12-05 14:54:24 +00:00
# The following line may die if the output_filename_format template substitution fails.
2016-12-20 18:01:51 +00:00
my $ output_file = $ self - > output_filepath ( $ params { output_file } ) ;
2017-07-11 15:15:34 +00:00
$ output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ ;
2013-06-07 10:00:03 +00:00
Slic3r:: open ( \ $ fh , ">" , $ output_file ) or die "Failed to open $output_file for writing\n" ;
print "Exporting to $output_file..." unless $ params { quiet } ;
}
2012-04-30 12:56:01 +00:00
2015-01-30 18:34:46 +00:00
my $ print_bb = $ self - > bounding_box ;
my $ print_size = $ print_bb - > size ;
2012-04-30 12:56:01 +00:00
print $ fh sprintf << "EOF" , unscale ( $ print_size - > [ X ] ) , unscale ( $ print_size - > [ Y ] ) ;
< ? xml version = "1.0" encoding = "UTF-8" standalone = "yes" ? >
< ! DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
< svg width = "%s" height = "%s" xmlns = "http://www.w3.org/2000/svg" xmlns:svg = "http://www.w3.org/2000/svg" xmlns:xlink = "http://www.w3.org/1999/xlink" xmlns:slic3r = "http://slic3r.org/namespaces/slic3r" >
< ! - -
Generated using Slic3r $ Slic3r:: VERSION
http: //s lic3r . org /
- - >
EOF
my $ print_polygon = sub {
my ( $ polygon , $ type ) = @ _ ;
printf $ fh qq{ <polygon slic3r:type="%s" points="%s" style="fill: %s" /> \ n } ,
$ type , ( join ' ' , map { join ',' , map unscale $ _ , @$ _ } @$ polygon ) ,
2012-05-21 16:29:19 +00:00
( $ type eq 'contour' ? 'white' : 'black' ) ;
2012-04-30 12:56:01 +00:00
} ;
2014-01-11 16:40:09 +00:00
my @ layers = sort { $ a - > print_z <=> $ b - > print_z }
map { @ { $ _ - > layers } , @ { $ _ - > support_layers } }
@ { $ self - > objects } ;
my $ layer_id = - 1 ;
2012-06-11 12:47:48 +00:00
my @ previous_layer_slices = ( ) ;
2014-01-11 16:40:09 +00:00
for my $ layer ( @ layers ) {
$ layer_id + + ;
2015-01-30 17:45:30 +00:00
if ( $ layer - > slice_z == - 1 ) {
printf $ fh qq{ <g id="layer%d"> \ n } , $ layer_id ;
} else {
printf $ fh qq{ <g id="layer%d" slic3r:z="%s"> \ n } , $ layer_id , unscale ( $ layer - > slice_z ) ;
}
2012-04-30 12:56:01 +00:00
2012-06-11 12:47:48 +00:00
my @ current_layer_slices = ( ) ;
2014-01-11 16:40:09 +00:00
# sort slices so that the outermost ones come first
2015-01-30 18:34:46 +00:00
my @ slices = sort { $ a - > contour - > contains_point ( $ b - > contour - > first_point ) ? 0 : 1 } @ { $ layer - > slices } ;
foreach my $ copy ( @ { $ layer - > object - > _shifted_copies } ) {
2014-01-11 16:40:09 +00:00
foreach my $ slice ( @ slices ) {
my $ expolygon = $ slice - > clone ;
$ expolygon - > translate ( @$ copy ) ;
2015-01-30 18:34:46 +00:00
$ expolygon - > translate ( - $ print_bb - > x_min , - $ print_bb - > y_min ) ;
2014-01-11 16:40:09 +00:00
$ print_polygon - > ( $ expolygon - > contour , 'contour' ) ;
$ print_polygon - > ( $ _ , 'hole' ) for @ { $ expolygon - > holes } ;
push @ current_layer_slices , $ expolygon ;
2012-04-30 12:56:01 +00:00
}
}
2012-06-11 12:47:48 +00:00
# generate support material
2014-01-11 16:40:09 +00:00
if ( $ self - > has_support_material && $ layer - > id > 0 ) {
2012-06-11 12:47:48 +00:00
my ( @ supported_slices , @ unsupported_slices ) = ( ) ;
foreach my $ expolygon ( @ current_layer_slices ) {
my $ intersection = intersection_ex (
[ map @$ _ , @ previous_layer_slices ] ,
2014-08-03 09:35:18 +00:00
[ @$ expolygon ] ,
2012-06-11 12:47:48 +00:00
) ;
@$ intersection
? push @ supported_slices , $ expolygon
: push @ unsupported_slices , $ expolygon ;
}
my @ supported_points = map @$ _ , @$ _ , @ supported_slices ;
foreach my $ expolygon ( @ unsupported_slices ) {
# look for the nearest point to this island among all
# supported points
2013-08-26 22:52:20 +00:00
my $ contour = $ expolygon - > contour ;
my $ support_point = $ contour - > first_point - > nearest_point ( \ @ supported_points )
2012-09-21 14:52:05 +00:00
or next ;
2013-08-26 22:52:20 +00:00
my $ anchor_point = $ support_point - > nearest_point ( [ @$ contour ] ) ;
2012-06-11 18:42:39 +00:00
printf $ fh qq{ <line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke-width: 2; stroke: white" /> \ n } ,
2012-06-11 12:47:48 +00:00
map @$ _ , $ support_point , $ anchor_point ;
}
}
2012-04-30 12:56:01 +00:00
print $ fh qq{ </g> \ n } ;
2012-06-11 12:47:48 +00:00
@ previous_layer_slices = @ current_layer_slices ;
2012-04-30 12:56:01 +00:00
}
print $ fh "</svg>\n" ;
close $ fh ;
2013-06-07 10:00:03 +00:00
print "Done.\n" unless $ params { quiet } ;
2011-09-25 20:11:56 +00:00
}
2012-04-29 10:51:20 +00:00
sub make_skirt {
2011-11-13 17:41:12 +00:00
my $ self = shift ;
2014-05-10 18:54:12 +00:00
2014-06-13 18:05:18 +00:00
# prerequisites
$ _ - > make_perimeters for @ { $ self - > objects } ;
$ _ - > infill for @ { $ self - > objects } ;
$ _ - > generate_support_material for @ { $ self - > objects } ;
return if $ self - > step_done ( STEP_SKIRT ) ;
2017-05-25 20:27:53 +00:00
2014-06-13 18:05:18 +00:00
$ self - > set_step_started ( STEP_SKIRT ) ;
2017-05-25 20:27:53 +00:00
$ self - > skirt - > clear ;
if ( $ self - > has_skirt ) {
$ self - > status_cb - > ( 88 , "Generating skirt" ) ;
$ self - > _make_skirt ( ) ;
2014-06-13 18:18:34 +00:00
}
2014-06-13 18:05:18 +00:00
$ self - > set_step_done ( STEP_SKIRT ) ;
2012-02-19 11:03:36 +00:00
}
2012-06-23 19:31:29 +00:00
sub make_brim {
my $ self = shift ;
2014-03-24 16:52:14 +00:00
2014-06-13 18:05:18 +00:00
# prerequisites
$ _ - > make_perimeters for @ { $ self - > objects } ;
$ _ - > infill for @ { $ self - > objects } ;
$ _ - > generate_support_material for @ { $ self - > objects } ;
$ self - > make_skirt ;
return if $ self - > step_done ( STEP_BRIM ) ;
2017-07-07 14:40:23 +00:00
2014-06-13 18:05:18 +00:00
$ self - > set_step_started ( STEP_BRIM ) ;
2014-05-10 18:54:12 +00:00
# since this method must be idempotent, we clear brim paths *before*
# checking whether we need to generate them
$ self - > brim - > clear ;
2017-07-07 14:40:23 +00:00
if ( $ self - > config - > brim_width > 0 ) {
$ self - > status_cb - > ( 88 , "Generating brim" ) ;
$ self - > _make_brim ;
2014-06-13 18:18:34 +00:00
}
2017-07-07 14:40:23 +00:00
2014-06-13 18:05:18 +00:00
$ self - > set_step_done ( STEP_BRIM ) ;
2012-06-23 19:31:29 +00:00
}
2017-05-25 20:27:53 +00:00
sub make_wipe_tower {
my $ self = shift ;
# prerequisites
$ _ - > make_perimeters for @ { $ self - > objects } ;
$ _ - > infill for @ { $ self - > objects } ;
$ _ - > generate_support_material for @ { $ self - > objects } ;
$ self - > make_skirt ;
$ self - > make_brim ;
return if $ self - > step_done ( STEP_WIPE_TOWER ) ;
$ self - > set_step_started ( STEP_WIPE_TOWER ) ;
$ self - > _clear_wipe_tower ;
if ( $ self - > has_wipe_tower ) {
# $self->status_cb->(95, "Generating wipe tower");
$ self - > _make_wipe_tower ;
}
$ self - > set_step_done ( STEP_WIPE_TOWER ) ;
}
2011-09-01 19:06:28 +00:00
1 ;