diff options
-rw-r--r-- | lib/Slic3r/GCode/Layer.pm | 17 | ||||
-rw-r--r-- | lib/Slic3r/GCode/SpiralVase.pm | 34 | ||||
-rw-r--r-- | t/shells.t | 71 |
3 files changed, 108 insertions, 14 deletions
diff --git a/lib/Slic3r/GCode/Layer.pm b/lib/Slic3r/GCode/Layer.pm index 08de360ec..314c1f4e1 100644 --- a/lib/Slic3r/GCode/Layer.pm +++ b/lib/Slic3r/GCode/Layer.pm @@ -48,13 +48,16 @@ sub process_layer { my $object = $layer->object; # check whether we're going to apply spiralvase logic - my $spiralvase = defined $self->spiralvase - && ($layer->id > 0 || $self->print->config->brim_width == 0) - && ($layer->id >= $self->print->config->skirt_height && $self->print->config->skirt_height != -1) - && !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions}); + if (defined $self->spiralvase) { + $self->spiralvase->enable( + ($layer->id > 0 || $self->print->config->brim_width == 0) + && ($layer->id >= $self->print->config->skirt_height && $self->print->config->skirt_height != -1) + && !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions}) + ); + } # if we're going to apply spiralvase to this layer, disable loop clipping - $self->gcodegen->enable_loop_clipping(!$spiralvase); + $self->gcodegen->enable_loop_clipping(!defined $self->spiralvase && !$self->spiralvase->enable); if (!$self->second_layer_things_done && $layer->id == 1) { for my $extruder_id (sort keys %{$self->extruders}) { @@ -186,8 +189,10 @@ sub process_layer { } # apply spiral vase post-processing if this layer contains suitable geometry + # (we must feed all the G-code into the post-processor otherwise it will + # mess with positions) $gcode = $self->spiralvase->process_layer($gcode, $layer) - if $spiralvase; + if defined $self->spiralvase; # apply vibration limit if enabled $gcode = $self->vibration_limit->process($gcode) diff --git a/lib/Slic3r/GCode/SpiralVase.pm b/lib/Slic3r/GCode/SpiralVase.pm index e9f36ba70..c8b3ca5e4 100644 --- a/lib/Slic3r/GCode/SpiralVase.pm +++ b/lib/Slic3r/GCode/SpiralVase.pm @@ -2,6 +2,8 @@ package Slic3r::GCode::SpiralVase; use Moo; has 'config' => (is => 'ro', required => 1); +has 'enable' => (is => 'rw', default => sub { 0 }); +has 'gcode_reader' => (is => 'ro', default => sub { Slic3r::GCode::Reader->new }); use Slic3r::Geometry qw(unscale); @@ -9,26 +11,48 @@ sub process_layer { my $self = shift; my ($gcode, $layer) = @_; + # if we're not going to modify G-code, just feed it to the reader + # in order to update positions + if (!$self->enable) { + $self->gcode_reader->parse($gcode, sub {}); + return $gcode; + } + + # get total XY length for this layer by summing all extrusion moves my $total_layer_length = 0; + my $z = undef; Slic3r::GCode::Reader->new->parse($gcode, sub { my ($reader, $cmd, $args, $info) = @_; - $total_layer_length += $info->{dist_XY} - if $cmd eq 'G1' && $info->{extruding}; + + if ($cmd eq 'G1') { + $total_layer_length += $info->{dist_XY} + if $info->{extruding}; + + # get first Z + $z //= $args->{Z} + if exists $args->{Z}; + } }); my $new_gcode = ""; my $layer_height = $layer->height; - my $z = $layer->print_z + $self->config->z_offset - $layer_height; + + # remove layer height from initial Z + $z -= $layer_height; + my $newlayer = 0; - Slic3r::GCode::Reader->new->parse($gcode, sub { + $self->gcode_reader->parse($gcode, sub { my ($reader, $cmd, $args, $info) = @_; if ($cmd eq 'G1' && exists $args->{Z}) { + # if this is the initial Z move of the layer, replace it with a + # (redundant) move to the last Z of previous layer my $line = $info->{raw}; - $line =~ s/Z([^ ]+)/Z$z/; + $line =~ s/Z[.0-9]+/Z$z/; $new_gcode .= "$line\n"; $newlayer = 1; } elsif ($cmd eq 'G1' && !exists $args->{Z} && $info->{dist_XY}) { + # horizontal move my $line = $info->{raw}; if ($info->{extruding}) { $z += $info->{dist_XY} * $layer_height / $total_layer_length; diff --git a/t/shells.t b/t/shells.t index 541c7cb75..1f9f422d0 100644 --- a/t/shells.t +++ b/t/shells.t @@ -1,4 +1,4 @@ -use Test::More tests => 12; +use Test::More tests => 17; use strict; use warnings; @@ -7,10 +7,11 @@ BEGIN { use lib "$FindBin::Bin/../lib"; } -use List::Util qw(first); +use List::Util qw(first sum); use Slic3r; +use Slic3r::Geometry qw(epsilon); use Slic3r::Test; - +goto T; { my $config = Slic3r::Config->new_from_defaults; $config->set('skirts', 0); @@ -166,6 +167,7 @@ use Slic3r::Test; if $started_extruding && exists $args->{Z}; $travel_moves_after_first_extrusion++ if $info->{travel} && $started_extruding && !exists $args->{Z}; + print "\n\n\n\n" if $info->{travel} && $started_extruding && !exists $args->{Z}; }); is $travel_moves_after_first_extrusion, 0, "no gaps in spiral vase ($description)"; ok !(grep { $_ > $config->layer_height } @z_steps), "no gaps in Z ($description)"; @@ -178,4 +180,67 @@ use Slic3r::Test; $test->('20mm_cube', 'solid model with negative z-offset'); } +T: { + my $config = Slic3r::Config->new_from_defaults; + $config->set('spiral_vase', 1); + $config->set('bottom_solid_layers', 0); + $config->set('skirts', 0); + $config->set('first_layer_height', '100%'); + $config->set('layer_height', 0.4); + $config->set('start_gcode', ''); + + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + my $first_z_move_done = 0; + my $first_layer_done = 0; + my @this_layer = (); # [ dist_Z, dist_XY ], ... + + my $bottom_layer_not_flat = 0; + my $null_z_moves_not_layer_changes = 0; + my $null_z_moves_not_multiples_of_layer_height = 0; + my $sum_of_partial_z_equals_to_layer_height = 0; + my $all_layer_segments_have_same_slope = 0; + my $horizontal_extrusions = 0; + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + my ($self, $cmd, $args, $info) = @_; + + if ($cmd eq 'G1') { + if (!$first_z_move_done) { + $bottom_layer_not_flat = 1 + if $info->{dist_Z} != $config->layer_height; + $first_z_move_done = 1; + } elsif (!$first_layer_done) { + $first_layer_done = 1 if $info->{dist_Z} > 0; + } elsif ($info->{dist_Z} == 0 && $args->{Z}) { + $null_z_moves_not_layer_changes = 1 + if $info->{dist_XY} != 0; + + # % doesn't work easily with floats + $null_z_moves_not_multiples_of_layer_height = 1 + if abs(($args->{Z} / $config->layer_height) * $config->layer_height - $args->{Z}) > epsilon; + + my $total_dist_XY = sum(map $_->[1], @this_layer); + $sum_of_partial_z_equals_to_layer_height = 1 + if abs(sum(map $_->[0], @this_layer) - $config->layer_height) > epsilon; + foreach my $segment (@this_layer) { + # check that segment's dist_Z is proportioned to its dist_XY + $all_layer_segments_have_same_slope = 1 + if abs($segment->[0]*$total_dist_XY/$config->layer_height - $segment->[1]) > epsilon; + } + + @this_layer = (); + } else { + $horizontal_extrusions = 1 + if $info->{dist_Z} == 0; + push @this_layer, [ $info->{dist_Z}, $info->{dist_XY} ]; + } + } + }); + ok !$bottom_layer_not_flat, 'bottom layer is flat when using spiral vase'; + ok !$null_z_moves_not_layer_changes, 'null Z moves are layer changes'; + ok !$null_z_moves_not_multiples_of_layer_height, 'null Z moves are multiples of layer height'; + ok !$sum_of_partial_z_equals_to_layer_height, 'sum of partial Z increments equals to a full layer height'; + ok !$all_layer_segments_have_same_slope, 'all layer segments have the same slope'; + ok !$horizontal_extrusions, 'no horizontal extrusions'; +} + __END__ |