diff --git a/MANIFEST b/MANIFEST index 86390ed37..02faef124 100644 --- a/MANIFEST +++ b/MANIFEST @@ -75,6 +75,7 @@ t/gcode.t t/geometry.t t/layers.t t/loops.t +t/perimeters.t t/polyclip.t t/print.t t/retraction.t diff --git a/lib/Slic3r/ExtrusionPath/Collection.pm b/lib/Slic3r/ExtrusionPath/Collection.pm index 5a4af96de..88cc12528 100644 --- a/lib/Slic3r/ExtrusionPath/Collection.pm +++ b/lib/Slic3r/ExtrusionPath/Collection.pm @@ -14,7 +14,7 @@ sub first_point { sub chained_path { my $self = shift; - my ($start_near) = @_; + my ($start_near, $no_reverse) = @_; return @{$self->paths} if $self->no_sort; @@ -26,7 +26,7 @@ sub chained_path { polylines => [ map $_->polyline, @paths ], ); - return $collection->chained_path($start_near, \@paths); + return $collection->chained_path($start_near, \@paths, $no_reverse); } sub cleanup { diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index f1eada2d0..4dd4587d6 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -51,7 +51,7 @@ sub _build_speeds { my %role_speeds = ( &EXTR_ROLE_PERIMETER => 'perimeter', &EXTR_ROLE_EXTERNAL_PERIMETER => 'external_perimeter', - &EXTR_ROLE_OVERHANG_PERIMETER => 'external_perimeter', + &EXTR_ROLE_OVERHANG_PERIMETER => 'bridge', &EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER => 'perimeter', &EXTR_ROLE_FILL => 'infill', &EXTR_ROLE_SOLIDFILL => 'solid_infill', @@ -176,13 +176,19 @@ sub extrude_loop { # inwards move consider the new actual starting point) @paths = Slic3r::ExtrusionPath::Collection ->new(paths => [@paths]) - ->chained_path($last_pos); + ->chained_path($last_pos, 1); } else { push @paths, $extrusion_path; } + # apply the small perimeter speed + my %params = (); + if ($extrusion_path->is_perimeter && abs($extrusion_path->length) <= &Slic3r::SMALL_PERIMETER_LENGTH) { + $params{speed} = 'small_perimeter'; + } + # extrude along the path - my $gcode = join '', map $self->extrude_path($_, $description), @paths; + my $gcode = join '', map $self->extrude_path($_, $description, %params), @paths; $self->wipe_path($extrusion_path->polyline); # make a little move inwards before leaving loop @@ -211,23 +217,24 @@ sub extrude_loop { sub extrude_path { my $self = shift; - my ($path, $description, $recursive) = @_; + my ($path, $description, %params) = @_; $path = $path->unpack if $path->isa('Slic3r::ExtrusionPath::Packed'); $path->simplify(&Slic3r::SCALED_RESOLUTION); # detect arcs - if ($self->config->gcode_arcs && !$recursive) { + if ($self->config->gcode_arcs && !$params{dont_detect_arcs}) { my $gcode = ""; foreach my $arc_path ($path->detect_arcs) { - $gcode .= $self->extrude_path($arc_path, $description, 1); + $gcode .= $self->extrude_path($arc_path, $description, %params, dont_detect_arcs => 1); } return $gcode; } # go to first point of extrusion path my $gcode = ""; - $gcode .= $self->travel_to($path->points->[0], $path->role, "move to first $description point"); + $gcode .= $self->travel_to($path->points->[0], $path->role, "move to first $description point") + if !$self->last_pos || !$self->last_pos->coincides_with($path->points->[0]); # compensate retraction $gcode .= $self->unretract; @@ -257,12 +264,7 @@ sub extrude_path { my $e = $self->extruder->e_per_mm3 * $area; # set speed - $self->speed( $role_speeds{$path->role} || die "Unknown role: " . $path->role ); - if ($path->role == EXTR_ROLE_PERIMETER || $path->role == EXTR_ROLE_EXTERNAL_PERIMETER || $path->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) { - if (abs($path->length) <= &Slic3r::SMALL_PERIMETER_LENGTH) { - $self->speed('small_perimeter'); - } - } + $self->speed( $params{speed} || $role_speeds{$path->role} || die "Unknown role: " . $path->role ); # extrude arc or line my $path_length = 0; diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 50d3c5523..43a0917c5 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -221,7 +221,7 @@ has 'polylines' => (is => 'ro', default => sub { [] }); # Note that our polylines will be reversed in place when necessary. sub chained_path { my $self = shift; - my ($start_near, $items) = @_; + my ($start_near, $items, $no_reverse) = @_; $items ||= $self->polylines; my %items_map = map { $self->polylines->[$_] => $items->[$_] } 0 .. $#{$self->polylines}; @@ -229,7 +229,9 @@ sub chained_path { my @paths = (); my $start_at; - my $endpoints = [ map { $_->[0], $_->[-1] } @my_paths ]; + my $endpoints = $no_reverse + ? [ map { $_->[0], $_->[0] } @my_paths ] + : [ map { $_->[0], $_->[-1] } @my_paths ]; while (@my_paths) { # find nearest point my $start_index = $start_near @@ -237,7 +239,7 @@ sub chained_path { : 0; my $path_index = int($start_index/2); - if ($start_index%2) { # index is end so reverse to make it the start + if ($start_index % 2 && !$no_reverse) { # index is end so reverse to make it the start $my_paths[$path_index]->reverse; } push @paths, splice @my_paths, $path_index, 1; diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index f645e3821..89a3ed7de 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -34,6 +34,13 @@ sub model { $facets = [ [0,1,2],[2,1,3],[1,0,4],[5,1,4],[4,0,2],[6,4,2],[7,6,2],[8,9,7],[9,6,7],[2,3,7],[7,3,10],[1,5,3],[3,5,11],[11,12,13],[11,13,3],[3,13,10],[5,4,6],[11,5,6],[6,9,11],[11,9,12],[12,9,8],[13,12,8],[8,7,10],[13,8,10] ], + } elsif ($model_name eq 'overhang') { + $vertices = [ + [1364.68505859375,614.398010253906,20.002498626709],[1389.68505859375,614.398010253906,20.002498626709],[1377.18505859375,589.398986816406,20.002498626709],[1389.68505859375,589.398986816406,20.002498626709],[1389.68505859375,564.398986816406,20.0014991760254],[1364.68505859375,589.398986816406,20.002498626709],[1364.68505859375,564.398986816406,20.0014991760254],[1360.93505859375,589.398986816406,17.0014991760254],[1360.93505859375,585.64697265625,17.0014991760254],[1357.18505859375,564.398986816406,17.0014991760254],[1364.68505859375,589.398986816406,17.0014991760254],[1364.68505859375,571.899963378906,17.0014991760254],[1364.68505859375,564.398986816406,17.0014991760254],[1348.43603515625,564.398986816406,17.0014991760254],[1352.80908203125,589.398986816406,17.0014991760254],[1357.18408203125,589.398986816406,17.0014991760254],[1357.18310546875,614.398010253906,17.0014991760254],[1364.68505859375,606.89599609375,17.0014991760254],[1364.68505859375,614.398010253906,17.0014991760254],[1352.18603515625,564.398986816406,20.0014991760254],[1363.65405273438,589.398986816406,23.3004989624023],[1359.46704101562,589.398986816406,23.3004989624023],[1358.37109375,564.398986816406,23.3004989624023],[1385.56103515625,564.398986816406,23.3004989624023],[1373.06311035156,589.398986816406,23.3004989624023],[1368.80810546875,564.398986816406,23.3004989624023],[1387.623046875,589.398986816406,23.3004989624023],[1387.623046875,585.276000976562,23.3004989624023],[1389.68505859375,589.398986816406,23.3004989624023],[1389.68505859375,572.64599609375,23.3004989624023],[1389.68505859375,564.398986816406,23.3004989624023],[1367.77709960938,589.398986816406,23.3004989624023],[1366.7470703125,564.398986816406,23.3004989624023],[1354.31201171875,589.398986816406,23.3004989624023],[1352.18603515625,564.398986816406,23.3004989624023],[1389.68505859375,614.398010253906,23.3004989624023],[1377.31701660156,614.398010253906,23.3004989624023],[1381.43908691406,589.398986816406,23.3004989624023],[1368.80700683594,614.398010253906,23.3004989624023],[1368.80810546875,589.398986816406,23.3004989624023],[1356.43908691406,614.398010253906,23.3004989624023],[1357.40502929688,589.398986816406,23.3004989624023],[1360.56201171875,614.398010253906,23.3004989624023],[1348.705078125,614.398010253906,23.3004989624023],[1350.44506835938,589.398986816406,23.3004989624023],[1389.68505859375,606.153015136719,23.3004989624023],[1347.35205078125,589.398986816406,23.3004989624023],[1346.56005859375,589.398986816406,23.3004989624023],[1346.56005859375,594.159912109375,17.0014991760254],[1346.56005859375,589.398986816406,17.0014991760254],[1346.56005859375,605.250427246094,23.3004989624023],[1346.56005859375,614.398010253906,23.3004989624023],[1346.56005859375,614.398010253906,20.8258285522461],[1346.56005859375,614.398010253906,17.0014991760254],[1346.56005859375,564.398986816406,19.10133934021],[1346.56005859375,567.548583984375,23.3004989624023],[1346.56005859375,564.398986816406,17.0020332336426],[1346.56005859375,564.398986816406,23.0018501281738],[1346.56005859375,564.398986816406,23.3004989624023],[1346.56005859375,575.118957519531,17.0014991760254],[1346.56005859375,574.754028320312,23.3004989624023] + ]; + $facets = [ + [0,1,2],[2,3,4],[2,5,0],[4,6,2],[2,6,5],[2,1,3],[7,8,9],[10,9,8],[11,9,10],[12,9,11],[9,13,14],[7,15,16],[10,17,0],[10,0,5],[12,11,6],[18,16,0],[6,19,13],[6,13,9],[9,12,6],[17,18,0],[11,10,5],[11,5,6],[14,16,15],[17,7,18],[16,18,7],[14,15,9],[7,9,15],[7,17,8],[10,8,17],[20,21,22],[23,24,25],[26,23,27],[28,27,23],[29,28,23],[30,29,23],[25,31,32],[22,33,34],[35,36,37],[24,38,39],[21,40,41],[38,42,20],[33,43,44],[6,4,23],[6,23,25],[36,35,1],[1,0,38],[1,38,36],[29,30,4],[25,32,6],[40,42,0],[35,45,1],[4,3,28],[4,28,29],[3,1,45],[3,45,28],[22,34,19],[19,6,32],[19,32,22],[42,38,0],[30,23,4],[0,16,43],[0,43,40],[24,37,36],[38,24,36],[24,23,37],[37,23,26],[22,32,20],[20,32,31],[33,41,40],[43,33,40],[45,35,26],[37,26,35],[33,44,34],[44,43,46],[20,42,21],[40,21,42],[31,39,38],[20,31,38],[33,22,41],[21,41,22],[31,25,39],[24,39,25],[26,27,45],[28,45,27],[47,48,49],[47,50,48],[51,48,50],[52,48,51],[53,48,52],[54,55,56],[57,55,54],[58,55,57],[49,59,47],[60,56,55],[59,56,60],[60,47,59],[48,53,16],[56,13,19],[54,56,19],[56,59,13],[59,49,14],[59,14,13],[49,48,16],[49,16,14],[44,46,60],[44,60,55],[51,50,43],[19,34,58],[19,58,57],[53,52,16],[43,16,52],[43,52,51],[57,54,19],[47,60,46],[55,58,34],[55,34,44],[50,47,46],[50,46,43] + ], } my $model = Slic3r::Model->new; diff --git a/t/perimeters.t b/t/perimeters.t new file mode 100644 index 000000000..9f3457cc2 --- /dev/null +++ b/t/perimeters.t @@ -0,0 +1,67 @@ +use Test::More tests => 2; +use strict; +use warnings; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; +} + +use Slic3r; +use Slic3r::Test; + +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('skirts', 0); + $config->set('fill_density', 0); + $config->set('perimeters', 3); + $config->set('top_solid_layers', 0); + $config->set('bottom_solid_layers', 0); + $config->set('cooling', 0); # to prevent speeds to be altered + $config->set('first_layer_speed', '100%'); # to prevent speeds to be altered + + { + my $print = Slic3r::Test::init_print('overhang', config => $config); + my $has_cw_loops = 0; + my $cur_loop; + Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + my ($self, $cmd, $args, $info) = @_; + + if ($info->{extruding}) { + $cur_loop ||= [ [$self->X, $self->Y] ]; + push @$cur_loop, [ @$info{qw(new_X new_Y)} ]; + } else { + if ($cur_loop) { + $has_cw_loops = 1 if !Slic3r::Geometry::Clipper::is_counter_clockwise($cur_loop); + $cur_loop = undef; + } + } + }); + ok !$has_cw_loops, 'all perimeters extruded ccw'; + } + + { + $config->set('perimeters', 1); + $config->set('perimeter_speed', 77); + $config->set('external_perimeter_speed', 66); + $config->set('bridge_speed', 99); + my $print = Slic3r::Test::init_print('overhang', config => $config); + my %layer_speeds = (); # print Z => [ speeds ] + Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + my ($self, $cmd, $args, $info) = @_; + + if ($info->{extruding} && $info->{dist_XY} > 0) { + $layer_speeds{$self->Z} ||= {}; + $layer_speeds{$self->Z}{my $feedrate = $args->{F} // $self->F} = 1; + fail 'wrong speed found' + if $feedrate != $config->perimeter_speed*60 + && $feedrate != $config->external_perimeter_speed*60 + && $feedrate != $config->bridge_speed*60; + } + }); + is scalar(grep { keys %$_ > 1 } values %layer_speeds), 1, + 'only overhang layer has more than one speed'; + } +} + +__END__