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' ;
2014-11-30 17:09:06 +00:00
use Slic3r::Geometry qw( X Y Z X1 Y1 X2 Y2 MIN MAX PI scale unscale convex_hull ) ;
2014-10-25 09:15:12 +00:00
use Slic3r::Geometry::Clipper qw( diff_ex union_ex intersection_ex intersection offset
2014-03-24 16:52:14 +00:00
offset2 union union_pt_chained JT_ROUND JT_SQUARE ) ;
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
}
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
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" : "" ) ) ;
2016-12-17 20:59:33 +00:00
{
# open output gcode file if we weren't supplied a file-handle
2017-05-03 16:28:22 +00:00
my $ tempfile = "$output_file.tmp" ;
my $ gcode = Slic3r::GCode - > new ( ) ;
2017-05-04 07:24:34 +00:00
my $ result = $ gcode - > do_export ( $ self , Slic3r:: encode_path ( $ tempfile ) ) ;
2017-05-03 16:28:22 +00:00
die $ result . "\n" if ( $ result ne '' ) ;
my $ i ;
for ( $ i = 0 ; $ i < 5 ; $ i += 1 ) {
last if ( rename Slic3r:: encode_path ( $ tempfile ) , Slic3r:: encode_path ( $ output_file ) ) ;
# Wait for 1/4 seconds and try to rename once again.
select ( undef , undef , undef , 0.25 ) ;
2016-12-17 20:59:33 +00:00
}
2017-05-03 16:28:22 +00:00
Slic3r:: debugf "Failed to remove the output G-code file from $tempfile to $output_file. Is $tempfile locked?\n" if ( $ i == 5 ) ;
2016-12-17 20:59:33 +00:00
}
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 } ) {
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" ;
}
system ( $ script , $ output_file ) ;
2012-02-19 09:48:58 +00:00
}
}
}
2016-09-13 09:24:55 +00:00
# Export SVG slices for the offline SLA printing.
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 ) {
2016-12-20 18:01:51 +00:00
my $ output_file = $ self - > output_filepath ( $ params { output_file } ) ;
2013-06-07 10:00:03 +00:00
$ output_file =~ s/\.gcode$/.svg/i ;
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 ) ;
$ 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 ;
2014-06-13 18:18:34 +00:00
if ( $ self - > config - > brim_width == 0 ) {
$ self - > set_step_done ( STEP_BRIM ) ;
return ;
}
$ self - > status_cb - > ( 88 , "Generating brim" ) ;
2014-03-24 16:52:14 +00:00
2014-12-16 23:45:05 +00:00
# brim is only printed on first layer and uses perimeter extruder
2014-07-24 16:32:07 +00:00
my $ first_layer_height = $ self - > skirt_first_layer_height ;
2014-12-16 23:45:05 +00:00
my $ flow = $ self - > brim_flow ;
2014-06-11 23:00:13 +00:00
my $ mm3_per_mm = $ flow - > mm3_per_mm ;
2013-02-22 15:08:11 +00:00
my $ grow_distance = $ flow - > scaled_width / 2 ;
2012-06-23 19:31:29 +00:00
my @ islands = ( ) ; # array of polygons
2014-05-06 08:07:18 +00:00
foreach my $ obj_idx ( 0 .. ( $ self - > object_count - 1 ) ) {
2013-07-29 18:49:54 +00:00
my $ object = $ self - > objects - > [ $ obj_idx ] ;
2014-06-13 15:45:44 +00:00
my $ layer0 = $ object - > get_layer ( 0 ) ;
2012-08-06 18:54:49 +00:00
my @ object_islands = (
( map $ _ - > contour , @ { $ layer0 - > slices } ) ,
) ;
2013-07-29 18:49:54 +00:00
if ( @ { $ object - > support_layers } ) {
my $ support_layer0 = $ object - > support_layers - > [ 0 ] ;
push @ object_islands ,
2014-03-24 16:52:14 +00:00
( map @ { $ _ - > polyline - > grow ( $ grow_distance ) } , @ { $ support_layer0 - > support_fills } )
2013-07-29 18:49:54 +00:00
if $ support_layer0 - > support_fills ;
}
2014-03-24 16:52:14 +00:00
foreach my $ copy ( @ { $ object - > _shifted_copies } ) {
2013-09-16 08:33:30 +00:00
push @ islands , map { $ _ - > translate ( @$ copy ) ; $ _ } map $ _ - > clone , @ object_islands ;
2012-06-23 19:31:29 +00:00
}
}
2013-05-09 12:52:56 +00:00
my @ loops = ( ) ;
2014-03-24 16:52:14 +00:00
my $ num_loops = sprintf "%.0f" , $ self - > config - > brim_width / $ flow - > width ;
2012-06-23 19:31:29 +00:00
for my $ i ( reverse 1 .. $ num_loops ) {
2012-08-06 18:26:08 +00:00
# JT_SQUARE ensures no vertex is outside the given offset distance
2013-05-09 12:52:56 +00:00
# -0.5 because islands are not represented by their centerlines
2013-08-09 12:22:41 +00:00
# (first offset more, then step back - reverse order than the one used for
# perimeters because here we're offsetting outwards)
2016-11-28 16:33:17 +00:00
push @ loops , @ { offset2 ( \ @ islands , ( $ i + 0.5 ) * $ flow - > scaled_spacing , - 1.0 * $ flow - > scaled_spacing , JT_SQUARE ) } ;
2012-06-23 19:31:29 +00:00
}
2013-05-09 12:52:56 +00:00
2014-05-08 09:07:37 +00:00
$ self - > brim - > append ( map Slic3r::ExtrusionLoop - > new_from_paths (
Slic3r::ExtrusionPath - > new (
polyline = > Slic3r::Polygon - > new ( @$ _ ) - > split_at_first_point ,
role = > EXTR_ROLE_SKIRT ,
mm3_per_mm = > $ mm3_per_mm ,
width = > $ flow - > width ,
height = > $ first_layer_height ,
) ,
2014-03-24 16:52:14 +00:00
) , reverse @ { union_pt_chained ( \ @ loops ) } ) ;
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 ) ;
}
2016-11-05 01:23:46 +00:00
# Wrapper around the C++ Slic3r::Print::validate()
# to produce a Perl exception without a hang-up on some Strawberry perls.
sub validate
{
my $ self = shift ;
my $ err = $ self - > _validate ;
die $ err . "\n" if ( defined ( $ err ) && $ err ne '' ) ;
}
2011-09-01 19:06:28 +00:00
1 ;