multi.t 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. use Test::More tests => 13;
  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::Geometry::Clipper qw(offset);
  13. use Slic3r::Test;
  14. {
  15. my $config = Slic3r::Config->new_from_defaults;
  16. $config->set('layer_height', 0.3);
  17. $config->set('first_layer_height', 0.35);
  18. $config->set('raft_layers', 2);
  19. $config->set('infill_extruder', 2);
  20. $config->set('solid_infill_extruder', 3);
  21. $config->set('support_material_extruder', 4);
  22. $config->set('ooze_prevention', 1);
  23. $config->set('extruder_offset', [ [0,0], [20,0], [0,20], [20,20] ]);
  24. $config->set('temperature', [200, 180, 170, 160]);
  25. $config->set('first_layer_temperature', [206, 186, 166, 156]);
  26. $config->set('standby_temperature_delta', -5);
  27. $config->set('toolchange_gcode', ';toolchange'); # test that it doesn't crash when this is supplied
  28. $config->set('skirts', 2); # test correct temperatures are applied to skirt as well
  29. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  30. my $tool = undef;
  31. my @tool_temp = (0,0,0,0);
  32. my @toolchange_points = ();
  33. my @extrusion_points = ();
  34. Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
  35. my ($self, $cmd, $args, $info) = @_;
  36. if ($cmd =~ /^T(\d+)/) {
  37. # ignore initial toolchange
  38. if (defined $tool) {
  39. my $expected_temp = $self->Z == ($config->get_value('first_layer_height') + $config->z_offset)
  40. ? $config->first_layer_temperature->[$tool]
  41. : $config->temperature->[$tool];
  42. die 'standby temperature was not set before toolchange'
  43. if $tool_temp[$tool] != $expected_temp + $config->standby_temperature_delta;
  44. push @toolchange_points, my $point = Slic3r::Point->new_scale($self->X, $self->Y);
  45. }
  46. $tool = $1;
  47. } elsif ($cmd eq 'M104' || $cmd eq 'M109') {
  48. my $t = $args->{T} // $tool;
  49. if ($tool_temp[$t] == 0) {
  50. fail 'initial temperature is not equal to first layer temperature + standby delta'
  51. unless $args->{S} == $config->first_layer_temperature->[$t] + $config->standby_temperature_delta;
  52. }
  53. $tool_temp[$t] = $args->{S};
  54. } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
  55. push @extrusion_points, my $point = Slic3r::Point->new_scale($args->{X}, $args->{Y});
  56. $point->translate(map +scale($_), @{ $config->extruder_offset->[$tool] });
  57. # check temperature (we don't do it at M104/M109 because that may be
  58. # issued before layer change)
  59. if ($self->Z == $config->first_layer_height) {
  60. fail 'unexpected temperature in first layer'
  61. unless $tool_temp[$tool] == ($config->first_layer_temperature->[$tool] + $config->standby_temperature_delta)
  62. || $tool_temp[$tool] == $config->first_layer_temperature->[$tool];
  63. } else {
  64. fail 'unexpected temperature'
  65. unless $tool_temp[$tool] == ($config->temperature->[$tool] + $config->standby_temperature_delta)
  66. || $tool_temp[$tool] == $config->temperature->[$tool];
  67. }
  68. }
  69. });
  70. my $convex_hull = convex_hull(\@extrusion_points);
  71. my @t = ();
  72. foreach my $point (@toolchange_points) {
  73. foreach my $offset (@{$config->extruder_offset}) {
  74. push @t, my $p = $point->clone;
  75. $p->translate(map +scale($_), @$offset);
  76. }
  77. }
  78. ok !(defined first { $convex_hull->contains_point($_) } @t), 'all nozzles are outside skirt at toolchange';
  79. if (0) {
  80. require "Slic3r/SVG.pm";
  81. Slic3r::SVG::output(
  82. "ooze_prevention_test.svg",
  83. no_arrows => 1,
  84. polygons => [$convex_hull],
  85. red_points => \@t,
  86. points => \@toolchange_points,
  87. );
  88. }
  89. # offset the skirt by the maximum displacement between extruders plus a safety extra margin
  90. my $delta = scale(20 * sqrt(2) + 1);
  91. my $outer_convex_hull = offset([$convex_hull], +$delta)->[0];
  92. ok !(defined first { !$outer_convex_hull->contains_point($_) } @toolchange_points), 'all toolchanges happen within expected area';
  93. }
  94. {
  95. my $config = Slic3r::Config->new_from_defaults;
  96. $config->set('support_material_extruder', 3);
  97. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  98. ok Slic3r::Test::gcode($print), 'no errors when using non-consecutive extruders';
  99. }
  100. {
  101. my $config = Slic3r::Config->new;
  102. $config->set('extruder', 2);
  103. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  104. like Slic3r::Test::gcode($print), qr/ T1/, 'extruder shortcut';
  105. }
  106. {
  107. my $config = Slic3r::Config->new;
  108. $config->set('perimeter_extruder', 2);
  109. $config->set('infill_extruder', 2);
  110. $config->set('support_material_extruder', 2);
  111. $config->set('support_material_interface_extruder', 2);
  112. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  113. ok Slic3r::Test::gcode($print), 'no errors when using multiple skirts with a single, non-zero, extruder';
  114. }
  115. {
  116. my $model = stacked_cubes();
  117. my $lower_config = $model->get_material('lower')->config;
  118. my $upper_config = $model->get_material('upper')->config;
  119. $lower_config->set('extruder', 1);
  120. $lower_config->set('bottom_solid_layers', 0);
  121. $lower_config->set('top_solid_layers', 1);
  122. $upper_config->set('extruder', 2);
  123. $upper_config->set('bottom_solid_layers', 1);
  124. $upper_config->set('top_solid_layers', 0);
  125. my $config = Slic3r::Config->new_from_defaults;
  126. $config->set('fill_density', 0);
  127. $config->set('solid_infill_speed', 99);
  128. $config->set('top_solid_infill_speed', 99);
  129. $config->set('cooling', 0); # for preventing speeds from being altered
  130. $config->set('first_layer_speed', '100%'); # for preventing speeds from being altered
  131. my $test = sub {
  132. my $print = Slic3r::Test::init_print($model, config => $config);
  133. my $tool = undef;
  134. my %T0_shells = my %T1_shells = (); # Z => 1
  135. Slic3r::GCode::Reader->new->parse(my $gcode = Slic3r::Test::gcode($print), sub {
  136. my ($self, $cmd, $args, $info) = @_;
  137. if ($cmd =~ /^T(\d+)/) {
  138. $tool = $1;
  139. } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
  140. if (($args->{F} // $self->F) == $config->solid_infill_speed*60) {
  141. if ($tool == 0) {
  142. $T0_shells{$self->Z} = 1;
  143. } elsif ($tool == 1) {
  144. $T1_shells{$self->Z} = 1;
  145. }
  146. }
  147. }
  148. });
  149. return [ sort keys %T0_shells ], [ sort keys %T1_shells ];
  150. };
  151. {
  152. my ($t0, $t1) = $test->();
  153. is scalar(@$t0), 0, 'no interface shells';
  154. is scalar(@$t1), 0, 'no interface shells';
  155. }
  156. {
  157. $config->set('interface_shells', 1);
  158. my ($t0, $t1) = $test->();
  159. is scalar(@$t0), $lower_config->top_solid_layers, 'top interface shells';
  160. is scalar(@$t1), $upper_config->bottom_solid_layers, 'bottom interface shells';
  161. }
  162. }
  163. {
  164. my $model = stacked_cubes();
  165. my $object = $model->objects->[0];
  166. my $config = Slic3r::Config->new_from_defaults;
  167. $config->set('layer_height', 0.4);
  168. $config->set('first_layer_height', '100%');
  169. $config->set('skirts', 0);
  170. my $print = Slic3r::Test::init_print($model, config => $config);
  171. is $object->volumes->[0]->config->extruder, 1, 'auto_assign_extruders() assigned correct extruder to first volume';
  172. is $object->volumes->[1]->config->extruder, 2, 'auto_assign_extruders() assigned correct extruder to second volume';
  173. my $tool = undef;
  174. my %T0 = my %T1 = (); # Z => 1
  175. Slic3r::GCode::Reader->new->parse(my $gcode = Slic3r::Test::gcode($print), sub {
  176. my ($self, $cmd, $args, $info) = @_;
  177. if ($cmd =~ /^T(\d+)/) {
  178. $tool = $1;
  179. } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
  180. if ($tool == 0) {
  181. $T0{$self->Z} = 1;
  182. } elsif ($tool == 1) {
  183. $T1{$self->Z} = 1;
  184. }
  185. }
  186. });
  187. ok !(defined first { $_ > 20 } keys %T0), 'T0 is never used for upper object';
  188. ok !(defined first { $_ < 20 } keys %T1), 'T1 is never used for lower object';
  189. }
  190. sub stacked_cubes {
  191. my $model = Slic3r::Model->new;
  192. my $object = $model->add_object;
  193. $object->add_volume(mesh => Slic3r::Test::mesh('20mm_cube'), material_id => 'lower');
  194. $object->add_volume(mesh => Slic3r::Test::mesh('20mm_cube', translate => [0,0,20]), material_id => 'upper');
  195. $object->add_instance(offset => Slic3r::Pointf->new(0,0));
  196. return $model;
  197. }
  198. __END__