diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 969991f9..405df49c 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -131,25 +131,33 @@ sub change_layer { sub move_z { my ($self, $z, $comment) = @_; - $z += $self->config->z_offset; - my $gcode = ""; + + $z += $self->config->z_offset; my $current_z = $self->z; - if (!defined $self->z || $z > $self->z) { - # if we're going over the current Z we won't be lifted anymore + my $nominal_z = defined $current_z ? ($current_z - $self->lifted) : undef; + + if (!defined $current_z || $z > $current_z || $z < $nominal_z) { + # we're moving above the current actual Z (so above the lift height of the current + # layer if any) or below the current nominal layer + + # in both cases, we're going to the nominal Z of the next layer $self->lifted(0); - # this retraction may alter $self->z - $gcode .= $self->retract(move_z => $z) if $self->extruder->retract_layer_change; + if ($self->extruder->retract_layer_change) { + # this retraction may alter $self->z + $gcode .= $self->retract(move_z => $z); + $current_z = $self->z; # update current z in case retract() changed it + $nominal_z = defined $current_z ? ($current_z - $self->lifted) : undef; + } $self->speed('travel'); $gcode .= $self->G0(undef, $z, 0, $comment || ('move to next layer (' . $self->layer->id . ')')) - if !defined $self->z || abs($z - ($self->z - $self->lifted)) > epsilon; - } elsif ($z < $self->z && $z > ($self->z - $self->lifted + epsilon)) { - # we're moving to a layer height which is greater than the nominal current one - # (nominal = actual - lifted) and less than the actual one. we're basically - # advancing to next layer, whose nominal Z is still lower than the previous + if !defined $current_z || abs($z - $nominal_z) > epsilon; + } elsif ($z < $current_z) { + # we're moving above the current nominal layer height and below the current actual one. + # we're basically advancing to next layer, whose nominal Z is still lower than the previous # layer Z with lift. - $self->lifted($self->z - $z); + $self->lifted($current_z - $z); } return $gcode; diff --git a/t/gcode.t b/t/gcode.t index f8ee4158..17f4ed82 100644 --- a/t/gcode.t +++ b/t/gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 4; +use Test::More tests => 6; use strict; use warnings; @@ -43,16 +43,30 @@ use Slic3r::Test; } { + # This tests the following behavior: + # - complete objects does not crash + # - no hard-coded "E" are generated + # - Z moves are correctly generated for both objects my $config = Slic3r::Config->new_from_defaults; $config->set('complete_objects', 1); $config->set('duplicate', 2); $config->set('extrusion_axis', 'A'); + $config->set('start_gcode', ''); # prevent any default extra Z move + $config->set('layer_height', 0.4); + $config->set('first_layer_height', 0.4); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); ok my $gcode = Slic3r::Test::gcode($print), "complete_objects"; + my @z_moves = (); Slic3r::GCode::Reader->new->parse($gcode, sub { my ($self, $cmd, $args, $info) = @_; fail 'unexpected E argument' if defined $args->{E}; + if (defined $args->{Z}) { + push @z_moves, $args->{Z}; + } }); + my $layer_count = 20/0.4; # cube is 20mm tall + is scalar(@z_moves), 2*$layer_count, 'complete_objects generates the correct number of Z moves'; + is_deeply [ @z_moves[0..($layer_count-1)] ], [ @z_moves[$layer_count..$#z_moves] ], 'complete_objects generates the correct Z moves'; } __END__