gcode.t 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. use Test::More tests => 22;
  2. use strict;
  3. use warnings;
  4. BEGIN {
  5. use FindBin;
  6. use lib "$FindBin::Bin/../lib";
  7. use local::lib "$FindBin::Bin/../local-lib";
  8. }
  9. use List::Util qw(first);
  10. use Slic3r;
  11. use Slic3r::Geometry qw(scale convex_hull);
  12. use Slic3r::Test;
  13. {
  14. my $config = Slic3r::Config::new_from_defaults;
  15. $config->set('wipe', [1]);
  16. $config->set('retract_layer_change', [0]);
  17. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  18. my $have_wipe = 0;
  19. my @retract_speeds = ();
  20. my $extruded_on_this_layer = 0;
  21. my $wiping_on_new_layer = 0;
  22. Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
  23. my ($self, $cmd, $args, $info) = @_;
  24. if ($info->{travel} && $info->{dist_Z}) {
  25. # changing layer
  26. $extruded_on_this_layer = 0;
  27. } elsif ($info->{extruding} && $info->{dist_XY}) {
  28. $extruded_on_this_layer = 1;
  29. } elsif ($info->{retracting} && $info->{dist_XY} > 0) {
  30. $have_wipe = 1;
  31. $wiping_on_new_layer = 1 if !$extruded_on_this_layer;
  32. my $move_time = $info->{dist_XY} / ($args->{F} // $self->F);
  33. push @retract_speeds, abs($info->{dist_E}) / $move_time;
  34. }
  35. });
  36. ok $have_wipe, "wipe";
  37. ok !defined (first { abs($_ - $config->retract_speed->[0]*60) < 5 } @retract_speeds), 'wipe moves don\'t retract faster than configured speed';
  38. ok !$wiping_on_new_layer, 'no wiping after layer change';
  39. }
  40. {
  41. my $config = Slic3r::Config::new_from_defaults;
  42. $config->set('z_offset', 5);
  43. $config->set('start_gcode', '');
  44. my $test = sub {
  45. my ($comment) = @_;
  46. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  47. my $moves_below_z_offset = 0;
  48. Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
  49. my ($self, $cmd, $args, $info) = @_;
  50. if ($info->{travel} && exists $args->{Z}) {
  51. $moves_below_z_offset++ if $args->{Z} < $config->z_offset;
  52. }
  53. });
  54. is $moves_below_z_offset, 0, "no Z moves below Z offset ($comment)";
  55. };
  56. $test->("no lift");
  57. $config->set('retract_lift', [3]);
  58. $test->("lift < z_offset");
  59. $config->set('retract_lift', [6]);
  60. $test->("lift > z_offset");
  61. }
  62. {
  63. # This tests the following behavior:
  64. # - complete objects does not crash
  65. # - no hard-coded "E" are generated
  66. # - Z moves are correctly generated for both objects
  67. # - no travel moves go outside skirt
  68. # - temperatures are set correctly
  69. my $config = Slic3r::Config::new_from_defaults;
  70. $config->set('gcode_comments', 1);
  71. $config->set('complete_objects', 1);
  72. $config->set('extrusion_axis', 'A');
  73. $config->set('start_gcode', ''); # prevent any default extra Z move
  74. $config->set('layer_height', 0.4);
  75. $config->set('first_layer_height', 0.4);
  76. $config->set('temperature', [200]);
  77. $config->set('first_layer_temperature', [210]);
  78. my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 2);
  79. ok my $gcode = Slic3r::Test::gcode($print), "complete_objects";
  80. my @z_moves = ();
  81. my @travel_moves = (); # array of scaled points
  82. my @extrusions = (); # array of scaled points
  83. my @temps = ();
  84. Slic3r::GCode::Reader->new->parse($gcode, sub {
  85. my ($self, $cmd, $args, $info) = @_;
  86. fail 'unexpected E argument' if defined $args->{E};
  87. if (defined $args->{Z}) {
  88. push @z_moves, $args->{Z};
  89. }
  90. if ($info->{dist_XY}) {
  91. if ($info->{extruding} || $args->{A}) {
  92. push @extrusions, Slic3r::Point->new_scale($info->{new_X}, $info->{new_Y});
  93. } else {
  94. push @travel_moves, Slic3r::Point->new_scale($info->{new_X}, $info->{new_Y})
  95. if @extrusions; # skip initial travel move to first skirt point
  96. }
  97. } elsif ($cmd eq 'M104' || $cmd eq 'M109') {
  98. push @temps, $args->{S} if !@temps || $args->{S} != $temps[-1];
  99. }
  100. });
  101. my $layer_count = 20/0.4; # cube is 20mm tall
  102. is scalar(@z_moves), 2*$layer_count, 'complete_objects generates the correct number of Z moves';
  103. is_deeply [ @z_moves[0..($layer_count-1)] ], [ @z_moves[$layer_count..$#z_moves] ], 'complete_objects generates the correct Z moves';
  104. my $convex_hull = convex_hull(\@extrusions);
  105. ok !(defined first { !$convex_hull->contains_point($_) } @travel_moves), 'all travel moves happen within skirt';
  106. is_deeply \@temps, [210, 200, 210, 200, 0], 'expected temperature changes';
  107. }
  108. {
  109. my $config = Slic3r::Config::new_from_defaults;
  110. $config->set('retract_length', [1000000]);
  111. $config->set('use_relative_e_distances', 1);
  112. $config->set('layer_gcode', "G92 E0\n");
  113. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  114. Slic3r::Test::gcode($print);
  115. ok $print->print->total_used_filament > 0, 'final retraction is not considered in total used filament';
  116. }
  117. {
  118. my $test = sub {
  119. my ($print, $comment) = @_;
  120. my @percent = ();
  121. my $got_100 = 0;
  122. my $extruding_after_100 = 0;
  123. Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
  124. my ($self, $cmd, $args, $info) = @_;
  125. if ($cmd eq 'M73') {
  126. push @percent, $args->{P};
  127. $got_100 = 1 if $args->{P} eq '100';
  128. }
  129. if ($info->{extruding} && $got_100) {
  130. $extruding_after_100 = 1;
  131. }
  132. });
  133. # the extruder heater is turned off when M73 P100 is reached
  134. ok !(defined first { $_ > 100 } @percent), "M73 is never given more than 100% ($comment)";
  135. ok !$extruding_after_100, "no extrusions after M73 P100 ($comment)";
  136. };
  137. {
  138. my $config = Slic3r::Config::new_from_defaults;
  139. $config->set('gcode_flavor', 'sailfish');
  140. $config->set('raft_layers', 3);
  141. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  142. $test->($print, 'single object');
  143. }
  144. {
  145. my $config = Slic3r::Config::new_from_defaults;
  146. $config->set('gcode_flavor', 'sailfish');
  147. my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 2);
  148. $test->($print, 'two copies of single object');
  149. }
  150. {
  151. my $config = Slic3r::Config::new_from_defaults;
  152. $config->set('gcode_flavor', 'sailfish');
  153. my $print = Slic3r::Test::init_print(['20mm_cube','20mm_cube'], config => $config);
  154. $test->($print, 'two objects');
  155. }
  156. {
  157. my $config = Slic3r::Config::new_from_defaults;
  158. $config->set('gcode_flavor', 'sailfish');
  159. my $print = Slic3r::Test::init_print('20mm_cube', config => $config, scale_xyz => [1,1, 1/(20/$config->layer_height) ]);
  160. $test->($print, 'one layer object');
  161. }
  162. }
  163. #{
  164. # [input_filename] placeholder was removed in 0cbbe96.
  165. # my $config = Slic3r::Config::new_from_defaults;
  166. # $config->set('start_gcode', 'START:[input_filename]');
  167. # my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  168. # my $gcode = Slic3r::Test::gcode($print);
  169. # like $gcode, qr/START:20mm_cube/, '[input_filename] is also available in custom G-code';
  170. #}
  171. # The current Spiral Vase slicing code removes the holes and all but the largest contours from each slice,
  172. # therefore the following test is no more valid.
  173. #{
  174. # my $config = Slic3r::Config::new_from_defaults;
  175. # $config->set('spiral_vase', 1);
  176. # my $print = Slic3r::Test::init_print('cube_with_hole', config => $config);
  177. #
  178. # my $spiral = 0;
  179. # Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
  180. # my ($self, $cmd, $args, $info) = @_;
  181. #
  182. # if ($cmd eq 'G1' && exists $args->{E} && exists $args->{Z}) {
  183. # $spiral = 1;
  184. # }
  185. # });
  186. #
  187. # ok !$spiral, 'spiral vase is correctly disabled on layers with multiple loops';
  188. #}
  189. {
  190. # Tests that the Repetier flavor produces M201 Xnnn Ynnn for resetting
  191. # acceleration, also that M204 Snnn syntax is not generated.
  192. my $config = Slic3r::Config::new_from_defaults;
  193. $config->set('gcode_flavor', 'repetier');
  194. $config->set('default_acceleration', 1337);
  195. my $print = Slic3r::Test::init_print('cube_with_hole', config => $config);
  196. my $has_accel = 0;
  197. my $has_m204 = 0;
  198. Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
  199. my ($self, $cmd, $args, $info) = @_;
  200. if ($cmd eq 'M201' && exists $args->{X} && exists $args->{Y}) {
  201. if ($args->{X} == 1337 && $args->{Y} == 1337) {
  202. $has_accel = 1;
  203. }
  204. }
  205. if ($cmd eq 'M204' && exists $args->{S}) {
  206. $has_m204 = 1;
  207. }
  208. });
  209. ok $has_accel, 'M201 is generated for repetier firmware.';
  210. ok !$has_m204, 'M204 is not generated for repetier firmware';
  211. }
  212. __END__