Print.pm 34 KB

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