2011-09-01 19:06:28 +00:00
package Slic3r::Print ;
2011-09-06 09:50:43 +00:00
use Moo ;
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 ;
2012-10-29 10:17:57 +00:00
use List::Util qw( max first ) ;
2012-10-22 13:29:54 +00:00
use Math::ConvexHull::MonotoneChain qw( convex_hull ) ;
2012-05-19 13:40:11 +00:00
use Slic3r::ExtrusionPath ':roles' ;
2012-10-22 13:29:54 +00:00
use Slic3r::Geometry qw( X Y Z X1 Y1 X2 Y2 MIN PI scale unscale move_points nearest_point ) ;
2012-08-06 18:26:08 +00:00
use Slic3r::Geometry::Clipper qw( diff_ex union_ex intersection_ex offset JT_ROUND JT_SQUARE ) ;
2012-04-30 12:56:01 +00:00
use Time::HiRes qw( gettimeofday tv_interval ) ;
2011-09-25 20:11:56 +00:00
2012-07-29 15:02:46 +00:00
has 'config' = > ( is = > 'rw' , default = > sub { Slic3r::Config - > new_from_defaults } , trigger = > 1 ) ;
2012-09-12 13:29:44 +00:00
has 'extra_variables' = > ( is = > 'rw' , default = > sub { { } } ) ;
2012-04-30 12:56:01 +00:00
has 'objects' = > ( is = > 'rw' , default = > sub { [] } ) ;
2012-04-29 10:51:20 +00:00
has 'total_extrusion_length' = > ( is = > 'rw' ) ;
2012-09-23 00:40:25 +00:00
has 'processing_time' = > ( is = > 'rw' ) ;
has 'extruders' = > ( is = > 'rw' , default = > sub { [] } ) ;
2012-09-23 00:52:31 +00:00
has 'regions' = > ( is = > 'rw' , default = > sub { [] } ) ;
2012-09-23 00:40:25 +00:00
has 'support_material_flow' = > ( is = > 'rw' ) ;
has 'first_layer_support_material_flow' = > ( is = > 'rw' ) ;
2013-02-04 14:48:57 +00:00
has 'has_support_material' = > ( is = > 'lazy' ) ;
2011-09-03 18:47:38 +00:00
2012-04-29 10:51:20 +00:00
# ordered collection of extrusion paths to build skirt loops
has 'skirt' = > (
2011-09-01 19:06:28 +00:00
is = > 'rw' ,
2012-04-29 10:51:20 +00:00
#isa => 'ArrayRef[Slic3r::ExtrusionLoop]',
2011-09-01 19:06:28 +00:00
default = > sub { [] } ,
) ;
2012-06-23 19:31:29 +00:00
# ordered collection of extrusion paths to build a brim
has 'brim' = > (
is = > 'rw' ,
#isa => 'ArrayRef[Slic3r::ExtrusionLoop]',
default = > sub { [] } ,
) ;
2012-07-27 19:13:03 +00:00
sub BUILD {
my $ self = shift ;
2012-07-29 15:02:46 +00:00
# call this manually because the 'default' coderef doesn't trigger the trigger
$ self - > _trigger_config ;
}
sub _trigger_config {
my $ self = shift ;
2012-07-27 19:13:03 +00:00
# store config in a handy place
$ Slic3r:: Config = $ self - > config ;
2012-08-30 21:04:56 +00:00
# legacy with existing config files
$ self - > config - > set ( 'first_layer_height' , $ self - > config - > layer_height )
if ! $ self - > config - > first_layer_height ;
$ self - > config - > set_ifndef ( 'small_perimeter_speed' , $ self - > config - > perimeter_speed ) ;
$ self - > config - > set_ifndef ( 'bridge_speed' , $ self - > config - > infill_speed ) ;
$ self - > config - > set_ifndef ( 'solid_infill_speed' , $ self - > config - > infill_speed ) ;
$ self - > config - > set_ifndef ( 'top_solid_infill_speed' , $ self - > config - > solid_infill_speed ) ;
2012-10-25 10:21:04 +00:00
$ self - > config - > set_ifndef ( 'top_solid_layers' , $ self - > config - > solid_layers ) ;
$ self - > config - > set_ifndef ( 'bottom_solid_layers' , $ self - > config - > solid_layers ) ;
2012-08-30 21:04:56 +00:00
2012-07-27 19:13:03 +00:00
# G-code flavors
$ self - > config - > set ( 'extrusion_axis' , 'A' ) if $ self - > config - > gcode_flavor eq 'mach3' ;
$ self - > config - > set ( 'extrusion_axis' , '' ) if $ self - > config - > gcode_flavor eq 'no-extrusion' ;
}
2013-02-04 14:48:57 +00:00
sub _build_has_support_material {
my $ self = shift ;
return $ self - > config - > support_material
|| $ self - > config - > raft_layers > 0
|| $ self - > config - > support_material_enforce_layers > 0 ;
}
2012-08-29 14:49:38 +00:00
sub add_model {
my $ self = shift ;
my ( $ model ) = @ _ ;
2012-09-23 00:40:25 +00:00
# append/merge materials and preserve a mapping between the original material ID
# and our numeric material index
my % materials = ( ) ;
{
my @ material_ids = sort keys % { $ model - > materials } ;
@ material_ids = ( 0 ) if ! @ material_ids ;
2012-09-23 00:52:31 +00:00
for ( my $ i = $ self - > regions_count ; $ i < @ material_ids ; $ i + + ) {
push @ { $ self - > regions } , Slic3r::Print::Region - > new ;
$ materials { $ material_ids [ $ i ] } = $# { $ self - > regions } ;
2012-09-23 00:40:25 +00:00
}
}
2012-09-22 17:04:36 +00:00
2012-08-29 14:49:38 +00:00
foreach my $ object ( @ { $ model - > objects } ) {
2012-09-23 00:52:31 +00:00
my @ meshes = ( ) ; # by region_id
2012-08-29 14:49:38 +00:00
2012-09-22 17:04:36 +00:00
foreach my $ volume ( @ { $ object - > volumes } ) {
# should the object contain multiple volumes of the same material, merge them
2012-09-23 00:52:31 +00:00
my $ region_id = defined $ volume - > material_id ? $ materials { $ volume - > material_id } : 0 ;
2012-09-22 17:38:25 +00:00
my $ mesh = $ volume - > mesh - > clone ;
2012-09-23 00:52:31 +00:00
$ meshes [ $ region_id ] = $ meshes [ $ region_id ]
? Slic3r::TriangleMesh - > merge ( $ meshes [ $ region_id ] , $ mesh )
2012-09-22 17:38:25 +00:00
: $ mesh ;
2012-08-29 14:49:38 +00:00
}
2012-09-22 17:04:36 +00:00
foreach my $ mesh ( @ meshes ) {
next unless $ mesh ;
$ mesh - > check_manifoldness ;
if ( $ object - > instances ) {
# we ignore the per-instance rotation currently and only
# consider the first one
$ mesh - > rotate ( $ object - > instances - > [ 0 ] - > rotation ) ;
}
$ mesh - > rotate ( $ Slic3r:: Config - > rotate ) ;
$ mesh - > scale ( $ Slic3r:: Config - > scale / & Slic3r:: SCALING_FACTOR ) ;
}
2012-09-22 19:03:57 +00:00
my $ complete_mesh = Slic3r::TriangleMesh - > merge ( grep defined $ _ , @ meshes ) ;
2012-09-22 17:04:36 +00:00
# initialize print object
2012-09-22 19:03:57 +00:00
my $ print_object = Slic3r::Print::Object - > new (
2012-09-22 17:04:36 +00:00
print = > $ self ,
meshes = > [ @ meshes ] ,
2012-09-22 19:03:57 +00:00
size = > [ $ complete_mesh - > size ] ,
2013-03-10 13:58:49 +00:00
input_file = > $ object - > input_file ,
layer_height_ranges = > $ object - > layer_height_ranges ,
2012-09-22 17:04:36 +00:00
) ;
2012-09-22 19:03:57 +00:00
push @ { $ self - > objects } , $ print_object ;
# align object to origin
{
my @ extents = $ complete_mesh - > extents ;
foreach my $ mesh ( grep defined $ _ , @ meshes ) {
$ mesh - > move ( map - $ extents [ $ _ ] [ MIN ] , X , Y , Z ) ;
}
}
2012-08-29 14:49:38 +00:00
if ( $ object - > instances ) {
# replace the default [0,0] instance with the custom ones
2012-09-22 19:03:57 +00:00
@ { $ print_object - > copies } = map [ scale $ _ - > offset - > [ X ] , scale $ _ - > offset - > [ Y ] ] , @ { $ object - > instances } ;
2012-08-29 14:49:38 +00:00
}
2012-04-30 12:56:01 +00:00
}
2011-10-02 07:57:37 +00:00
}
2012-05-23 09:47:52 +00:00
sub validate {
my $ self = shift ;
2012-07-27 19:13:03 +00:00
if ( $ Slic3r:: Config - > complete_objects ) {
2012-05-23 09:47:52 +00:00
# check horizontal clearance
{
my @ a = ( ) ;
for my $ obj_idx ( 0 .. $# { $ self - > objects } ) {
my $ clearance ;
{
2012-09-22 17:04:36 +00:00
my @ points = map [ @$ _ [ X , Y ] ] , map @ { $ _ - > vertices } , @ { $ self - > objects - > [ $ obj_idx ] - > meshes } ;
2012-05-23 09:47:52 +00:00
my $ convex_hull = Slic3r::Polygon - > new ( convex_hull ( \ @ points ) ) ;
2012-07-27 19:13:03 +00:00
$ clearance = + ( $ convex_hull - > offset ( scale $ Slic3r:: Config - > extruder_clearance_radius / 2 , 1 , JT_ROUND ) ) [ 0 ] ;
2012-05-23 09:47:52 +00:00
}
2012-09-22 14:19:24 +00:00
for my $ copy ( @ { $ self - > objects - > [ $ obj_idx ] - > copies } ) {
2012-05-23 09:47:52 +00:00
my $ copy_clearance = $ clearance - > clone ;
$ copy_clearance - > translate ( @$ copy ) ;
if ( @ { intersection_ex ( \ @ a , [ $ copy_clearance ] ) } ) {
die "Some objects are too close; your extruder will collide with them.\n" ;
}
@ a = map @$ _ , @ { union_ex ( [ @ a , $ copy_clearance ] ) } ;
}
}
}
# check vertical clearance
{
my @ obj_copies = $ self - > object_copies ;
pop @ obj_copies ; # ignore the last copy: its height doesn't matter
2012-09-22 17:04:36 +00:00
my $ scaled_clearance = scale $ Slic3r:: Config - > extruder_clearance_height ;
if ( grep { + ( $ _ - > size ) [ Z ] > $ scaled_clearance } map @ { $ self - > objects - > [ $ _ - > [ 0 ] ] - > meshes } , @ obj_copies ) {
2012-05-23 09:47:52 +00:00
die "Some objects are too tall and cannot be printed without extruder collisions.\n" ;
}
}
}
}
2012-09-23 00:40:25 +00:00
sub init_extruders {
my $ self = shift ;
2012-09-23 00:52:31 +00:00
# map regions to extruders (ghetto mapping for now)
my % extruder_mapping = map { $ _ = > $ _ } 0 .. $# { $ self - > regions } ;
2012-09-23 00:40:25 +00:00
# initialize all extruder(s) we need
my @ used_extruders = (
0 ,
( map $ self - > config - > get ( "${_}_extruder" ) - 1 , qw( perimeter infill support_material ) ) ,
( values % extruder_mapping ) ,
) ;
for my $ extruder_id ( keys % { { map { $ _ = > 1 } @ used_extruders } } ) {
$ self - > extruders - > [ $ extruder_id ] = Slic3r::Extruder - > new (
id = > $ extruder_id ,
map { $ _ = > $ self - > config - > get ( $ _ ) - > [ $ extruder_id ] // $ self - > config - > get ( $ _ ) - > [ 0 ] } #/
@ { & Slic3r::Extruder:: OPTIONS }
) ;
}
2012-09-23 00:52:31 +00:00
# calculate regions' flows
for my $ region_id ( 0 .. $# { $ self - > regions } ) {
my $ region = $ self - > regions - > [ $ region_id ] ;
2012-09-23 00:40:25 +00:00
# per-role extruders and flows
2013-03-07 15:00:58 +00:00
for ( qw( perimeter infill top_infill ) ) {
my $ extruder_name = $ _ eq 'top_infill' ? 'infill' : $ _ ;
2012-09-23 00:52:31 +00:00
$ region - > extruders - > { $ _ } = ( $ self - > regions_count > 1 )
? $ self - > extruders - > [ $ extruder_mapping { $ region_id } ]
2013-03-07 15:00:58 +00:00
: $ self - > extruders - > [ $ self - > config - > get ( "${extruder_name}_extruder" ) - 1 ] ;
2012-09-23 00:52:31 +00:00
$ region - > flows - > { $ _ } = $ region - > extruders - > { $ _ } - > make_flow (
2012-09-23 00:40:25 +00:00
width = > $ self - > config - > get ( "${_}_extrusion_width" ) || $ self - > config - > extrusion_width ,
) ;
2012-09-23 00:52:31 +00:00
$ region - > first_layer_flows - > { $ _ } = $ region - > extruders - > { $ _ } - > make_flow (
2012-09-23 00:40:25 +00:00
layer_height = > $ self - > config - > get_value ( 'first_layer_height' ) ,
width = > $ self - > config - > first_layer_extrusion_width ,
2013-02-22 15:08:11 +00:00
) if $ self - > config - > first_layer_extrusion_width ;
2012-09-23 00:40:25 +00:00
}
}
# calculate support material flow
2013-02-04 14:48:57 +00:00
if ( $ self - > has_support_material ) {
2012-09-23 00:40:25 +00:00
my $ extruder = $ self - > extruders - > [ $ self - > config - > support_material_extruder - 1 ] ;
$ self - > support_material_flow ( $ extruder - > make_flow (
width = > $ self - > config - > support_material_extrusion_width || $ self - > config - > extrusion_width ,
) ) ;
$ self - > first_layer_support_material_flow ( $ extruder - > make_flow (
layer_height = > $ self - > config - > get_value ( 'first_layer_height' ) ,
width = > $ self - > config - > first_layer_extrusion_width ,
) ) ;
}
}
2012-05-23 09:47:52 +00:00
sub object_copies {
my $ self = shift ;
my @ oc = ( ) ;
for my $ obj_idx ( 0 .. $# { $ self - > objects } ) {
2012-09-22 14:19:24 +00:00
push @ oc , map [ $ obj_idx , $ _ ] , @ { $ self - > objects - > [ $ obj_idx ] - > copies } ;
2012-05-23 09:47:52 +00:00
}
return @ oc ;
}
2012-04-29 10:51:20 +00:00
sub layer_count {
my $ self = shift ;
2013-01-17 13:56:31 +00:00
return max ( map { scalar @ { $ _ - > layers } } @ { $ self - > objects } ) ;
2012-04-29 10:51:20 +00:00
}
2012-09-23 00:52:31 +00:00
sub regions_count {
2012-09-23 00:40:25 +00:00
my $ self = shift ;
2012-09-23 00:52:31 +00:00
return scalar @ { $ self - > regions } ;
2012-09-23 00:40:25 +00:00
}
2012-04-30 12:56:01 +00:00
sub duplicate {
2012-02-19 09:48:58 +00:00
my $ self = shift ;
2012-07-27 19:13:03 +00:00
if ( $ Slic3r:: Config - > duplicate_grid - > [ X ] > 1 || $ Slic3r:: Config - > duplicate_grid - > [ Y ] > 1 ) {
2012-04-29 10:51:20 +00:00
if ( @ { $ self - > objects } > 1 ) {
die "Grid duplication is not supported with multiple objects\n" ;
}
my $ object = $ self - > objects - > [ 0 ] ;
2012-03-06 03:55:21 +00:00
# generate offsets for copies
2012-07-27 19:13:03 +00:00
my $ dist = scale $ Slic3r:: Config - > duplicate_distance ;
2012-09-22 14:19:24 +00:00
@ { $ self - > objects - > [ 0 ] - > copies } = ( ) ;
2012-07-27 19:13:03 +00:00
for my $ x_copy ( 1 .. $ Slic3r:: Config - > duplicate_grid - > [ X ] ) {
for my $ y_copy ( 1 .. $ Slic3r:: Config - > duplicate_grid - > [ Y ] ) {
2012-09-22 14:19:24 +00:00
push @ { $ self - > objects - > [ 0 ] - > copies } , [
2012-08-25 18:14:01 +00:00
( $ object - > size - > [ X ] + $ dist ) * ( $ x_copy - 1 ) ,
( $ object - > size - > [ Y ] + $ dist ) * ( $ y_copy - 1 ) ,
2012-03-06 03:55:21 +00:00
] ;
}
}
2012-07-27 19:13:03 +00:00
} elsif ( $ Slic3r:: Config - > duplicate > 1 ) {
2012-09-22 14:19:24 +00:00
foreach my $ object ( @ { $ self - > objects } ) {
@ { $ object - > copies } = map [ 0 , 0 ] , 1 .. $ Slic3r:: Config - > duplicate ;
2012-04-11 18:44:09 +00:00
}
2012-04-30 12:56:01 +00:00
$ self - > arrange_objects ;
}
}
2012-04-11 18:44:09 +00:00
2012-04-30 12:56:01 +00:00
sub arrange_objects {
my $ self = shift ;
2012-03-06 03:55:21 +00:00
2012-09-22 14:19:24 +00:00
my $ total_parts = scalar map @ { $ _ - > copies } , @ { $ self - > objects } ;
2012-08-29 17:37:27 +00:00
my $ partx = max ( map $ _ - > size - > [ X ] , @ { $ self - > objects } ) ;
my $ party = max ( map $ _ - > size - > [ Y ] , @ { $ self - > objects } ) ;
2012-05-29 07:38:53 +00:00
2012-04-30 12:56:01 +00:00
my @ positions = Slic3r::Geometry:: arrange
2012-09-12 14:30:44 +00:00
( $ total_parts , $ partx , $ party , ( map scale $ _ , @ { $ Slic3r:: Config - > bed_size } ) , scale $ Slic3r:: Config - > min_object_distance , $ self - > config ) ;
2012-04-30 12:56:01 +00:00
2012-09-22 14:19:24 +00:00
@ { $ _ - > copies } = splice @ positions , 0 , scalar @ { $ _ - > copies } for @ { $ self - > objects } ;
2012-04-30 12:56:01 +00:00
}
2012-03-06 03:55:21 +00:00
2012-04-30 12:56:01 +00:00
sub bounding_box {
my $ self = shift ;
my @ points = ( ) ;
foreach my $ obj_idx ( 0 .. $# { $ self - > objects } ) {
my $ object = $ self - > objects - > [ $ obj_idx ] ;
2012-09-22 14:19:24 +00:00
foreach my $ copy ( @ { $ self - > objects - > [ $ obj_idx ] - > copies } ) {
2012-04-30 12:56:01 +00:00
push @ points ,
[ $ copy - > [ X ] , $ copy - > [ Y ] ] ,
2012-08-25 18:14:01 +00:00
[ $ copy - > [ X ] + $ object - > size - > [ X ] , $ copy - > [ Y ] ] ,
[ $ copy - > [ X ] + $ object - > size - > [ X ] , $ copy - > [ Y ] + $ object - > size - > [ Y ] ] ,
[ $ copy - > [ X ] , $ copy - > [ Y ] + $ object - > size - > [ Y ] ] ;
2012-04-30 12:56:01 +00:00
}
}
return Slic3r::Geometry:: bounding_box ( \ @ points ) ;
}
2012-03-06 03:55:21 +00:00
2012-04-30 12:56:01 +00:00
sub size {
my $ self = shift ;
my @ bb = $ self - > bounding_box ;
return [ $ bb [ X2 ] - $ bb [ X1 ] , $ bb [ Y2 ] - $ bb [ Y1 ] ] ;
}
2012-03-06 03:55:21 +00:00
2012-04-30 12:56:01 +00:00
sub export_gcode {
my $ self = shift ;
my % params = @ _ ;
2012-09-23 00:40:25 +00:00
$ self - > init_extruders ;
2012-04-30 12:56:01 +00:00
my $ status_cb = $ params { status_cb } || sub { } ;
my $ t0 = [ gettimeofday ] ;
# skein the STL into layers
# each layer has surfaces with holes
$ status_cb - > ( 10 , "Processing triangulated mesh" ) ;
2012-05-28 17:58:27 +00:00
$ _ - > slice ( keep_meshes = > $ params { keep_meshes } ) for @ { $ self - > objects } ;
2012-04-30 12:56:01 +00:00
# make perimeters
# this will add a set of extrusion loops to each layer
# as well as generate infill boundaries
$ status_cb - > ( 20 , "Generating perimeters" ) ;
2012-05-05 14:36:10 +00:00
$ _ - > make_perimeters for @ { $ self - > objects } ;
2012-04-30 12:56:01 +00:00
2012-09-23 00:52:31 +00:00
# simplify slices (both layer and region slices),
2012-09-22 17:04:36 +00:00
# we only need the max resolution for perimeters
2012-09-22 17:38:25 +00:00
foreach my $ layer ( map @ { $ _ - > layers } , @ { $ self - > objects } ) {
2012-11-18 16:38:08 +00:00
$ _ - > simplify ( & Slic3r:: SCALED_RESOLUTION )
2012-09-23 00:52:31 +00:00
for @ { $ layer - > slices } , ( map $ _ - > expolygon , map @ { $ _ - > slices } , @ { $ layer - > regions } ) ;
2012-09-22 17:38:25 +00:00
}
2012-06-23 18:08:08 +00:00
2013-03-07 14:47:32 +00:00
# this will assign a type (top/bottom/internal) to $layerm->slices
# and transform $layerm->fill_surfaces from expolygon
2012-12-22 22:57:39 +00:00
# to typed top/bottom/internal surfaces;
2012-04-30 12:56:01 +00:00
$ status_cb - > ( 30 , "Detecting solid surfaces" ) ;
$ _ - > detect_surfaces_type for @ { $ self - > objects } ;
# decide what surfaces are to be filled
$ status_cb - > ( 35 , "Preparing infill surfaces" ) ;
2012-09-23 00:52:31 +00:00
$ _ - > prepare_fill_surfaces for map @ { $ _ - > regions } , map @ { $ _ - > layers } , @ { $ self - > objects } ;
2012-04-30 12:56:01 +00:00
# this will detect bridges and reverse bridges
# and rearrange top/bottom/internal surfaces
$ status_cb - > ( 45 , "Detect bridges" ) ;
2013-03-07 14:47:32 +00:00
$ _ - > process_external_surfaces for map @ { $ _ - > regions } , map @ { $ _ - > layers } , @ { $ self - > objects } ;
2012-04-30 12:56:01 +00:00
# detect which fill surfaces are near external layers
# they will be split in internal and internal-solid surfaces
$ status_cb - > ( 60 , "Generating horizontal shells" ) ;
$ _ - > discover_horizontal_shells for @ { $ self - > objects } ;
2013-02-09 22:36:32 +00:00
$ _ - > clip_fill_surfaces for @ { $ self - > objects } ;
2013-02-23 20:39:13 +00:00
# the following step needs to be done before combination because it may need
# to remove only half of the combined infill
$ _ - > bridge_over_infill for @ { $ self - > objects } ;
2012-04-30 12:56:01 +00:00
# combine fill surfaces to honor the "infill every N layers" option
$ status_cb - > ( 70 , "Combining infill" ) ;
2012-07-22 18:48:38 +00:00
$ _ - > combine_infill for @ { $ self - > objects } ;
2012-04-30 12:56:01 +00:00
# this will generate extrusion paths for each layer
$ status_cb - > ( 80 , "Infilling layers" ) ;
{
my $ fill_maker = Slic3r::Fill - > new ( 'print' = > $ self ) ;
Slic3r:: parallelize (
2012-09-22 17:04:36 +00:00
items = > sub {
my @ items = ( ) ; # [obj_idx, layer_id]
for my $ obj_idx ( 0 .. $# { $ self - > objects } ) {
2012-09-23 00:52:31 +00:00
for my $ region_id ( 0 .. ( $ self - > regions_count - 1 ) ) {
push @ items , map [ $ obj_idx , $ _ , $ region_id ] , 0 .. ( $ self - > objects - > [ $ obj_idx ] - > layer_count - 1 ) ;
2012-09-22 17:04:36 +00:00
}
}
@ items ;
} ,
2012-04-30 12:56:01 +00:00
thread_cb = > sub {
my $ q = shift ;
$ Slic3r:: Geometry:: Clipper:: clipper = Math::Clipper - > new ;
my $ fills = { } ;
while ( defined ( my $ obj_layer = $ q - > dequeue ) ) {
2012-09-23 00:52:31 +00:00
my ( $ obj_idx , $ layer_id , $ region_id ) = @$ obj_layer ;
2012-04-30 12:56:01 +00:00
$ fills - > { $ obj_idx } || = { } ;
2012-09-22 17:04:36 +00:00
$ fills - > { $ obj_idx } { $ layer_id } || = { } ;
2012-09-23 00:52:31 +00:00
$ fills - > { $ obj_idx } { $ layer_id } { $ region_id } = [
$ fill_maker - > make_fill ( $ self - > objects - > [ $ obj_idx ] - > layers - > [ $ layer_id ] - > regions - > [ $ region_id ] ) ,
2012-09-22 17:04:36 +00:00
] ;
2012-04-30 12:56:01 +00:00
}
return $ fills ;
} ,
collect_cb = > sub {
my $ fills = shift ;
foreach my $ obj_idx ( keys %$ fills ) {
2012-09-22 17:04:36 +00:00
my $ object = $ self - > objects - > [ $ obj_idx ] ;
2012-04-30 12:56:01 +00:00
foreach my $ layer_id ( keys % { $ fills - > { $ obj_idx } } ) {
2012-09-22 17:04:36 +00:00
my $ layer = $ object - > layers - > [ $ layer_id ] ;
2012-09-23 00:52:31 +00:00
foreach my $ region_id ( keys % { $ fills - > { $ obj_idx } { $ layer_id } } ) {
$ layer - > regions - > [ $ region_id ] - > fills ( $ fills - > { $ obj_idx } { $ layer_id } { $ region_id } ) ;
2012-09-22 17:04:36 +00:00
}
2012-03-06 03:55:21 +00:00
}
}
2012-04-30 12:56:01 +00:00
} ,
no_threads_cb = > sub {
2012-09-23 00:52:31 +00:00
foreach my $ layerm ( map @ { $ _ - > regions } , map @ { $ _ - > layers } , @ { $ self - > objects } ) {
2012-09-22 17:04:36 +00:00
$ layerm - > fills ( [ $ fill_maker - > make_fill ( $ layerm ) ] ) ;
2012-04-30 12:56:01 +00:00
}
} ,
) ;
}
# generate support material
2013-02-04 14:48:57 +00:00
if ( $ self - > has_support_material ) {
2012-04-30 12:56:01 +00:00
$ status_cb - > ( 85 , "Generating support material" ) ;
2012-09-22 17:04:36 +00:00
$ _ - > generate_support_material for @ { $ self - > objects } ;
2012-04-30 12:56:01 +00:00
}
# free memory (note that support material needs fill_surfaces)
2012-09-23 00:52:31 +00:00
$ _ - > fill_surfaces ( undef ) for map @ { $ _ - > regions } , map @ { $ _ - > layers } , @ { $ self - > objects } ;
2012-04-30 12:56:01 +00:00
# make skirt
$ status_cb - > ( 88 , "Generating skirt" ) ;
$ self - > make_skirt ;
2012-10-14 20:10:49 +00:00
$ self - > make_brim ; # must come after make_skirt
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 ;
}
2012-04-30 12:56:01 +00:00
# output everything to a G-code file
my $ output_file = $ self - > expanded_output_filepath ( $ params { output_file } ) ;
2012-11-23 10:24:04 +00:00
$ status_cb - > ( 90 , "Exporting G-code" . ( $ output_file ? " to $output_file" : "" ) ) ;
2012-11-21 19:41:14 +00:00
$ self - > write_gcode ( $ params { output_fh } || $ output_file ) ;
2012-04-30 12:56:01 +00:00
# run post-processing scripts
2012-07-27 19:13:03 +00:00
if ( @ { $ Slic3r:: Config - > post_process } ) {
2012-04-30 12:56:01 +00:00
$ status_cb - > ( 95 , "Running post-processing scripts" ) ;
2012-07-27 19:13:03 +00:00
$ Slic3r:: Config - > setenv ;
for ( @ { $ Slic3r:: Config - > post_process } ) {
2012-04-30 12:56:01 +00:00
Slic3r:: debugf " '%s' '%s'\n" , $ _ , $ output_file ;
system ( $ _ , $ output_file ) ;
2012-02-19 09:48:58 +00:00
}
}
2012-04-30 12:56:01 +00:00
# output some statistics
2012-11-21 19:41:14 +00:00
unless ( $ params { quiet } ) {
$ self - > processing_time ( tv_interval ( $ t0 ) ) ;
printf "Done. Process took %d minutes and %.3f seconds\n" ,
int ( $ self - > processing_time / 60 ) ,
$ self - > processing_time - int ( $ self - > processing_time / 60 ) * 60 ;
# TODO: more statistics!
printf "Filament required: %.1fmm (%.1fcm3)\n" ,
$ self - > total_extrusion_length , $ self - > total_extrusion_volume ;
}
2012-02-19 09:48:58 +00:00
}
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 = @ _ ;
2012-09-28 14:32:53 +00:00
# this shouldn't be needed, but we're currently relying on ->make_surfaces() which
# calls ->perimeter_flow
$ self - > init_extruders ;
2012-05-30 10:06:01 +00:00
$ _ - > slice ( keep_meshes = > $ params { keep_meshes } ) for @ { $ self - > objects } ;
2012-04-30 12:56:01 +00:00
$ self - > arrange_objects ;
my $ output_file = $ self - > expanded_output_filepath ( $ params { output_file } ) ;
$ output_file =~ s/\.gcode$/.svg/i ;
2013-01-13 09:18:34 +00:00
Slic3r:: open ( \ my $ fh , ">" , $ output_file ) or die "Failed to open $output_file for writing\n" ;
2012-04-30 12:56:01 +00:00
print "Exporting to $output_file..." ;
my $ print_size = $ self - > size ;
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
} ;
2012-06-11 12:47:48 +00:00
my @ previous_layer_slices = ( ) ;
2012-04-30 12:56:01 +00:00
for my $ layer_id ( 0 .. $ self - > layer_count - 1 ) {
my @ layers = map $ _ - > layers - > [ $ layer_id ] , @ { $ self - > objects } ;
printf $ fh qq{ <g id="layer%d" slic3r:z="%s"> \ n } , $ layer_id , unscale + ( grep defined $ _ , @ layers ) [ 0 ] - > slice_z ;
2012-06-11 12:47:48 +00:00
my @ current_layer_slices = ( ) ;
2012-06-11 11:50:38 +00:00
for my $ obj_idx ( 0 .. $# { $ self - > objects } ) {
my $ layer = $ self - > objects - > [ $ obj_idx ] - > layers - > [ $ layer_id ] or next ;
2012-04-30 12:56:01 +00:00
# sort slices so that the outermost ones come first
2012-09-28 14:32:53 +00:00
my @ slices = sort { $ a - > contour - > encloses_point ( $ b - > contour - > [ 0 ] ) ? 0 : 1 } @ { $ layer - > slices } ;
2012-09-22 14:19:24 +00:00
foreach my $ copy ( @ { $ self - > objects - > [ $ obj_idx ] - > copies } ) {
2012-04-30 12:56:01 +00:00
foreach my $ slice ( @ slices ) {
2012-09-28 14:32:53 +00:00
my $ expolygon = $ slice - > clone ;
2012-04-30 12:56:01 +00:00
$ expolygon - > translate ( @$ copy ) ;
$ print_polygon - > ( $ expolygon - > contour , 'contour' ) ;
$ print_polygon - > ( $ _ , 'hole' ) for $ expolygon - > holes ;
2012-06-11 12:47:48 +00:00
push @ current_layer_slices , $ expolygon ;
2012-04-30 12:56:01 +00:00
}
}
}
2012-06-11 12:47:48 +00:00
# generate support material
2013-02-04 14:48:57 +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 ] ,
$ expolygon ,
) ;
@$ 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
2012-09-21 14:52:05 +00:00
my $ support_point = nearest_point ( $ expolygon - > contour - > [ 0 ] , \ @ supported_points )
or next ;
2012-09-28 14:32:53 +00:00
my $ anchor_point = nearest_point ( $ support_point , $ expolygon - > 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 ;
print "Done.\n" ;
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 ;
2012-07-27 19:13:03 +00:00
return unless $ Slic3r:: Config - > skirts > 0 ;
2011-11-13 17:41:12 +00:00
# collect points from all layers contained in skirt height
2012-04-29 10:51:20 +00:00
my @ points = ( ) ;
foreach my $ obj_idx ( 0 .. $# { $ self - > objects } ) {
2013-01-18 16:36:01 +00:00
my $ skirt_height = $ Slic3r:: Config - > skirt_height ;
$ skirt_height = $ self - > objects - > [ $ obj_idx ] - > layer_count if $ skirt_height > $ self - > objects - > [ $ obj_idx ] - > layer_count ;
2013-01-01 22:28:48 +00:00
my @ layers = map $ self - > objects - > [ $ obj_idx ] - > layers - > [ $ _ ] , 0 .. ( $ skirt_height - 1 ) ;
2012-04-29 10:51:20 +00:00
my @ layer_points = (
2012-09-22 17:38:25 +00:00
( map @$ _ , map @$ _ , map @ { $ _ - > slices } , @ layers ) ,
2012-09-23 00:52:31 +00:00
( map @$ _ , map @ { $ _ - > thin_walls } , map @ { $ _ - > regions } , @ layers ) ,
2012-07-20 12:39:07 +00:00
( map @ { $ _ - > unpack - > polyline } , map @ { $ _ - > support_fills - > paths } , grep $ _ - > support_fills , @ layers ) ,
2012-04-29 10:51:20 +00:00
) ;
2012-09-22 14:19:24 +00:00
push @ points , map move_points ( $ _ , @ layer_points ) , @ { $ self - > objects - > [ $ obj_idx ] - > copies } ;
2012-04-29 10:51:20 +00:00
}
2012-03-31 16:32:53 +00:00
return if @ points < 3 ; # at least three points required for a convex hull
2011-11-13 17:41:12 +00:00
# find out convex hull
2012-04-29 10:51:20 +00:00
my $ convex_hull = convex_hull ( \ @ points ) ;
2011-11-13 17:41:12 +00:00
2012-10-29 10:17:57 +00:00
my @ extruded_length = ( ) ; # for each extruder
2013-02-22 15:08:11 +00:00
# TODO: use each extruder's own flow
my $ spacing = $ self - > objects - > [ 0 ] - > layers - > [ 0 ] - > regions - > [ 0 ] - > perimeter_flow - > spacing ;
2012-10-29 10:17:57 +00:00
my $ first_layer_height = $ Slic3r:: Config - > get_value ( 'first_layer_height' ) ;
my @ extruders_e_per_mm = ( ) ;
my $ extruder_idx = 0 ;
2011-11-13 17:41:12 +00:00
# draw outlines from outside to inside
2012-10-29 10:17:57 +00:00
# loop while we have less skirts than required or any extruder hasn't reached the min length if any
my $ distance = scale $ Slic3r:: Config - > skirt_distance ;
2012-07-27 19:13:03 +00:00
for ( my $ i = $ Slic3r:: Config - > skirts ; $ i > 0 ; $ i - - ) {
2012-10-29 10:17:57 +00:00
$ distance += scale $ spacing ;
2012-11-04 23:17:46 +00:00
my ( $ loop ) = Slic3r::Geometry::Clipper:: offset ( [ $ convex_hull ] , $ distance , 0.0001 , JT_ROUND ) ;
2012-10-14 20:10:49 +00:00
push @ { $ self - > skirt } , Slic3r::ExtrusionLoop - > pack (
2012-10-29 10:17:57 +00:00
polygon = > Slic3r::Polygon - > new ( @$ loop ) ,
2012-10-15 08:57:15 +00:00
role = > EXTR_ROLE_SKIRT ,
2012-10-29 10:17:57 +00:00
flow_spacing = > $ spacing ,
2011-12-30 18:59:51 +00:00
) ;
2012-10-29 10:17:57 +00:00
if ( $ Slic3r:: Config - > min_skirt_length > 0 ) {
bless $ loop , 'Slic3r::Polygon' ;
$ extruded_length [ $ extruder_idx ] || = 0 ;
$ extruders_e_per_mm [ $ extruder_idx ] || = $ self - > extruders - > [ $ extruder_idx ] - > e_per_mm ( $ spacing , $ first_layer_height ) ;
$ extruded_length [ $ extruder_idx ] += unscale $ loop - > length * $ extruders_e_per_mm [ $ extruder_idx ] ;
$ i + + if defined first { ( $ extruded_length [ $ _ ] // 0 ) < $ Slic3r:: Config - > min_skirt_length } 0 .. $# { $ self - > extruders } ;
if ( $ extruded_length [ $ extruder_idx ] >= $ Slic3r:: Config - > min_skirt_length ) {
if ( $ extruder_idx < $# { $ self - > extruders } ) {
$ extruder_idx + + ;
next ;
}
}
}
2011-11-13 17:41:12 +00:00
}
2012-10-29 10:17:57 +00:00
@ { $ self - > skirt } = reverse @ { $ self - > skirt } ;
2012-02-19 11:03:36 +00:00
}
2012-06-23 19:31:29 +00:00
sub make_brim {
my $ self = shift ;
2012-07-27 19:13:03 +00:00
return unless $ Slic3r:: Config - > brim_width > 0 ;
2012-06-23 19:31:29 +00:00
2013-02-22 15:08:11 +00:00
my $ flow = $ self - > objects - > [ 0 ] - > layers - > [ 0 ] - > regions - > [ 0 ] - > perimeter_flow ;
my $ grow_distance = $ flow - > scaled_width / 2 ;
2012-06-23 19:31:29 +00:00
my @ islands = ( ) ; # array of polygons
foreach my $ obj_idx ( 0 .. $# { $ self - > objects } ) {
2012-08-06 18:54:49 +00:00
my $ layer0 = $ self - > objects - > [ $ obj_idx ] - > layers - > [ 0 ] ;
my @ object_islands = (
( map $ _ - > contour , @ { $ layer0 - > slices } ) ,
2012-09-23 00:52:31 +00:00
( map { $ _ - > isa ( 'Slic3r::Polygon' ) ? $ _ : $ _ - > grow ( $ grow_distance ) } map @ { $ _ - > thin_walls } , @ { $ layer0 - > regions } ) ,
2012-08-07 20:04:58 +00:00
( map $ _ - > unpack - > polyline - > grow ( $ grow_distance ) , map @ { $ _ - > support_fills - > paths } , grep $ _ - > support_fills , $ layer0 ) ,
2012-08-06 18:54:49 +00:00
) ;
2012-09-22 14:19:24 +00:00
foreach my $ copy ( @ { $ self - > objects - > [ $ obj_idx ] - > copies } ) {
2012-06-23 19:31:29 +00:00
push @ islands , map $ _ - > clone - > translate ( @$ copy ) , @ object_islands ;
}
}
2012-10-14 20:10:49 +00:00
# if brim touches skirt, make it around skirt too
2013-02-22 15:08:11 +00:00
# TODO: calculate actual skirt width (using each extruder's flow in multi-extruder setups)
if ( $ Slic3r:: Config - > skirt_distance + ( ( $ Slic3r:: Config - > skirts - 1 ) * $ flow - > spacing ) <= $ Slic3r:: Config - > brim_width ) {
2012-10-14 20:10:49 +00:00
push @ islands , map $ _ - > unpack - > split_at_first_point - > polyline - > grow ( $ grow_distance ) , @ { $ self - > skirt } ;
}
2013-02-22 15:08:11 +00:00
my $ num_loops = sprintf "%.0f" , $ Slic3r:: 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
2012-07-20 12:39:07 +00:00
push @ { $ self - > brim } , Slic3r::ExtrusionLoop - > pack (
2012-10-15 08:57:15 +00:00
polygon = > Slic3r::Polygon - > new ( $ _ ) ,
role = > EXTR_ROLE_SKIRT ,
2013-02-22 15:08:11 +00:00
flow_spacing = > $ flow - > spacing ,
2013-02-24 15:40:14 +00:00
) for Slic3r::Geometry::Clipper:: offset ( \ @ islands , ( $ i - 0.5 ) * $ flow - > scaled_spacing , undef , JT_SQUARE ) ; # -0.5 because islands are not represented by their centerlines
2012-09-28 13:27:33 +00:00
# TODO: we need the offset inwards/offset outwards logic to avoid overlapping extrusions
2012-06-23 19:31:29 +00:00
}
}
2012-04-30 12:56:01 +00:00
sub write_gcode {
2011-09-03 18:47:38 +00:00
my $ self = shift ;
my ( $ file ) = @ _ ;
2012-11-21 19:41:14 +00:00
# open output gcode file if we weren't supplied a file-handle
my $ fh ;
if ( ref $ file eq 'IO::Scalar' ) {
$ fh = $ file ;
} else {
2013-01-13 09:18:34 +00:00
Slic3r:: open ( \ $ fh , ">" , $ file )
2012-11-21 19:41:14 +00:00
or die "Failed to open $file for writing\n" ;
}
2011-09-03 18:47:38 +00:00
2011-12-01 21:20:48 +00:00
# write some information
my @ lt = localtime ;
2012-05-01 13:01:56 +00:00
printf $ fh "; generated by Slic3r $Slic3r::VERSION on %04d-%02d-%02d at %02d:%02d:%02d\n\n" ,
2011-12-30 17:57:58 +00:00
$ lt [ 5 ] + 1900 , $ lt [ 4 ] + 1 , $ lt [ 3 ] , $ lt [ 2 ] , $ lt [ 1 ] , $ lt [ 0 ] ;
2012-02-05 19:55:17 +00:00
2012-07-27 19:13:03 +00:00
print $ fh "; $_\n" foreach split /\R/ , $ Slic3r:: Config - > notes ;
print $ fh "\n" if $ Slic3r:: Config - > notes ;
2011-12-01 21:20:48 +00:00
2012-10-25 10:21:04 +00:00
for ( qw( layer_height perimeters top_solid_layers bottom_solid_layers fill_density perimeter_speed infill_speed travel_speed scale ) ) {
2012-07-27 19:13:03 +00:00
printf $ fh "; %s = %s\n" , $ _ , $ Slic3r:: Config - > $ _ ;
2011-12-01 21:20:48 +00:00
}
2012-06-28 14:22:11 +00:00
for ( qw( nozzle_diameter filament_diameter extrusion_multiplier ) ) {
2012-07-27 19:13:03 +00:00
printf $ fh "; %s = %s\n" , $ _ , $ Slic3r:: Config - > $ _ - > [ 0 ] ;
2012-06-28 14:22:11 +00:00
}
2012-10-28 23:31:25 +00:00
printf $ fh "; perimeters extrusion width = %.2fmm\n" , $ self - > regions - > [ 0 ] - > flows - > { perimeter } - > width ;
printf $ fh "; infill extrusion width = %.2fmm\n" , $ self - > regions - > [ 0 ] - > flows - > { infill } - > width ;
printf $ fh "; support material extrusion width = %.2fmm\n" , $ self - > support_material_flow - > width
if $ self - > support_material_flow ;
2013-02-22 15:08:11 +00:00
printf $ fh "; first layer extrusion width = %.2fmm\n" , $ self - > regions - > [ 0 ] - > first_layer_flows - > { perimeter } - > width
if $ self - > regions - > [ 0 ] - > first_layer_flows - > { perimeter } ;
2011-12-01 21:20:48 +00:00
print $ fh "\n" ;
2012-05-20 18:07:39 +00:00
# set up our extruder object
2012-09-23 00:40:25 +00:00
my $ gcodegen = Slic3r::GCode - > new (
2013-01-17 13:56:31 +00:00
multiple_extruders = > ( @ { $ self - > extruders } > 1 ) ,
layer_count = > $ self - > layer_count ,
2012-09-23 00:40:25 +00:00
) ;
2012-07-27 19:13:03 +00:00
my $ min_print_speed = 60 * $ Slic3r:: Config - > min_print_speed ;
2012-06-28 12:44:54 +00:00
my $ dec = $ gcodegen - > dec ;
2013-03-09 18:51:09 +00:00
print $ fh "G21 ; set units to millimeters\n" ;
2012-07-27 19:13:03 +00:00
print $ fh $ gcodegen - > set_fan ( 0 , 1 ) if $ Slic3r:: Config - > cooling && $ Slic3r:: Config - > disable_fan_first_layers ;
2012-05-20 18:07:39 +00:00
2011-09-03 18:47:38 +00:00
# write start commands to file
2012-07-27 19:13:03 +00:00
printf $ fh $ gcodegen - > set_bed_temperature ( $ Slic3r:: Config - > first_layer_bed_temperature , 1 ) ,
2013-03-09 18:51:09 +00:00
if $ Slic3r:: Config - > first_layer_bed_temperature && $ Slic3r:: Config - > start_gcode !~ /M(?:190|140)/i ;
2012-08-22 15:58:38 +00:00
my $ print_first_layer_temperature = sub {
2012-09-23 00:40:25 +00:00
for my $ t ( grep $ self - > extruders - > [ $ _ ] , 0 .. $# { $ Slic3r:: Config - > first_layer_temperature } ) {
printf $ fh $ gcodegen - > set_temperature ( $ self - > extruders - > [ $ t ] - > first_layer_temperature , 0 , $ t )
if $ self - > extruders - > [ $ t ] - > first_layer_temperature ;
2012-08-22 15:58:38 +00:00
}
} ;
2013-03-09 18:51:09 +00:00
$ print_first_layer_temperature - > ( ) if $ Slic3r:: Config - > start_gcode !~ /M(?:109|104)/i ;
2012-07-27 19:13:03 +00:00
printf $ fh "%s\n" , $ Slic3r:: Config - > replace_options ( $ Slic3r:: Config - > start_gcode ) ;
2012-09-23 00:40:25 +00:00
for my $ t ( grep $ self - > extruders - > [ $ _ ] , 0 .. $# { $ Slic3r:: Config - > first_layer_temperature } ) {
printf $ fh $ gcodegen - > set_temperature ( $ self - > extruders - > [ $ t ] - > first_layer_temperature , 1 , $ t )
2013-03-09 18:51:09 +00:00
if $ self - > extruders - > [ $ t ] - > first_layer_temperature && $ Slic3r:: Config - > start_gcode !~ /M(?:109|104)/i ;
2012-06-28 14:22:11 +00:00
}
2011-09-03 18:47:38 +00:00
print $ fh "G90 ; use absolute coordinates\n" ;
2012-07-27 19:13:03 +00:00
if ( $ Slic3r:: Config - > gcode_flavor =~ /^(?:reprap|teacup)$/ ) {
2012-07-06 17:57:58 +00:00
printf $ fh $ gcodegen - > reset_e ;
2013-01-17 13:56:31 +00:00
if ( $ Slic3r:: Config - > gcode_flavor =~ /^(?:reprap|makerbot|sailfish)$/ ) {
2012-07-27 19:13:03 +00:00
if ( $ Slic3r:: Config - > use_relative_e_distances ) {
2012-02-24 22:12:16 +00:00
print $ fh "M83 ; use relative distances for extrusion\n" ;
} else {
print $ fh "M82 ; use absolute distances for extrusion\n" ;
}
2012-02-20 10:44:30 +00:00
}
2011-09-03 18:47:38 +00:00
}
2012-02-19 09:48:58 +00:00
# calculate X,Y shift to center print around specified origin
2012-04-30 12:56:01 +00:00
my @ print_bb = $ self - > bounding_box ;
2012-02-19 09:48:58 +00:00
my @ shift = (
2012-07-27 19:13:03 +00:00
$ Slic3r:: Config - > print_center - > [ X ] - ( unscale ( $ print_bb [ X2 ] - $ print_bb [ X1 ] ) / 2 ) - unscale $ print_bb [ X1 ] ,
$ Slic3r:: Config - > print_center - > [ Y ] - ( unscale ( $ print_bb [ Y2 ] - $ print_bb [ Y1 ] ) / 2 ) - unscale $ print_bb [ Y1 ] ,
2011-09-26 09:42:08 +00:00
) ;
2011-09-05 10:21:27 +00:00
2012-08-23 13:42:58 +00:00
# initialize a motion planner for object-to-object travel moves
if ( $ Slic3r:: Config - > avoid_crossing_perimeters ) {
my $ distance_from_objects = 1 ;
# compute the offsetted convex hull for each object and repeat it for each copy.
my @ islands = ( ) ;
foreach my $ obj_idx ( 0 .. $# { $ self - > objects } ) {
2013-02-04 18:33:30 +00:00
my $ convex_hull = convex_hull ( [
2012-10-24 09:49:31 +00:00
map @ { $ _ - > contour } , map @ { $ _ - > slices } , @ { $ self - > objects - > [ $ obj_idx ] - > layers } ,
2013-02-04 18:33:30 +00:00
] ) ;
# discard layers only containing thin walls (offset would fail on an empty polygon)
if ( @$ convex_hull ) {
my @ island = Slic3r::ExPolygon - > new ( $ convex_hull )
- > translate ( scale $ shift [ X ] , scale $ shift [ Y ] )
- > offset_ex ( scale $ distance_from_objects , 1 , JT_SQUARE ) ;
foreach my $ copy ( @ { $ self - > objects - > [ $ obj_idx ] - > copies } ) {
push @ islands , map $ _ - > clone - > translate ( @$ copy ) , @ island ;
}
2012-08-23 13:42:58 +00:00
}
}
2012-08-23 19:10:04 +00:00
$ gcodegen - > external_mp ( Slic3r::GCode::MotionPlanner - > new (
2012-08-23 13:42:58 +00:00
islands = > union_ex ( [ map @$ _ , @ islands ] ) ,
no_internal = > 1 ,
2012-08-23 19:10:04 +00:00
) ) ;
2012-08-23 13:42:58 +00:00
}
2012-05-20 18:07:39 +00:00
# prepare the logic to print one layer
2012-05-31 07:37:39 +00:00
my $ skirt_done = 0 ; # count of skirt layers done
2012-06-30 14:14:45 +00:00
my $ brim_done = 0 ;
2013-03-10 14:36:52 +00:00
my $ second_layer_things_done = 0 ;
2012-08-23 13:42:58 +00:00
my $ last_obj_copy = "" ;
2012-05-20 18:07:39 +00:00
my $ extrude_layer = sub {
2013-03-10 14:36:52 +00:00
my ( $ layer , $ object_copies ) = @ _ ;
2012-05-20 18:07:39 +00:00
my $ gcode = "" ;
2013-03-10 14:36:52 +00:00
if ( ! $ second_layer_things_done && $ layer - > id == 1 ) {
2012-09-23 00:40:25 +00:00
for my $ t ( grep $ self - > extruders - > [ $ _ ] , 0 .. $# { $ Slic3r:: Config - > temperature } ) {
$ gcode . = $ gcodegen - > set_temperature ( $ self - > extruders - > [ $ t ] - > temperature , 0 , $ t )
if $ self - > extruders - > [ $ t ] - > temperature && $ self - > extruders - > [ $ t ] - > temperature != $ self - > extruders - > [ $ t ] - > first_layer_temperature ;
2012-06-28 14:22:11 +00:00
}
2012-07-27 19:13:03 +00:00
$ gcode . = $ gcodegen - > set_bed_temperature ( $ Slic3r:: Config - > bed_temperature )
2012-10-24 20:57:19 +00:00
if $ Slic3r:: Config - > bed_temperature && $ Slic3r:: Config - > bed_temperature != $ Slic3r:: Config - > first_layer_bed_temperature ;
2012-02-26 13:54:38 +00:00
}
2013-02-03 16:23:50 +00:00
# set new layer, but don't move Z as support material contact areas may need an intermediate one
2013-03-10 14:36:52 +00:00
$ gcode . = $ gcodegen - > change_layer ( $ layer ) ;
2012-06-28 12:44:54 +00:00
$ gcodegen - > elapsed_time ( 0 ) ;
2012-02-25 20:01:00 +00:00
2012-12-20 16:01:01 +00:00
# prepare callback to call as soon as a Z command is generated
$ gcodegen - > move_z_callback ( sub {
$ gcodegen - > move_z_callback ( undef ) ; # circular ref or not?
2012-12-20 16:03:50 +00:00
return "" if ! $ Slic3r:: Config - > layer_gcode ;
return $ Slic3r:: Config - > replace_options ( $ Slic3r:: Config - > layer_gcode ) . "\n" ;
2012-12-20 16:01:01 +00:00
} ) ;
2012-02-25 20:01:00 +00:00
2012-04-29 10:51:20 +00:00
# extrude skirt
2012-07-27 19:13:03 +00:00
if ( $ skirt_done < $ Slic3r:: Config - > skirt_height ) {
2012-08-23 13:42:58 +00:00
$ gcodegen - > set_shift ( @ shift ) ;
2012-10-28 18:24:24 +00:00
$ gcode . = $ gcodegen - > set_extruder ( $ self - > extruders - > [ 0 ] ) ; # move_z requires extruder
$ gcode . = $ gcodegen - > move_z ( $ gcodegen - > layer - > print_z ) ;
2012-06-23 19:31:29 +00:00
# skip skirt if we have a large brim
2013-03-10 14:36:52 +00:00
if ( $ layer - > id < $ Slic3r:: Config - > skirt_height ) {
2012-10-24 17:13:40 +00:00
# distribute skirt loops across all extruders
for my $ i ( 0 .. $# { $ self - > skirt } ) {
2012-11-05 13:26:55 +00:00
# when printing layers > 0 ignore 'min_skirt_length' and
# just use the 'skirts' setting; also just use the current extruder
2013-03-10 14:36:52 +00:00
last if ( $ layer - > id > 0 ) && ( $ i >= $ Slic3r:: Config - > skirts ) ;
2012-11-05 13:26:55 +00:00
$ gcode . = $ gcodegen - > set_extruder ( $ self - > extruders - > [ ( $ i / @ { $ self - > extruders } ) % @ { $ self - > extruders } ] )
2013-03-10 14:36:52 +00:00
if $ layer - > id == 0 ;
2012-10-24 17:13:40 +00:00
$ gcode . = $ gcodegen - > extrude_loop ( $ self - > skirt - > [ $ i ] , 'skirt' ) ;
}
2012-05-20 18:07:39 +00:00
}
2012-05-31 07:37:39 +00:00
$ skirt_done + + ;
2012-08-23 19:10:04 +00:00
$ gcodegen - > straight_once ( 1 ) ;
2012-04-29 10:51:20 +00:00
}
2011-09-05 18:00:59 +00:00
2012-06-23 19:31:29 +00:00
# extrude brim
2013-03-10 14:36:52 +00:00
if ( ! $ brim_done ) {
2012-11-07 22:06:32 +00:00
$ gcode . = $ gcodegen - > set_extruder ( $ self - > extruders - > [ $ Slic3r:: Config - > support_material_extruder - 1 ] ) ; # move_z requires extruder
2012-10-28 18:24:24 +00:00
$ gcode . = $ gcodegen - > move_z ( $ gcodegen - > layer - > print_z ) ;
2012-11-06 18:54:21 +00:00
$ gcodegen - > set_shift ( @ shift ) ;
2012-06-28 12:44:54 +00:00
$ gcode . = $ gcodegen - > extrude_loop ( $ _ , 'brim' ) for @ { $ self - > brim } ;
2012-06-30 14:14:45 +00:00
$ brim_done = 1 ;
2012-08-23 19:10:04 +00:00
$ gcodegen - > straight_once ( 1 ) ;
2012-06-23 19:31:29 +00:00
}
2013-03-10 14:36:52 +00:00
for my $ copy ( @$ object_copies ) {
$ gcodegen - > new_object ( 1 ) if $ last_obj_copy && $ last_obj_copy ne "$copy" ;
$ last_obj_copy = "$copy" ;
2012-05-20 18:07:39 +00:00
2012-11-06 18:54:21 +00:00
$ gcodegen - > set_shift ( map $ shift [ $ _ ] + unscale $ copy - > [ $ _ ] , X , Y ) ;
2012-05-20 18:07:39 +00:00
2012-10-28 15:59:20 +00:00
# extrude support material before other things because it might use a lower Z
# and also because we avoid travelling on other things when printing it
2013-02-04 14:48:57 +00:00
if ( $ self - > has_support_material ) {
2013-02-03 16:23:50 +00:00
$ gcode . = $ gcodegen - > move_z ( $ layer - > support_material_contact_z )
if ( $ layer - > support_contact_fills && @ { $ layer - > support_contact_fills - > paths } ) ;
2012-10-28 15:59:20 +00:00
$ gcode . = $ gcodegen - > set_extruder ( $ self - > extruders - > [ $ Slic3r:: Config - > support_material_extruder - 1 ] ) ;
2013-02-03 16:23:50 +00:00
if ( $ layer - > support_contact_fills ) {
$ gcode . = $ gcodegen - > extrude_path ( $ _ , 'support material contact area' )
2013-02-05 16:27:45 +00:00
for $ layer - > support_contact_fills - > chained_path ( $ gcodegen - > last_pos ) ;
2012-11-02 22:39:08 +00:00
}
2012-10-28 15:59:20 +00:00
$ gcode . = $ gcodegen - > move_z ( $ layer - > print_z ) ;
2012-11-02 22:39:08 +00:00
if ( $ layer - > support_fills ) {
$ gcode . = $ gcodegen - > extrude_path ( $ _ , 'support material' )
2013-02-05 16:27:45 +00:00
for $ layer - > support_fills - > chained_path ( $ gcodegen - > last_pos ) ;
2012-11-02 22:39:08 +00:00
}
2012-10-28 15:59:20 +00:00
}
2012-10-30 09:45:55 +00:00
# set actual Z - this will force a retraction
2012-10-28 15:59:20 +00:00
$ gcode . = $ gcodegen - > move_z ( $ layer - > print_z ) ;
2012-05-20 18:07:39 +00:00
2013-03-10 16:28:03 +00:00
# tweak region ordering to save toolchanges
my @ region_ids = 0 .. ( $ self - > regions_count - 1 ) ;
if ( $ gcodegen - > multiple_extruders ) {
my $ last_extruder = $ gcodegen - > extruder ;
my $ best_region_id = first { $ self - > regions - > [ $ _ ] - > extruders - > { perimeter } eq $ last_extruder } @ region_ids ;
2013-03-10 16:40:19 +00:00
@ region_ids = ( $ best_region_id , grep $ _ != $ best_region_id , @ region_ids ) if $ best_region_id ;
2013-03-10 16:28:03 +00:00
}
foreach my $ region_id ( @ region_ids ) {
2012-09-23 00:52:31 +00:00
my $ layerm = $ layer - > regions - > [ $ region_id ] ;
my $ region = $ self - > regions - > [ $ region_id ] ;
2012-09-22 17:04:36 +00:00
2013-03-10 15:09:03 +00:00
my @ islands = ( ) ;
if ( $ Slic3r:: Config - > avoid_crossing_perimeters ) {
push @ islands , map + { perimeters = > [] , fills = > [] } , @ { $ layer - > slices } ;
PERIMETER: foreach my $ perimeter ( @ { $ layerm - > perimeters } ) {
$ perimeter = $ perimeter - > unpack ;
for my $ i ( 0 .. $# { $ layer - > slices } - 1 ) {
if ( $ layer - > slices - > [ $ i ] - > contour - > encloses_point ( $ perimeter - > first_point ) ) {
push @ { $ islands [ $ i ] { perimeters } } , $ perimeter ;
next PERIMETER ;
}
}
push @ { $ islands [ - 1 ] { perimeters } } , $ perimeter ; # optimization
}
FILL: foreach my $ fill ( @ { $ layerm - > fills } ) {
for my $ i ( 0 .. $# { $ layer - > slices } - 1 ) {
$ fill = $ fill - > unpack ;
if ( $ layer - > slices - > [ $ i ] - > contour - > encloses_point ( $ fill - > first_point ) ) {
push @ { $ islands [ $ i ] { fills } } , $ fill ;
next FILL ;
}
}
push @ { $ islands [ - 1 ] { fills } } , $ fill ; # optimization
}
} else {
push @ islands , {
perimeters = > $ layerm - > perimeters ,
fills = > $ layerm - > fills ,
} ;
2012-10-24 17:38:18 +00:00
}
2012-09-22 17:04:36 +00:00
2013-03-10 15:09:03 +00:00
foreach my $ island ( @ islands ) {
2013-03-10 16:40:19 +00:00
my $ extrude_perimeters = sub {
next if ! @ { $ island - > { perimeters } } ;
2013-03-10 15:09:03 +00:00
$ gcode . = $ gcodegen - > set_extruder ( $ region - > extruders - > { perimeter } ) ;
$ gcode . = $ gcodegen - > extrude ( $ _ , 'perimeter' ) for @ { $ island - > { perimeters } } ;
2013-03-10 16:40:19 +00:00
} ;
2013-03-10 15:09:03 +00:00
2013-03-10 16:40:19 +00:00
my $ extrude_fills = sub {
next if ! @ { $ island - > { fills } } ;
2013-03-10 15:09:03 +00:00
$ gcode . = $ gcodegen - > set_extruder ( $ region - > extruders - > { infill } ) ;
for my $ fill ( @ { $ island - > { fills } } ) {
if ( $ fill - > isa ( 'Slic3r::ExtrusionPath::Collection' ) ) {
$ gcode . = $ gcodegen - > extrude ( $ _ , 'fill' )
for $ fill - > chained_path ( $ gcodegen - > last_pos ) ;
} else {
$ gcode . = $ gcodegen - > extrude ( $ fill , 'fill' ) ;
}
2012-10-24 17:38:18 +00:00
}
2013-03-10 16:40:19 +00:00
} ;
# give priority to infill if we were already using its extruder
if ( $ gcodegen - > multiple_extruders && $ region - > extruders - > { infill } eq $ gcodegen - > extruder ) {
$ extrude_fills - > ( ) ;
$ extrude_perimeters - > ( ) ;
} else {
$ extrude_perimeters - > ( ) ;
$ extrude_fills - > ( ) ;
2012-09-22 17:04:36 +00:00
}
2012-07-20 13:02:25 +00:00
}
2012-05-20 18:07:39 +00:00
}
2011-09-26 08:52:58 +00:00
}
2012-06-23 16:31:24 +00:00
return if ! $ gcode ;
2012-02-25 20:01:00 +00:00
2012-07-27 19:13:03 +00:00
my $ fan_speed = $ Slic3r:: Config - > fan_always_on ? $ Slic3r:: Config - > min_fan_speed : 0 ;
2012-02-25 20:01:00 +00:00
my $ speed_factor = 1 ;
2012-07-27 19:13:03 +00:00
if ( $ Slic3r:: Config - > cooling ) {
2012-06-28 12:44:54 +00:00
my $ layer_time = $ gcodegen - > elapsed_time ;
2013-03-10 14:36:52 +00:00
Slic3r:: debugf "Layer %d estimated printing time: %d seconds\n" , $ layer - > id , $ layer_time ;
2012-07-27 19:13:03 +00:00
if ( $ layer_time < $ Slic3r:: Config - > slowdown_below_layer_time ) {
$ fan_speed = $ Slic3r:: Config - > max_fan_speed ;
$ speed_factor = $ layer_time / $ Slic3r:: Config - > slowdown_below_layer_time ;
} elsif ( $ layer_time < $ Slic3r:: Config - > fan_below_layer_time ) {
$ fan_speed = $ Slic3r:: Config - > max_fan_speed - ( $ Slic3r:: Config - > max_fan_speed - $ Slic3r:: Config - > min_fan_speed )
* ( $ layer_time - $ Slic3r:: Config - > slowdown_below_layer_time )
/ ($Slic3r::Config->fan_below_layer_time - $Slic3r::Config->slowdown_below_layer_time); #/
2012-02-25 20:01:00 +00:00
}
2012-02-25 20:56:36 +00:00
Slic3r:: debugf " fan = %d%%, speed = %d%%\n" , $ fan_speed , $ speed_factor * 100 ;
if ( $ speed_factor < 1 ) {
2012-10-24 20:55:03 +00:00
$ gcode =~ s /^(?=.*? [XY])(?=.*? E)(?<!;_BRIDGE_FAN_START\n)(G1 .*?F)(\d+(?:\.\d+)?)/
2012-02-25 20:56:36 +00:00
my $ new_speed = $ 2 * $ speed_factor ;
$ 1 . sprintf ( "%.${dec}f" , $ new_speed < $ min_print_speed ? $ min_print_speed : $ new_speed )
/ gexm ;
}
2013-03-10 14:36:52 +00:00
$ fan_speed = 0 if $ layer - > id < $ Slic3r:: Config - > disable_fan_first_layers ;
2012-02-25 20:01:00 +00:00
}
2012-06-28 12:44:54 +00:00
$ gcode = $ gcodegen - > set_fan ( $ fan_speed ) . $ gcode ;
2012-02-25 20:01:00 +00:00
# bridge fan speed
2013-03-10 14:36:52 +00:00
if ( ! $ Slic3r:: Config - > cooling || $ Slic3r:: Config - > bridge_fan_speed == 0 || $ layer - > id < $ Slic3r:: Config - > disable_fan_first_layers ) {
2012-05-20 18:07:39 +00:00
$ gcode =~ s/^;_BRIDGE_FAN_(?:START|END)\n//gm ;
2012-02-25 20:01:00 +00:00
} else {
2012-07-27 19:13:03 +00:00
$ gcode =~ s/^;_BRIDGE_FAN_START\n/ $gcodegen->set_fan($Slic3r::Config->bridge_fan_speed, 1) /gmex ;
2012-06-28 12:44:54 +00:00
$ gcode =~ s/^;_BRIDGE_FAN_END\n/ $gcodegen->set_fan($fan_speed, 1) /gmex ;
2012-05-20 18:07:39 +00:00
}
return $ gcode ;
} ;
# do all objects for each layer
2012-07-27 19:13:03 +00:00
if ( $ Slic3r:: Config - > complete_objects ) {
2012-05-20 18:07:39 +00:00
2012-05-21 18:19:30 +00:00
# print objects from the smallest to the tallest to avoid collisions
# when moving onto next object starting point
2012-05-21 18:23:17 +00:00
my @ obj_idx = sort { $ self - > objects - > [ $ a ] - > layer_count <=> $ self - > objects - > [ $ b ] - > layer_count } 0 .. $# { $ self - > objects } ;
2012-02-25 20:01:00 +00:00
2012-05-20 18:07:39 +00:00
my $ finished_objects = 0 ;
2012-05-21 18:19:30 +00:00
for my $ obj_idx ( @ obj_idx ) {
2012-09-22 14:19:24 +00:00
for my $ copy ( @ { $ self - > objects - > [ $ obj_idx ] - > copies } ) {
2012-05-20 18:07:39 +00:00
# move to the origin position for the copy we're going to print.
# this happens before Z goes down to layer 0 again, so that
# no collision happens hopefully.
if ( $ finished_objects > 0 ) {
2012-11-06 18:54:21 +00:00
$ gcodegen - > set_shift ( map $ shift [ $ _ ] + unscale $ copy - > [ $ _ ] , X , Y ) ;
2012-06-28 12:44:54 +00:00
print $ fh $ gcodegen - > retract ;
print $ fh $ gcodegen - > G0 ( Slic3r::Point - > new ( 0 , 0 ) , undef , 0 , 'move to origin position for next object' ) ;
2012-05-20 18:07:39 +00:00
}
2013-03-10 14:36:52 +00:00
for my $ layer ( @ { $ self - > objects - > [ $ obj_idx ] - > layers } ) {
2012-05-20 18:07:39 +00:00
# if we are printing the bottom layer of an object, and we have already finished
# another one, set first layer temperatures. this happens before the Z move
# is triggered, so machine has more time to reach such temperatures
2013-03-10 14:36:52 +00:00
if ( $ layer - > id == 0 && $ finished_objects > 0 ) {
2012-07-27 19:13:03 +00:00
printf $ fh $ gcodegen - > set_bed_temperature ( $ Slic3r:: Config - > first_layer_bed_temperature ) ,
if $ Slic3r:: Config - > first_layer_bed_temperature ;
2012-08-22 15:58:38 +00:00
$ print_first_layer_temperature - > ( ) ;
2012-05-20 18:07:39 +00:00
}
2013-03-10 14:36:52 +00:00
print $ fh $ extrude_layer - > ( $ layer , [ $ copy ] ) ;
2012-05-20 18:07:39 +00:00
}
$ finished_objects + + ;
}
}
} else {
2013-03-10 14:36:52 +00:00
print $ fh $ extrude_layer - > ( $ _ , $ _ - > object - > copies )
for sort { $ a - > print_z <=> $ b - > print_z }
map @ { $ _ - > layers } , @ { $ self - > objects } ;
2011-09-03 18:47:38 +00:00
}
2011-12-20 14:29:15 +00:00
# save statistic data
2012-06-28 12:44:54 +00:00
$ self - > total_extrusion_length ( $ gcodegen - > total_extrusion_length ) ;
2011-12-20 14:29:15 +00:00
2011-09-03 18:47:38 +00:00
# write end commands to file
2012-06-28 12:44:54 +00:00
print $ fh $ gcodegen - > retract ;
print $ fh $ gcodegen - > set_fan ( 0 ) ;
2012-07-27 19:13:03 +00:00
printf $ fh "%s\n" , $ Slic3r:: Config - > replace_options ( $ Slic3r:: Config - > end_gcode ) ;
2011-09-03 18:47:38 +00:00
2011-12-20 14:29:15 +00:00
printf $ fh "; filament used = %.1fmm (%.1fcm3)\n" ,
$ self - > total_extrusion_length , $ self - > total_extrusion_volume ;
2012-11-18 18:53:52 +00:00
if ( $ Slic3r:: Config - > gcode_comments ) {
# append full config
print $ fh "\n" ;
foreach my $ opt_key ( sort keys % { $ Slic3r:: Config } ) {
next if $ Slic3r:: Config:: Options - > { $ opt_key } { shortcut } ;
next if $ Slic3r:: Config:: Options - > { $ opt_key } { gui_only } ;
printf $ fh "; %s = %s\n" , $ opt_key , $ Slic3r:: Config - > serialize ( $ opt_key ) ;
}
}
2011-09-03 18:47:38 +00:00
# close our gcode file
close $ fh ;
}
2011-12-20 14:29:15 +00:00
sub total_extrusion_volume {
my $ self = shift ;
2012-09-23 00:40:25 +00:00
return $ self - > total_extrusion_length * ( $ self - > extruders - > [ 0 ] - > filament_diameter ** 2 ) * PI /4 / 1000 ;
2011-12-20 14:29:15 +00:00
}
2012-09-12 14:30:44 +00:00
# this method will return the supplied input file path after expanding its
2012-04-30 12:56:01 +00:00
# format variables with their values
sub expanded_output_filepath {
my $ self = shift ;
2012-09-12 14:30:44 +00:00
my ( $ path , $ input_file ) = @ _ ;
# if no input file was supplied, take the first one from our objects
$ input_file || = $ self - > objects - > [ 0 ] - > input_file ;
2012-11-23 10:24:04 +00:00
return undef if ! defined $ input_file ;
2012-04-30 12:56:01 +00:00
2012-08-01 15:12:16 +00:00
# if output path is an existing directory, we take that and append
# the specified filename format
$ path = File::Spec - > join ( $ path , $ Slic3r:: Config - > output_filename_format ) if ( $ path && - d $ path ) ;
2012-04-30 12:56:01 +00:00
# if no explicit output file was defined, we take the input
# file directory and append the specified filename format
2012-07-27 19:13:03 +00:00
$ path || = ( fileparse ( $ input_file ) ) [ 1 ] . $ Slic3r:: Config - > output_filename_format ;
2012-04-30 12:56:01 +00:00
my $ input_filename = my $ input_filename_base = basename ( $ input_file ) ;
$ input_filename_base =~ s/\.(?:stl|amf(?:\.xml)?)$//i ;
2012-07-27 19:13:03 +00:00
return $ Slic3r:: Config - > replace_options ( $ path , {
2012-04-30 12:56:01 +00:00
input_filename = > $ input_filename ,
input_filename_base = > $ input_filename_base ,
2012-09-12 13:29:44 +00:00
% { $ self - > extra_variables } ,
2012-04-30 12:56:01 +00:00
} ) ;
}
2011-09-01 19:06:28 +00:00
1 ;