Print.pm 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. package Slic3r::Print;
  2. use Moo;
  3. use File::Basename qw(basename fileparse);
  4. use Math::ConvexHull 1.0.4 qw(convex_hull);
  5. use Slic3r::ExtrusionPath ':roles';
  6. use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 PI scale unscale move_points);
  7. use Slic3r::Geometry::Clipper qw(diff_ex union_ex intersection_ex offset JT_ROUND);
  8. use Time::HiRes qw(gettimeofday tv_interval);
  9. has 'objects' => (is => 'rw', default => sub {[]});
  10. has 'copies' => (is => 'rw', default => sub {[]}); # obj_idx => [copies...]
  11. has 'total_extrusion_length' => (is => 'rw');
  12. has 'processing_time' => (is => 'rw', required => 0);
  13. # ordered collection of extrusion paths to build skirt loops
  14. has 'skirt' => (
  15. is => 'rw',
  16. #isa => 'ArrayRef[Slic3r::ExtrusionLoop]',
  17. default => sub { [] },
  18. );
  19. # ordered collection of extrusion paths to build a brim
  20. has 'brim' => (
  21. is => 'rw',
  22. #isa => 'ArrayRef[Slic3r::ExtrusionLoop]',
  23. default => sub { [] },
  24. );
  25. sub add_object_from_file {
  26. my $self = shift;
  27. my ($input_file) = @_;
  28. my $object;
  29. if ($input_file =~ /\.stl$/i) {
  30. my $mesh = Slic3r::Format::STL->read_file($input_file);
  31. $mesh->check_manifoldness;
  32. $object = $self->add_object_from_mesh($mesh);
  33. } elsif ($input_file =~ /\.obj$/i) {
  34. my $mesh = Slic3r::Format::OBJ->read_file($input_file);
  35. $mesh->check_manifoldness;
  36. $object = $self->add_object_from_mesh($mesh);
  37. } elsif ( $input_file =~ /\.amf(\.xml)?$/i) {
  38. my ($materials, $meshes_by_material) = Slic3r::Format::AMF->read_file($input_file);
  39. $_->check_manifoldness for values %$meshes_by_material;
  40. $object = $self->add_object_from_mesh($meshes_by_material->{_} || +(values %$meshes_by_material)[0]);
  41. } else {
  42. die "Input file must have .stl, .obj or .amf(.xml) extension\n";
  43. }
  44. $object->input_file($input_file);
  45. return $object;
  46. }
  47. sub add_object_from_mesh {
  48. my $self = shift;
  49. my ($mesh) = @_;
  50. $mesh->rotate($Slic3r::rotate);
  51. $mesh->scale($Slic3r::scale / $Slic3r::scaling_factor);
  52. $mesh->align_to_origin;
  53. # initialize print object
  54. my @size = $mesh->size;
  55. my $object = Slic3r::Print::Object->new(
  56. mesh => $mesh,
  57. x_length => $size[X],
  58. y_length => $size[Y],
  59. );
  60. push @{$self->objects}, $object;
  61. push @{$self->copies}, [[0, 0]];
  62. return $object;
  63. }
  64. sub validate {
  65. my $self = shift;
  66. if ($Slic3r::complete_objects) {
  67. # check horizontal clearance
  68. {
  69. my @a = ();
  70. for my $obj_idx (0 .. $#{$self->objects}) {
  71. my $clearance;
  72. {
  73. my @points = map [ @$_[X,Y] ], @{$self->objects->[$obj_idx]->mesh->vertices};
  74. my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points));
  75. $clearance = +($convex_hull->offset(scale $Slic3r::extruder_clearance_radius / 2, 1, JT_ROUND))[0];
  76. }
  77. for my $copy (@{$self->copies->[$obj_idx]}) {
  78. my $copy_clearance = $clearance->clone;
  79. $copy_clearance->translate(@$copy);
  80. if (@{ intersection_ex(\@a, [$copy_clearance]) }) {
  81. die "Some objects are too close; your extruder will collide with them.\n";
  82. }
  83. @a = map @$_, @{union_ex([ @a, $copy_clearance ])};
  84. }
  85. }
  86. }
  87. # check vertical clearance
  88. {
  89. my @obj_copies = $self->object_copies;
  90. pop @obj_copies; # ignore the last copy: its height doesn't matter
  91. if (grep { +($self->objects->[$_->[0]]->mesh->size)[Z] > scale $Slic3r::extruder_clearance_height } @obj_copies) {
  92. die "Some objects are too tall and cannot be printed without extruder collisions.\n";
  93. }
  94. }
  95. }
  96. }
  97. sub object_copies {
  98. my $self = shift;
  99. my @oc = ();
  100. for my $obj_idx (0 .. $#{$self->objects}) {
  101. push @oc, map [ $obj_idx, $_ ], @{$self->copies->[$obj_idx]};
  102. }
  103. return @oc;
  104. }
  105. sub cleanup {
  106. my $self = shift;
  107. $_->cleanup for @{$self->objects};
  108. @{$self->skirt} = ();
  109. $self->total_extrusion_length(0);
  110. $self->processing_time(0);
  111. }
  112. sub layer_count {
  113. my $self = shift;
  114. my $count = 0;
  115. foreach my $object (@{$self->objects}) {
  116. $count = @{$object->layers} if @{$object->layers} > $count;
  117. }
  118. return $count;
  119. }
  120. sub duplicate {
  121. my $self = shift;
  122. if ($Slic3r::duplicate_grid->[X] > 1 || $Slic3r::duplicate_grid->[Y] > 1) {
  123. if (@{$self->objects} > 1) {
  124. die "Grid duplication is not supported with multiple objects\n";
  125. }
  126. my $object = $self->objects->[0];
  127. # generate offsets for copies
  128. my $dist = scale $Slic3r::duplicate_distance;
  129. @{$self->copies->[0]} = ();
  130. for my $x_copy (1..$Slic3r::duplicate_grid->[X]) {
  131. for my $y_copy (1..$Slic3r::duplicate_grid->[Y]) {
  132. push @{$self->copies->[0]}, [
  133. ($object->x_length + $dist) * ($x_copy-1),
  134. ($object->y_length + $dist) * ($y_copy-1),
  135. ];
  136. }
  137. }
  138. } elsif ($Slic3r::duplicate > 1) {
  139. foreach my $copies (@{$self->copies}) {
  140. @$copies = map [0,0], 1..$Slic3r::duplicate;
  141. }
  142. $self->arrange_objects;
  143. }
  144. }
  145. sub arrange_objects {
  146. my $self = shift;
  147. my $total_parts = scalar map @$_, @{$self->copies};
  148. my $partx = my $party = 0;
  149. foreach my $object (@{$self->objects}) {
  150. $partx = $object->x_length if $object->x_length > $partx;
  151. $party = $object->y_length if $object->y_length > $party;
  152. }
  153. # object distance is max(duplicate_distance, clearance_radius)
  154. my $distance = $Slic3r::complete_objects && $Slic3r::extruder_clearance_radius > $Slic3r::duplicate_distance
  155. ? $Slic3r::extruder_clearance_radius
  156. : $Slic3r::duplicate_distance;
  157. my @positions = Slic3r::Geometry::arrange
  158. ($total_parts, $partx, $party, (map scale $_, @$Slic3r::bed_size), scale $distance);
  159. for my $obj_idx (0..$#{$self->objects}) {
  160. @{$self->copies->[$obj_idx]} = splice @positions, 0, scalar @{$self->copies->[$obj_idx]};
  161. }
  162. }
  163. sub bounding_box {
  164. my $self = shift;
  165. my @points = ();
  166. foreach my $obj_idx (0 .. $#{$self->objects}) {
  167. my $object = $self->objects->[$obj_idx];
  168. foreach my $copy (@{$self->copies->[$obj_idx]}) {
  169. push @points,
  170. [ $copy->[X], $copy->[Y] ],
  171. [ $copy->[X] + $object->x_length, $copy->[Y] ],
  172. [ $copy->[X] + $object->x_length, $copy->[Y] + $object->y_length ],
  173. [ $copy->[X], $copy->[Y] + $object->y_length ];
  174. }
  175. }
  176. return Slic3r::Geometry::bounding_box(\@points);
  177. }
  178. sub size {
  179. my $self = shift;
  180. my @bb = $self->bounding_box;
  181. return [ $bb[X2] - $bb[X1], $bb[Y2] - $bb[Y1] ];
  182. }
  183. sub export_gcode {
  184. my $self = shift;
  185. my %params = @_;
  186. my $status_cb = $params{status_cb} || sub {};
  187. my $t0 = [gettimeofday];
  188. # skein the STL into layers
  189. # each layer has surfaces with holes
  190. $status_cb->(5, "Processing input file");
  191. $status_cb->(10, "Processing triangulated mesh");
  192. $_->slice(keep_meshes => $params{keep_meshes}) for @{$self->objects};
  193. # make perimeters
  194. # this will add a set of extrusion loops to each layer
  195. # as well as generate infill boundaries
  196. $status_cb->(20, "Generating perimeters");
  197. $_->make_perimeters for @{$self->objects};
  198. # simplify slices, we only need the max resolution for perimeters
  199. $_->simplify(scale $Slic3r::resolution)
  200. for map @{$_->expolygon}, map @{$_->slices}, map @{$_->layers}, @{$self->objects};
  201. # this will clip $layer->surfaces to the infill boundaries
  202. # and split them in top/bottom/internal surfaces;
  203. $status_cb->(30, "Detecting solid surfaces");
  204. $_->detect_surfaces_type for @{$self->objects};
  205. # decide what surfaces are to be filled
  206. $status_cb->(35, "Preparing infill surfaces");
  207. $_->prepare_fill_surfaces for map @{$_->layers}, @{$self->objects};
  208. # this will remove unprintable surfaces
  209. # (those that are too tight for extrusion)
  210. $status_cb->(40, "Cleaning up");
  211. $_->remove_small_surfaces for map @{$_->layers}, @{$self->objects};
  212. # this will detect bridges and reverse bridges
  213. # and rearrange top/bottom/internal surfaces
  214. $status_cb->(45, "Detect bridges");
  215. $_->process_bridges for map @{$_->layers}, @{$self->objects};
  216. # detect which fill surfaces are near external layers
  217. # they will be split in internal and internal-solid surfaces
  218. $status_cb->(60, "Generating horizontal shells");
  219. $_->discover_horizontal_shells for @{$self->objects};
  220. # free memory
  221. @{$_->surfaces} = () for map @{$_->layers}, @{$self->objects};
  222. # combine fill surfaces to honor the "infill every N layers" option
  223. $status_cb->(70, "Combining infill");
  224. $_->infill_every_layers for @{$self->objects};
  225. # this will generate extrusion paths for each layer
  226. $status_cb->(80, "Infilling layers");
  227. {
  228. my $fill_maker = Slic3r::Fill->new('print' => $self);
  229. my @items = (); # [obj_idx, layer_id]
  230. foreach my $obj_idx (0 .. $#{$self->objects}) {
  231. push @items, map [$obj_idx, $_], 0..$#{$self->objects->[$obj_idx]->layers};
  232. }
  233. Slic3r::parallelize(
  234. items => [@items],
  235. thread_cb => sub {
  236. my $q = shift;
  237. $Slic3r::Geometry::Clipper::clipper = Math::Clipper->new;
  238. my $fills = {};
  239. while (defined (my $obj_layer = $q->dequeue)) {
  240. my ($obj_idx, $layer_id) = @$obj_layer;
  241. $fills->{$obj_idx} ||= {};
  242. $fills->{$obj_idx}{$layer_id} = [ $fill_maker->make_fill($self->objects->[$obj_idx]->layers->[$layer_id]) ];
  243. }
  244. return $fills;
  245. },
  246. collect_cb => sub {
  247. my $fills = shift;
  248. foreach my $obj_idx (keys %$fills) {
  249. foreach my $layer_id (keys %{$fills->{$obj_idx}}) {
  250. @{$self->objects->[$obj_idx]->layers->[$layer_id]->fills} = @{$fills->{$obj_idx}{$layer_id}};
  251. }
  252. }
  253. },
  254. no_threads_cb => sub {
  255. foreach my $layer (map @{$_->layers}, @{$self->objects}) {
  256. @{$layer->fills} = $fill_maker->make_fill($layer);
  257. }
  258. },
  259. );
  260. }
  261. # generate support material
  262. if ($Slic3r::support_material) {
  263. $status_cb->(85, "Generating support material");
  264. $_->generate_support_material(print => $self) for @{$self->objects};
  265. }
  266. # free memory (note that support material needs fill_surfaces)
  267. @{$_->fill_surfaces} = () for map @{$_->layers}, @{$self->objects};
  268. # make skirt
  269. $status_cb->(88, "Generating skirt");
  270. $self->make_skirt;
  271. $self->make_brim;
  272. # output everything to a G-code file
  273. my $output_file = $self->expanded_output_filepath($params{output_file});
  274. $status_cb->(90, "Exporting G-code to $output_file");
  275. $self->write_gcode($output_file);
  276. # run post-processing scripts
  277. if (@$Slic3r::post_process) {
  278. $status_cb->(95, "Running post-processing scripts");
  279. Slic3r::Config->setenv;
  280. for (@$Slic3r::post_process) {
  281. Slic3r::debugf " '%s' '%s'\n", $_, $output_file;
  282. system($_, $output_file);
  283. }
  284. }
  285. # output some statistics
  286. $self->processing_time(tv_interval($t0));
  287. printf "Done. Process took %d minutes and %.3f seconds\n",
  288. int($self->processing_time/60),
  289. $self->processing_time - int($self->processing_time/60)*60;
  290. # TODO: more statistics!
  291. printf "Filament required: %.1fmm (%.1fcm3)\n",
  292. $self->total_extrusion_length, $self->total_extrusion_volume;
  293. }
  294. sub export_svg {
  295. my $self = shift;
  296. my %params = @_;
  297. $_->slice(keep_meshes => $params{keep_meshes}) for @{$self->objects};
  298. $self->arrange_objects;
  299. my $output_file = $self->expanded_output_filepath($params{output_file});
  300. $output_file =~ s/\.gcode$/.svg/i;
  301. open my $fh, ">", $output_file or die "Failed to open $output_file for writing\n";
  302. print "Exporting to $output_file...";
  303. my $print_size = $self->size;
  304. print $fh sprintf <<"EOF", unscale($print_size->[X]), unscale($print_size->[Y]);
  305. <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  306. <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
  307. <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">
  308. <!--
  309. Generated using Slic3r $Slic3r::VERSION
  310. http://slic3r.org/
  311. -->
  312. EOF
  313. my $print_polygon = sub {
  314. my ($polygon, $type) = @_;
  315. printf $fh qq{ <polygon slic3r:type="%s" points="%s" style="fill: %s" />\n},
  316. $type, (join ' ', map { join ',', map unscale $_, @$_ } @$polygon),
  317. ($type eq 'contour' ? 'white' : 'black');
  318. };
  319. my @previous_layer_slices = ();
  320. for my $layer_id (0..$self->layer_count-1) {
  321. my @layers = map $_->layers->[$layer_id], @{$self->objects};
  322. printf $fh qq{ <g id="layer%d" slic3r:z="%s">\n}, $layer_id, unscale +(grep defined $_, @layers)[0]->slice_z;
  323. my @current_layer_slices = ();
  324. for my $obj_idx (0 .. $#{$self->objects}) {
  325. my $layer = $self->objects->[$obj_idx]->layers->[$layer_id] or next;
  326. # sort slices so that the outermost ones come first
  327. my @slices = sort { $a->expolygon->contour->encloses_point($b->expolygon->contour->[0]) ? 0 : 1 } @{$layer->slices};
  328. foreach my $copy (@{$self->copies->[$obj_idx]}) {
  329. foreach my $slice (@slices) {
  330. my $expolygon = $slice->expolygon->clone;
  331. $expolygon->translate(@$copy);
  332. $print_polygon->($expolygon->contour, 'contour');
  333. $print_polygon->($_, 'hole') for $expolygon->holes;
  334. push @current_layer_slices, $expolygon;
  335. }
  336. }
  337. }
  338. # generate support material
  339. if ($Slic3r::support_material && $layer_id > 0) {
  340. my (@supported_slices, @unsupported_slices) = ();
  341. foreach my $expolygon (@current_layer_slices) {
  342. my $intersection = intersection_ex(
  343. [ map @$_, @previous_layer_slices ],
  344. $expolygon,
  345. );
  346. @$intersection
  347. ? push @supported_slices, $expolygon
  348. : push @unsupported_slices, $expolygon;
  349. }
  350. my @supported_points = map @$_, @$_, @supported_slices;
  351. foreach my $expolygon (@unsupported_slices) {
  352. # look for the nearest point to this island among all
  353. # supported points
  354. my $support_point = nearest_point($expolygon->contour->[0], \@supported_points);
  355. my $anchor_point = nearest_point($support_point, $expolygon->contour->[0]);
  356. printf $fh qq{ <line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke-width: 2; stroke: white" />\n},
  357. map @$_, $support_point, $anchor_point;
  358. }
  359. }
  360. print $fh qq{ </g>\n};
  361. @previous_layer_slices = @current_layer_slices;
  362. }
  363. print $fh "</svg>\n";
  364. close $fh;
  365. print "Done.\n";
  366. }
  367. sub make_skirt {
  368. my $self = shift;
  369. return unless $Slic3r::skirts > 0;
  370. # collect points from all layers contained in skirt height
  371. my $skirt_height = $Slic3r::skirt_height;
  372. $skirt_height = $self->layer_count if $skirt_height > $self->layer_count;
  373. my @points = ();
  374. foreach my $obj_idx (0 .. $#{$self->objects}) {
  375. my @layers = map $self->objects->[$obj_idx]->layer($_), 0..($skirt_height-1);
  376. my @layer_points = (
  377. (map @$_, map @{$_->expolygon}, map @{$_->slices}, @layers),
  378. (map @$_, map @{$_->thin_walls}, @layers),
  379. (map @{$_->unpack->polyline}, map @{$_->support_fills->paths}, grep $_->support_fills, @layers),
  380. );
  381. push @points, map move_points($_, @layer_points), @{$self->copies->[$obj_idx]};
  382. }
  383. return if @points < 3; # at least three points required for a convex hull
  384. # find out convex hull
  385. my $convex_hull = convex_hull(\@points);
  386. # draw outlines from outside to inside
  387. my $flow = $Slic3r::first_layer_flow || $Slic3r::flow;
  388. my @skirt = ();
  389. for (my $i = $Slic3r::skirts; $i > 0; $i--) {
  390. my $distance = scale ($Slic3r::skirt_distance + ($flow->spacing * $i));
  391. my $outline = offset([$convex_hull], $distance, $Slic3r::scaling_factor * 100, JT_ROUND);
  392. push @skirt, Slic3r::ExtrusionLoop->pack(
  393. polygon => Slic3r::Polygon->new(@{$outline->[0]}),
  394. role => EXTR_ROLE_SKIRT,
  395. );
  396. }
  397. unshift @{$self->skirt}, @skirt;
  398. }
  399. sub make_brim {
  400. my $self = shift;
  401. return unless $Slic3r::brim_width > 0;
  402. my @islands = (); # array of polygons
  403. foreach my $obj_idx (0 .. $#{$self->objects}) {
  404. my @object_islands = map $_->contour, @{ $self->objects->[$obj_idx]->layers->[0]->slices };
  405. foreach my $copy (@{$self->copies->[$obj_idx]}) {
  406. push @islands, map $_->clone->translate(@$copy), @object_islands;
  407. }
  408. }
  409. my $flow = $Slic3r::first_layer_flow || $Slic3r::flow;
  410. my $num_loops = sprintf "%.0f", $Slic3r::brim_width / $flow->width;
  411. for my $i (reverse 1 .. $num_loops) {
  412. push @{$self->brim}, Slic3r::ExtrusionLoop->pack(
  413. polygon => Slic3r::Polygon->new($_),
  414. role => EXTR_ROLE_SKIRT,
  415. ) for @{Math::Clipper::offset(\@islands, $i * scale $flow->spacing)};
  416. }
  417. }
  418. sub write_gcode {
  419. my $self = shift;
  420. my ($file) = @_;
  421. # open output gcode file
  422. open my $fh, ">", $file
  423. or die "Failed to open $file for writing\n";
  424. # write some information
  425. my @lt = localtime;
  426. printf $fh "; generated by Slic3r $Slic3r::VERSION on %04d-%02d-%02d at %02d:%02d:%02d\n\n",
  427. $lt[5] + 1900, $lt[4]+1, $lt[3], $lt[2], $lt[1], $lt[0];
  428. print $fh "; $_\n" foreach split /\R/, $Slic3r::notes;
  429. print $fh "\n" if $Slic3r::notes;
  430. for (qw(layer_height perimeters solid_layers fill_density perimeter_speed infill_speed travel_speed scale)) {
  431. printf $fh "; %s = %s\n", $_, Slic3r::Config->get($_);
  432. }
  433. for (qw(nozzle_diameter filament_diameter extrusion_multiplier)) {
  434. printf $fh "; %s = %s\n", $_, Slic3r::Config->get($_)->[0];
  435. }
  436. printf $fh "; single wall width = %.2fmm\n", $Slic3r::flow->width;
  437. printf $fh "; first layer single wall width = %.2fmm\n", $Slic3r::first_layer_flow->width
  438. if $Slic3r::first_layer_flow;
  439. print $fh "\n";
  440. # set up our extruder object
  441. my $gcodegen = Slic3r::GCode->new;
  442. my $min_print_speed = 60 * $Slic3r::min_print_speed;
  443. my $dec = $gcodegen->dec;
  444. print $fh $gcodegen->set_tool(0) if @$Slic3r::extruders > 1;
  445. print $fh $gcodegen->set_fan(0, 1) if $Slic3r::cooling && $Slic3r::disable_fan_first_layers;
  446. # write start commands to file
  447. printf $fh $gcodegen->set_bed_temperature($Slic3r::first_layer_bed_temperature, 1),
  448. if $Slic3r::first_layer_bed_temperature && $Slic3r::start_gcode !~ /M190/i;
  449. for my $t (grep $Slic3r::extruders->[$_], 0 .. $#$Slic3r::first_layer_temperature) {
  450. printf $fh $gcodegen->set_temperature($Slic3r::extruders->[$t]->first_layer_temperature, 0, $t)
  451. if $Slic3r::extruders->[$t]->first_layer_temperature;
  452. }
  453. printf $fh "%s\n", Slic3r::Config->replace_options($Slic3r::start_gcode);
  454. for my $t (grep $Slic3r::extruders->[$_], 0 .. $#$Slic3r::first_layer_temperature) {
  455. printf $fh $gcodegen->set_temperature($Slic3r::extruders->[$t]->first_layer_temperature, 1, $t)
  456. if $Slic3r::extruders->[$t]->first_layer_temperature && $Slic3r::start_gcode !~ /M109/i;
  457. }
  458. print $fh "G90 ; use absolute coordinates\n";
  459. print $fh "G21 ; set units to millimeters\n";
  460. if ($Slic3r::gcode_flavor =~ /^(?:reprap|teacup)$/) {
  461. printf $fh $gcodegen->reset_e;
  462. if ($Slic3r::gcode_flavor =~ /^(?:reprap|makerbot)$/) {
  463. if ($Slic3r::use_relative_e_distances) {
  464. print $fh "M83 ; use relative distances for extrusion\n";
  465. } else {
  466. print $fh "M82 ; use absolute distances for extrusion\n";
  467. }
  468. }
  469. }
  470. # calculate X,Y shift to center print around specified origin
  471. my @print_bb = $self->bounding_box;
  472. my @shift = (
  473. $Slic3r::print_center->[X] - (unscale ($print_bb[X2] - $print_bb[X1]) / 2) - unscale $print_bb[X1],
  474. $Slic3r::print_center->[Y] - (unscale ($print_bb[Y2] - $print_bb[Y1]) / 2) - unscale $print_bb[Y1],
  475. );
  476. # prepare the logic to print one layer
  477. my $skirt_done = 0; # count of skirt layers done
  478. my $brim_done = 0;
  479. my $extrude_layer = sub {
  480. my ($layer_id, $object_copies) = @_;
  481. my $gcode = "";
  482. if ($layer_id == 1) {
  483. for my $t (grep $Slic3r::extruders->[$_], 0 .. $#$Slic3r::temperature) {
  484. $gcode .= $gcodegen->set_temperature($Slic3r::extruders->[$t]->temperature, 0, $t)
  485. if $Slic3r::extruders->[$t]->temperature && $Slic3r::extruders->[$t]->temperature != $Slic3r::extruders->[$t]->first_layer_temperature;
  486. }
  487. $gcode .= $gcodegen->set_bed_temperature($Slic3r::bed_temperature)
  488. if $Slic3r::first_layer_bed_temperature && $Slic3r::bed_temperature != $Slic3r::first_layer_bed_temperature;
  489. }
  490. # go to layer (just use the first one, we only need Z from it)
  491. $gcode .= $gcodegen->change_layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id]);
  492. $gcodegen->elapsed_time(0);
  493. # extrude skirt
  494. if ($skirt_done < $Slic3r::skirt_height) {
  495. $gcodegen->shift_x($shift[X]);
  496. $gcodegen->shift_y($shift[Y]);
  497. $gcode .= $gcodegen->set_acceleration($Slic3r::perimeter_acceleration);
  498. # skip skirt if we have a large brim
  499. if ($layer_id < $Slic3r::skirt_height && ($layer_id != 0 || $Slic3r::skirt_distance + ($Slic3r::skirts * $Slic3r::flow->width) > $Slic3r::brim_width)) {
  500. $gcode .= $gcodegen->extrude_loop($_, 'skirt') for @{$self->skirt};
  501. }
  502. $skirt_done++;
  503. }
  504. # extrude brim
  505. if ($layer_id == 0 && !$brim_done) {
  506. $gcode .= $gcodegen->extrude_loop($_, 'brim') for @{$self->brim};
  507. $brim_done = 1;
  508. }
  509. for my $obj_copy (@$object_copies) {
  510. my ($obj_idx, $copy) = @$obj_copy;
  511. my $layer = $self->objects->[$obj_idx]->layers->[$layer_id];
  512. # retract explicitely because changing the shift_[xy] properties below
  513. # won't always trigger the automatic retraction
  514. $gcode .= $gcodegen->retract;
  515. $gcodegen->shift_x($shift[X] + unscale $copy->[X]);
  516. $gcodegen->shift_y($shift[Y] + unscale $copy->[Y]);
  517. # extrude perimeters
  518. $gcode .= $gcodegen->set_tool($Slic3r::perimeter_extruder-1);
  519. $gcode .= $gcodegen->extrude($_, 'perimeter') for @{ $layer->perimeters };
  520. # extrude fills
  521. $gcode .= $gcodegen->set_tool($Slic3r::infill_extruder-1);
  522. $gcode .= $gcodegen->set_acceleration($Slic3r::infill_acceleration);
  523. for my $fill (@{ $layer->fills }) {
  524. $gcode .= $gcodegen->extrude($_, 'fill')
  525. for $fill->shortest_path($gcodegen->last_pos);
  526. }
  527. # extrude support material
  528. if ($layer->support_fills) {
  529. $gcode .= $gcodegen->set_tool($Slic3r::support_material_extruder-1);
  530. $gcode .= $gcodegen->extrude_path($_, 'support material')
  531. for $layer->support_fills->shortest_path($gcodegen->last_pos);
  532. }
  533. }
  534. return if !$gcode;
  535. my $fan_speed = $Slic3r::fan_always_on ? $Slic3r::min_fan_speed : 0;
  536. my $speed_factor = 1;
  537. if ($Slic3r::cooling) {
  538. my $layer_time = $gcodegen->elapsed_time;
  539. Slic3r::debugf "Layer %d estimated printing time: %d seconds\n", $layer_id, $layer_time;
  540. if ($layer_time < $Slic3r::slowdown_below_layer_time) {
  541. $fan_speed = $Slic3r::max_fan_speed;
  542. $speed_factor = $layer_time / $Slic3r::slowdown_below_layer_time;
  543. } elsif ($layer_time < $Slic3r::fan_below_layer_time) {
  544. $fan_speed = $Slic3r::max_fan_speed - ($Slic3r::max_fan_speed - $Slic3r::min_fan_speed)
  545. * ($layer_time - $Slic3r::slowdown_below_layer_time)
  546. / ($Slic3r::fan_below_layer_time - $Slic3r::slowdown_below_layer_time); #/
  547. }
  548. Slic3r::debugf " fan = %d%%, speed = %d%%\n", $fan_speed, $speed_factor * 100;
  549. if ($speed_factor < 1) {
  550. $gcode =~ s/^(?=.*? [XY])(?=.*? E)(G1 .*?F)(\d+(?:\.\d+)?)/
  551. my $new_speed = $2 * $speed_factor;
  552. $1 . sprintf("%.${dec}f", $new_speed < $min_print_speed ? $min_print_speed : $new_speed)
  553. /gexm;
  554. }
  555. $fan_speed = 0 if $layer_id < $Slic3r::disable_fan_first_layers;
  556. }
  557. $gcode = $gcodegen->set_fan($fan_speed) . $gcode;
  558. # bridge fan speed
  559. if (!$Slic3r::cooling || $Slic3r::bridge_fan_speed == 0 || $layer_id < $Slic3r::disable_fan_first_layers) {
  560. $gcode =~ s/^;_BRIDGE_FAN_(?:START|END)\n//gm;
  561. } else {
  562. $gcode =~ s/^;_BRIDGE_FAN_START\n/ $gcodegen->set_fan($Slic3r::bridge_fan_speed, 1) /gmex;
  563. $gcode =~ s/^;_BRIDGE_FAN_END\n/ $gcodegen->set_fan($fan_speed, 1) /gmex;
  564. }
  565. return $gcode;
  566. };
  567. # do all objects for each layer
  568. if ($Slic3r::complete_objects) {
  569. # print objects from the smallest to the tallest to avoid collisions
  570. # when moving onto next object starting point
  571. my @obj_idx = sort { $self->objects->[$a]->layer_count <=> $self->objects->[$b]->layer_count } 0..$#{$self->objects};
  572. my $finished_objects = 0;
  573. for my $obj_idx (@obj_idx) {
  574. for my $copy (@{ $self->copies->[$obj_idx] }) {
  575. # move to the origin position for the copy we're going to print.
  576. # this happens before Z goes down to layer 0 again, so that
  577. # no collision happens hopefully.
  578. if ($finished_objects > 0) {
  579. $gcodegen->shift_x($shift[X] + unscale $copy->[X]);
  580. $gcodegen->shift_y($shift[Y] + unscale $copy->[Y]);
  581. print $fh $gcodegen->retract;
  582. print $fh $gcodegen->G0(Slic3r::Point->new(0,0), undef, 0, 'move to origin position for next object');
  583. }
  584. for my $layer_id (0..$#{$self->objects->[$obj_idx]->layers}) {
  585. # if we are printing the bottom layer of an object, and we have already finished
  586. # another one, set first layer temperatures. this happens before the Z move
  587. # is triggered, so machine has more time to reach such temperatures
  588. if ($layer_id == 0 && $finished_objects > 0) {
  589. printf $fh $gcodegen->set_bed_temperature($Slic3r::first_layer_bed_temperature),
  590. if $Slic3r::first_layer_bed_temperature;
  591. printf $fh $gcodegen->set_temperature($Slic3r::first_layer_temperature)
  592. if $Slic3r::first_layer_temperature;
  593. }
  594. print $fh $extrude_layer->($layer_id, [[ $obj_idx, $copy ]]);
  595. }
  596. $finished_objects++;
  597. }
  598. }
  599. } else {
  600. for my $layer_id (0..$self->layer_count-1) {
  601. my @object_copies = ();
  602. for my $obj_idx (grep $self->objects->[$_]->layers->[$layer_id], 0..$#{$self->objects}) {
  603. push @object_copies, map [ $obj_idx, $_ ], @{ $self->copies->[$obj_idx] };
  604. }
  605. print $fh $extrude_layer->($layer_id, \@object_copies);
  606. }
  607. }
  608. # save statistic data
  609. $self->total_extrusion_length($gcodegen->total_extrusion_length);
  610. # write end commands to file
  611. print $fh $gcodegen->retract;
  612. print $fh $gcodegen->set_fan(0);
  613. print $fh "M501 ; reset acceleration\n" if $Slic3r::acceleration;
  614. printf $fh "%s\n", Slic3r::Config->replace_options($Slic3r::end_gcode);
  615. printf $fh "; filament used = %.1fmm (%.1fcm3)\n",
  616. $self->total_extrusion_length, $self->total_extrusion_volume;
  617. # close our gcode file
  618. close $fh;
  619. }
  620. sub total_extrusion_volume {
  621. my $self = shift;
  622. return $self->total_extrusion_length * ($Slic3r::extruders->[0]->filament_diameter**2) * PI/4 / 1000;
  623. }
  624. # this method will return the value of $self->output_file after expanding its
  625. # format variables with their values
  626. sub expanded_output_filepath {
  627. my $self = shift;
  628. my ($path) = @_;
  629. # if no explicit output file was defined, we take the input
  630. # file directory and append the specified filename format
  631. my $input_file = $self->objects->[0]->input_file;
  632. $path ||= (fileparse($input_file))[1] . $Slic3r::output_filename_format;
  633. my $input_filename = my $input_filename_base = basename($input_file);
  634. $input_filename_base =~ s/\.(?:stl|amf(?:\.xml)?)$//i;
  635. return Slic3r::Config->replace_options($path, {
  636. input_filename => $input_filename,
  637. input_filename_base => $input_filename_base,
  638. });
  639. }
  640. 1;