1
use Test::More tests => 17;
7
use lib "$FindBin::Bin/../lib";
10
use List::Util qw(first sum);
12
use Slic3r::Geometry qw(epsilon);
16
my $config = Slic3r::Config->new_from_defaults;
17
$config->set('skirts', 0);
18
$config->set('perimeters', 0);
19
$config->set('solid_infill_speed', 99);
20
$config->set('top_solid_infill_speed', 99);
21
$config->set('bridge_speed', 72);
22
$config->set('first_layer_speed', '100%');
23
$config->set('cooling', 0);
29
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
32
my %layers_with_solid_infill = (); # Z => $count
33
my %layers_with_bridge_infill = (); # Z => $count
34
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
35
my ($self, $cmd, $args, $info) = @_;
39
if ($info->{extruding} && $info->{dist_XY} > 0) {
40
my $F = $args->{F} // $self->F;
41
$layers_with_solid_infill{$self->Z} = 1
42
if $F == $config->solid_infill_speed*60;
43
$layers_with_bridge_infill{$self->Z} = 1
44
if $F == $config->bridge_speed*60;
48
my @z = sort { $a <=> $b } keys %z;
49
my @shells = map $layers_with_solid_infill{$_} || $layers_with_bridge_infill{$_}, @z;
50
fail "insufficient number of bottom solid layers"
51
unless !defined(first { !$_ } @shells[0..$config->bottom_solid_layers-1]);
52
fail "excessive number of bottom solid layers"
53
unless scalar(grep $_, @shells[0 .. $#shells/2]) == $config->bottom_solid_layers;
54
fail "insufficient number of top solid layers"
55
unless !defined(first { !$_ } @shells[-$config->top_solid_layers..-1]);
56
fail "excessive number of top solid layers"
57
unless scalar(grep $_, @shells[($#shells/2)..$#shells]) == $config->top_solid_layers;
58
if ($config->top_solid_layers > 0) {
59
fail "unexpected solid infill speed in first solid layer over sparse infill"
60
if $layers_with_solid_infill{ $z[-$config->top_solid_layers] };
61
die "bridge speed not used in first solid layer over sparse infill"
62
if !$layers_with_bridge_infill{ $z[-$config->top_solid_layers] };
67
$config->set('top_solid_layers', 3);
68
$config->set('bottom_solid_layers', 3);
69
ok $test->(), "proper number of shells is applied";
71
$config->set('top_solid_layers', 0);
72
$config->set('bottom_solid_layers', 0);
73
ok $test->(), "no shells are applied when both top and bottom are set to zero";
75
$config->set('fill_density', 0);
76
ok $test->(), "proper number of shells is applied even when fill density is none";
81
my $config = Slic3r::Config->new_from_defaults;
82
$config->set('layer_height', 0.3);
83
$config->set('first_layer_height', '100%');
84
$config->set('bottom_solid_layers', 0);
85
$config->set('top_solid_layers', 3);
86
$config->set('cooling', 0);
87
$config->set('bridge_speed', 99);
88
$config->set('solid_infill_speed', 99);
89
$config->set('top_solid_infill_speed', 99);
90
$config->set('first_layer_speed', '100%');
92
my $print = Slic3r::Test::init_print('V', config => $config);
93
my %layers_with_solid_infill = (); # Z => 1
94
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
95
my ($self, $cmd, $args, $info) = @_;
97
$layers_with_solid_infill{$self->Z} = 1
98
if $info->{extruding} && ($args->{F} // $self->F) == $config->solid_infill_speed*60;
100
is scalar(map $layers_with_solid_infill{$_}, grep $_ <= 7.2, keys %layers_with_solid_infill), 3,
101
"correct number of top solid shells is generated in V-shaped object";
105
my $config = Slic3r::Config->new_from_defaults;
106
# we need to check against one perimeter because this test is calibrated
107
# (shape, extrusion_width) so that perimeters cover the bottom surfaces of
108
# their lower layer - the test checks that shells are not generated on the
109
# above layers (thus 'across' the shadow perimeter)
110
# the test is actually calibrated to leave a narrow bottom region for each
111
# layer - we test that in case of fill_density = 0 such narrow shells are
112
# discarded instead of grown
113
$config->set('perimeters', 1);
114
$config->set('fill_density', 0);
115
$config->set('cooling', 0); # prevent speed alteration
116
$config->set('first_layer_speed', '100%'); # prevent speed alteration
117
$config->set('layer_height', 0.4);
118
$config->set('first_layer_height', '100%');
119
$config->set('extrusion_width', 0.55);
120
$config->set('bottom_solid_layers', 3);
121
$config->set('top_solid_layers', 0);
122
$config->set('solid_infill_speed', 99);
124
my $print = Slic3r::Test::init_print('V', config => $config);
125
my %layers = (); # Z => 1
126
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
127
my ($self, $cmd, $args, $info) = @_;
128
$layers{$self->Z} = 1
129
if $info->{extruding} && ($args->{F} // $self->F) == $config->solid_infill_speed*60;
131
is scalar(keys %layers), $config->bottom_solid_layers,
132
"shells are not propagated across perimeters of the neighbor layer";
136
my $config = Slic3r::Config->new_from_defaults;
137
$config->set('perimeters', 3);
138
$config->set('cooling', 0); # prevent speed alteration
139
$config->set('first_layer_speed', '100%'); # prevent speed alteration
140
$config->set('layer_height', 0.4);
141
$config->set('first_layer_height', '100%');
142
$config->set('bottom_solid_layers', 3);
143
$config->set('top_solid_layers', 3);
144
$config->set('solid_infill_speed', 99);
145
$config->set('top_solid_infill_speed', 99);
147
my $print = Slic3r::Test::init_print('sloping_hole', config => $config);
148
my %solid_layers = (); # Z => 1
149
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
150
my ($self, $cmd, $args, $info) = @_;
151
$solid_layers{$self->Z} = 1
152
if $info->{extruding} && ($args->{F} // $self->F) == $config->solid_infill_speed*60;
154
is scalar(keys %solid_layers), $config->bottom_solid_layers + $config->top_solid_layers,
155
"no superfluous shells are generated";
159
my $config = Slic3r::Config->new_from_defaults;
160
$config->set('perimeters', 1);
161
$config->set('fill_density', 0);
162
$config->set('top_solid_layers', 0);
163
$config->set('spiral_vase', 1);
164
$config->set('bottom_solid_layers', 0);
165
$config->set('skirts', 0);
166
$config->set('first_layer_height', '100%');
167
$config->set('start_gcode', '');
169
# TODO: this needs to be tested with a model with sloping edges, where starting
170
# points of each layer are not aligned - in that case we would test that no
171
# travel moves are left to move to the new starting point - in a cube, end
172
# points coincide with next layer starting points (provided there's no clipping)
174
my ($model_name, $description) = @_;
175
my $print = Slic3r::Test::init_print($model_name, config => $config);
176
my $travel_moves_after_first_extrusion = 0;
177
my $started_extruding = 0;
179
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
180
my ($self, $cmd, $args, $info) = @_;
183
$started_extruding = 1 if $info->{extruding};
184
push @z_steps, $info->{dist_Z}
185
if $started_extruding && $info->{dist_Z} > 0;
186
$travel_moves_after_first_extrusion++
187
if $info->{travel} && $started_extruding && !exists $args->{Z};
191
# we allow one travel move after first extrusion: i.e. when moving to the first
192
# spiral point after moving to second layer (bottom layer had loop clipping, so
193
# we're slightly distant from the starting point of the loop)
194
ok $travel_moves_after_first_extrusion <= 1, "no gaps in spiral vase ($description)";
195
ok !(grep { $_ > $config->layer_height + epsilon } @z_steps), "no gaps in Z ($description)";
198
$test->('20mm_cube', 'solid model');
200
$config->set('z_offset', -10);
201
$test->('20mm_cube', 'solid model with negative z-offset');
203
### Disabled because the current unreliable medial axis code doesn't
204
### always produce valid loops.
205
###$test->('40x10', 'hollow model with negative z-offset');
209
my $config = Slic3r::Config->new_from_defaults;
210
$config->set('spiral_vase', 1);
211
$config->set('perimeters', 1);
212
$config->set('fill_density', 0);
213
$config->set('top_solid_layers', 0);
214
$config->set('bottom_solid_layers', 0);
215
$config->set('retract_layer_change', [0]);
216
$config->set('skirts', 0);
217
$config->set('first_layer_height', '100%');
218
$config->set('layer_height', 0.4);
219
$config->set('start_gcode', '');
222
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
224
my @this_layer = (); # [ dist_Z, dist_XY ], ...
226
my $bottom_layer_not_flat = 0;
227
my $null_z_moves_not_layer_changes = 0;
228
my $null_z_moves_not_multiples_of_layer_height = 0;
229
my $sum_of_partial_z_equals_to_layer_height = 0;
230
my $all_layer_segments_have_same_slope = 0;
231
my $horizontal_extrusions = 0;
233
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
234
my ($self, $cmd, $args, $info) = @_;
238
# skip everything up to the second Z move
239
# (i.e. start of second layer)
240
if (exists $args->{Z}) {
242
$bottom_layer_not_flat = 1
243
if $info->{dist_Z} > 0 && $info->{dist_Z} != $config->layer_height;
245
} elsif ($info->{dist_Z} == 0 && $args->{Z}) {
246
$null_z_moves_not_layer_changes = 1
247
if $info->{dist_XY} != 0;
249
# % doesn't work easily with floats
250
$null_z_moves_not_multiples_of_layer_height = 1
251
if abs(($args->{Z} / $config->layer_height) * $config->layer_height - $args->{Z}) > epsilon;
253
my $total_dist_XY = sum(map $_->[1], @this_layer);
254
$sum_of_partial_z_equals_to_layer_height = 1
255
if abs(sum(map $_->[0], @this_layer) - $config->layer_height) > epsilon;
257
foreach my $segment (@this_layer) {
258
# check that segment's dist_Z is proportioned to its dist_XY
259
$all_layer_segments_have_same_slope = 1
260
if abs($segment->[0]*$total_dist_XY/$config->layer_height - $segment->[1]) > 0.1;
264
} elsif ($info->{extruding} && $info->{dist_XY} > 0) {
265
$horizontal_extrusions = 1
266
if $info->{dist_Z} == 0;
267
push @this_layer, [ $info->{dist_Z}, $info->{dist_XY} ];
271
ok !$bottom_layer_not_flat, 'bottom layer is flat when using spiral vase';
272
ok !$null_z_moves_not_layer_changes, 'null Z moves are layer changes';
273
ok !$null_z_moves_not_multiples_of_layer_height, 'null Z moves are multiples of layer height';
274
ok !$sum_of_partial_z_equals_to_layer_height, 'sum of partial Z increments equals to a full layer height';
275
ok !$all_layer_segments_have_same_slope, 'all layer segments have the same slope';
276
ok !$horizontal_extrusions, 'no horizontal extrusions';
280
my $config = Slic3r::Config->new_from_defaults;
281
$config->set('perimeters', 1);
282
$config->set('fill_density', 0);
283
$config->set('top_solid_layers', 0);
284
$config->set('spiral_vase', 1);
285
$config->set('bottom_solid_layers', 0);
286
$config->set('skirts', 0);
287
$config->set('first_layer_height', '100%');
288
$config->set('start_gcode', '');
290
my $print = Slic3r::Test::init_print('two_hollow_squares', config => $config);
291
my $diagonal_moves = 0;
292
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
293
my ($self, $cmd, $args, $info) = @_;
296
if ($info->{extruding} && $info->{dist_XY} > 0) {
297
if ($info->{dist_Z} > 0) {
303
is $diagonal_moves, 0, 'no spiral moves on two-island object';