support.t 14 KB

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