support.t 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. use Test::More tests => 29;
  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(epsilon scale PI);
  12. use Slic3r::Geometry::Clipper qw(diff);
  13. use Slic3r::Test;
  14. {
  15. my $config = Slic3r::Config->new_from_defaults;
  16. $config->set('support_material', 1);
  17. my @contact_z = my @top_z = ();
  18. my $test = sub {
  19. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  20. my $flow = $print->print->objects->[0]->support_material_flow;
  21. my $support = Slic3r::Print::SupportMaterial->new(
  22. object_config => $print->print->objects->[0]->config,
  23. print_config => $print->print->config,
  24. flow => $flow,
  25. interface_flow => $flow,
  26. first_layer_flow => $flow,
  27. );
  28. my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height);
  29. my $expected_top_spacing = $support->contact_distance($config->layer_height, $config->nozzle_diameter->[0]);
  30. is $support_z->[0], $config->first_layer_height,
  31. 'first layer height is honored';
  32. is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0,
  33. 'no null or negative support layers';
  34. is scalar(grep { $support_z->[$_]-$support_z->[$_-1] > $config->nozzle_diameter->[0] + epsilon } 1..$#$support_z), 0,
  35. 'no layers thicker than nozzle diameter';
  36. my $wrong_top_spacing = 0;
  37. foreach my $top_z (@top_z) {
  38. # find layer index of this top surface
  39. my $layer_id = first { abs($support_z->[$_] - $top_z) < epsilon } 0..$#$support_z;
  40. # check that first support layer above this top surface (or the next one) is spaced with nozzle diameter
  41. $wrong_top_spacing = 1
  42. if ($support_z->[$layer_id+1] - $support_z->[$layer_id]) != $expected_top_spacing
  43. && ($support_z->[$layer_id+2] - $support_z->[$layer_id]) != $expected_top_spacing;
  44. }
  45. ok !$wrong_top_spacing, 'layers above top surfaces are spaced correctly';
  46. };
  47. $config->set('layer_height', 0.2);
  48. $config->set('first_layer_height', 0.3);
  49. @contact_z = (1.9);
  50. @top_z = (1.1);
  51. $test->();
  52. $config->set('first_layer_height', 0.4);
  53. $test->();
  54. $config->set('layer_height', $config->nozzle_diameter->[0]);
  55. $test->();
  56. }
  57. {
  58. my $config = Slic3r::Config->new_from_defaults;
  59. $config->set('raft_layers', 3);
  60. $config->set('brim_width', 0);
  61. $config->set('skirts', 0);
  62. $config->set('support_material_extruder', 2);
  63. $config->set('support_material_interface_extruder', 2);
  64. $config->set('layer_height', 0.4);
  65. $config->set('first_layer_height', 0.4);
  66. my $print = Slic3r::Test::init_print('overhang', config => $config);
  67. ok my $gcode = Slic3r::Test::gcode($print), 'no conflict between raft/support and brim';
  68. my $tool = 0;
  69. Slic3r::GCode::Reader->new->parse($gcode, sub {
  70. my ($self, $cmd, $args, $info) = @_;
  71. if ($cmd =~ /^T(\d+)/) {
  72. $tool = $1;
  73. } elsif ($info->{extruding}) {
  74. if ($self->Z <= ($config->raft_layers * $config->layer_height)) {
  75. fail 'not extruding raft with support material extruder'
  76. if $tool != ($config->support_material_extruder-1);
  77. } else {
  78. fail 'support material exceeds raft layers'
  79. if $tool == $config->support_material_extruder-1;
  80. # TODO: we should test that full support is generated when we use raft too
  81. }
  82. }
  83. });
  84. }
  85. {
  86. my $config = Slic3r::Config->new_from_defaults;
  87. $config->set('skirts', 0);
  88. $config->set('raft_layers', 3);
  89. $config->set('support_material_pattern', 'honeycomb');
  90. $config->set('support_material_extrusion_width', 0.6);
  91. $config->set('first_layer_extrusion_width', '100%');
  92. $config->set('bridge_speed', 99);
  93. $config->set('cooling', 0); # prevent speed alteration
  94. $config->set('first_layer_speed', '100%'); # prevent speed alteration
  95. $config->set('start_gcode', ''); # prevent any unexpected Z move
  96. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  97. my $layer_id = -1; # so that first Z move sets this to 0
  98. my @raft = my @first_object_layer = ();
  99. my %first_object_layer_speeds = (); # F => 1
  100. Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
  101. my ($self, $cmd, $args, $info) = @_;
  102. if ($info->{extruding} && $info->{dist_XY} > 0) {
  103. if ($layer_id <= $config->raft_layers) {
  104. # this is a raft layer or the first object layer
  105. my $line = Slic3r::Line->new_scale([ $self->X, $self->Y ], [ $info->{new_X}, $info->{new_Y} ]);
  106. my @path = @{$line->grow(scale($config->support_material_extrusion_width/2))};
  107. if ($layer_id < $config->raft_layers) {
  108. # this is a raft layer
  109. push @raft, @path;
  110. } else {
  111. push @first_object_layer, @path;
  112. $first_object_layer_speeds{ $args->{F} // $self->F } = 1;
  113. }
  114. }
  115. } elsif ($cmd eq 'G1' && $info->{dist_Z} > 0) {
  116. $layer_id++;
  117. }
  118. });
  119. ok !@{diff(\@first_object_layer, \@raft)},
  120. 'first object layer is completely supported by raft';
  121. is scalar(keys %first_object_layer_speeds), 1,
  122. 'only one speed used in first object layer';
  123. ok +(keys %first_object_layer_speeds)[0] == $config->bridge_speed*60,
  124. 'bridge speed used in first object layer';
  125. }
  126. {
  127. my $config = Slic3r::Config->new_from_defaults;
  128. $config->set('layer_height', 0.2);
  129. $config->set('skirts', 0);
  130. $config->set('raft_layers', 5);
  131. $config->set('support_material_pattern', 'rectilinear');
  132. $config->set('support_material_extrusion_width', 0.4);
  133. $config->set('support_material_interface_extrusion_width', 0.6);
  134. $config->set('support_material_interface_layers', 2);
  135. $config->set('first_layer_extrusion_width', '100%');
  136. $config->set('bridge_speed', 99);
  137. $config->set('cooling', 0); # prevent speed alteration
  138. $config->set('first_layer_speed', '100%'); # prevent speed alteration
  139. $config->set('start_gcode', ''); # prevent any unexpected Z move
  140. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  141. my $layer_id = -1;
  142. my $success = 1;
  143. Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
  144. my ($self, $cmd, $args, $info) = @_;
  145. if ($info->{extruding} && $info->{dist_XY} > 0) {
  146. # this is a raft layer
  147. if (($layer_id < $config->raft_layers) && ($layer_id > 0)) {
  148. my $width;
  149. my $support_layer_height = $config->nozzle_diameter->[0] * 0.75;
  150. # support layer
  151. if ($config->raft_layers - $config->support_material_interface_layers > $layer_id) {
  152. $width = $config->support_material_extrusion_width;
  153. }
  154. # interface layer
  155. else {
  156. $width = $config->support_material_interface_extrusion_width;
  157. }
  158. my $expected_E_per_mm3 = 4 / (($config->filament_diameter->[0]**2) * PI);
  159. my $expected_mm3_per_mm = $width * $support_layer_height + ($support_layer_height**2) / 4.0 * (PI-4.0);
  160. my $expected_e_per_mm = $expected_E_per_mm3 * $expected_mm3_per_mm;
  161. my $e_per_mm = ($info->{dist_E} / $info->{dist_XY});;
  162. my $diff = abs($e_per_mm - $expected_e_per_mm);
  163. if ($diff > 0.001) {
  164. $success = 0;
  165. }
  166. }
  167. } elsif ($cmd eq 'G1' && $info->{dist_Z} > 0) {
  168. $layer_id++;
  169. }
  170. });
  171. ok $success,
  172. 'support material interface extrusion width is used for interfaces';
  173. }
  174. {
  175. my $config = Slic3r::Config->new_from_defaults;
  176. $config->set('skirts', 0);
  177. $config->set('layer_height', 0.35);
  178. $config->set('first_layer_height', 0.3);
  179. $config->set('nozzle_diameter', [0.5]);
  180. $config->set('support_material_extruder', 2);
  181. $config->set('support_material_interface_extruder', 2);
  182. my $test = sub {
  183. my ($raft_layers) = @_;
  184. $config->set('raft_layers', $raft_layers);
  185. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  186. my %raft_z = (); # z => 1
  187. my $tool = undef;
  188. Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
  189. my ($self, $cmd, $args, $info) = @_;
  190. if ($cmd =~ /^T(\d+)/) {
  191. $tool = $1;
  192. } elsif ($info->{extruding} && $info->{dist_XY} > 0) {
  193. if ($tool == $config->support_material_extruder-1) {
  194. $raft_z{$self->Z} = 1;
  195. }
  196. }
  197. });
  198. is scalar(keys %raft_z), $config->raft_layers, 'correct number of raft layers is generated';
  199. };
  200. $test->(2);
  201. $test->(70);
  202. $config->set('layer_height', 0.4);
  203. $config->set('first_layer_height', 0.35);
  204. $test->(3);
  205. $test->(70);
  206. }
  207. {
  208. my $config = Slic3r::Config->new_from_defaults;
  209. $config->set('brim_width', 0);
  210. $config->set('skirts', 0);
  211. $config->set('support_material', 1);
  212. $config->set('top_solid_layers', 0); # so that we don't have the internal bridge over infill
  213. $config->set('bridge_speed', 99);
  214. $config->set('cooling', 0);
  215. $config->set('first_layer_speed', '100%');
  216. my $test = sub {
  217. my $print = Slic3r::Test::init_print('overhang', config => $config);
  218. my $has_bridge_speed = 0;
  219. Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
  220. my ($self, $cmd, $args, $info) = @_;
  221. if ($info->{extruding}) {
  222. if (($args->{F} // $self->F) == $config->bridge_speed*60) {
  223. $has_bridge_speed = 1;
  224. }
  225. }
  226. });
  227. return $has_bridge_speed;
  228. };
  229. $config->set('support_material_contact_distance', 0.2);
  230. ok $test->(), 'bridge speed is used when support_material_contact_distance > 0';
  231. $config->set('support_material_contact_distance', 0);
  232. ok !$test->(), 'bridge speed is not used when support_material_contact_distance == 0';
  233. $config->set('raft_layers', 5);
  234. $config->set('support_material_contact_distance', 0.2);
  235. ok $test->(), 'bridge speed is used when raft_layers > 0 and support_material_contact_distance > 0';
  236. $config->set('support_material_contact_distance', 0);
  237. ok !$test->(), 'bridge speed is not used when raft_layers > 0 and support_material_contact_distance == 0';
  238. }
  239. {
  240. my $config = Slic3r::Config->new_from_defaults;
  241. $config->set('skirts', 0);
  242. $config->set('start_gcode', '');
  243. $config->set('raft_layers', 8);
  244. $config->set('nozzle_diameter', [0.4, 1]);
  245. $config->set('layer_height', 0.1);
  246. $config->set('first_layer_height', 0.8);
  247. $config->set('support_material_extruder', 2);
  248. $config->set('support_material_interface_extruder', 2);
  249. $config->set('support_material_contact_distance', 0);
  250. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  251. ok my $gcode = Slic3r::Test::gcode($print), 'first_layer_height is validated with support material extruder nozzle diameter when using raft layers';
  252. my $tool = undef;
  253. my @z = (0);
  254. my %layer_heights_by_tool = (); # tool => [ lh, lh... ]
  255. Slic3r::GCode::Reader->new->parse($gcode, sub {
  256. my ($self, $cmd, $args, $info) = @_;
  257. if ($cmd =~ /^T(\d+)/) {
  258. $tool = $1;
  259. } elsif ($cmd eq 'G1' && exists $args->{Z} && $args->{Z} != $self->Z) {
  260. push @z, $args->{Z};
  261. } elsif ($info->{extruding} && $info->{dist_XY} > 0) {
  262. $layer_heights_by_tool{$tool} ||= [];
  263. push @{ $layer_heights_by_tool{$tool} }, $z[-1] - $z[-2];
  264. }
  265. });
  266. ok !defined(first { $_ > $config->nozzle_diameter->[0] + epsilon }
  267. @{ $layer_heights_by_tool{$config->perimeter_extruder-1} }),
  268. 'no object layer is thicker than nozzle diameter';
  269. ok !defined(first { abs($_ - $config->layer_height) < epsilon }
  270. @{ $layer_heights_by_tool{$config->support_material_extruder-1} }),
  271. 'no support material layer is as thin as object layers';
  272. }
  273. {
  274. my $config = Slic3r::Config->new_from_defaults;
  275. $config->set('support_material_enforce_layers', 100);
  276. $config->set('support_material', 0);
  277. my @contact_z = my @top_z = ();
  278. my $test = sub {
  279. my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
  280. my $flow = $print->print->objects->[0]->support_material_flow;
  281. my $support = Slic3r::Print::SupportMaterial->new(
  282. object_config => $print->print->objects->[0]->config,
  283. print_config => $print->print->config,
  284. flow => $flow,
  285. interface_flow => $flow,
  286. first_layer_flow => $flow,
  287. );
  288. my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height);
  289. is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0,
  290. 'forced support is generated';
  291. };
  292. $config->set('layer_height', 0.2);
  293. $config->set('first_layer_height', 0.3);
  294. @contact_z = (1.9);
  295. @top_z = (1.1);
  296. $test->();
  297. }
  298. __END__