diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 42bcd2a32..466dfbf4f 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -19,10 +19,16 @@ has 'total_extrusion_length' => (is => 'rw', default => sub {0} ); has 'lifted' => (is => 'rw', default => sub {0} ); has 'last_pos' => (is => 'rw', default => sub { Slic3r::Point->new(0,0) } ); has 'last_speed' => (is => 'rw', default => sub {""}); +has 'last_f' => (is => 'rw', default => sub {""}); +has 'force_f' => (is => 'rw', default => sub {0}); has 'last_fan_speed' => (is => 'rw', default => sub {0}); has 'last_path' => (is => 'rw'); has 'dec' => (is => 'ro', default => sub { 3 } ); +# used for vibration limit: +has 'last_dir' => (is => 'ro', default => sub { [0,0] }); +has 'segment_time' => (is => 'ro', default => sub { [ [0,0,0], [0,0,0] ] }); + # calculate speeds (mm/min) has 'speeds' => ( is => 'ro', @@ -69,6 +75,7 @@ sub move_z { my $current_z = $self->z; if (!defined $current_z || $current_z != ($z + $self->lifted)) { $gcode .= $self->retract(move_z => $z); + $self->speed('travel'); $gcode .= $self->G0(undef, $z, 0, $comment || ('move to next layer (' . $self->layer->id . ')')) unless ($current_z // -1) != ($self->z // -1); } @@ -148,6 +155,7 @@ sub extrude_path { my $point = Slic3r::Geometry::point_along_segment(@$last_line, $last_line->length + scale $path->flow_spacing); bless $point, 'Slic3r::Point'; $point->rotate(PI/6, $last_line->[B]); + $self->speed('travel'); $gcode .= $self->G0($point, undef, 0, "move inwards before travel"); } } @@ -157,6 +165,7 @@ sub extrude_path { } # go to first point of extrusion path + $self->speed('travel'); $gcode .= $self->G0($path->points->[0], undef, 0, "move to first $description point") if !points_coincide($self->last_pos, $path->points->[0]); @@ -267,6 +276,7 @@ sub unretract { my $gcode = ""; if ($self->lifted) { + $self->speed('travel'); $gcode .= $self->G0(undef, $self->z - $self->lifted, 0, 'restore layer Z'); $self->lifted(0); } @@ -311,10 +321,12 @@ sub _G0_G1 { my ($gcode, $point, $z, $e, $comment) = @_; my $dec = $self->dec; + my $speed_factor; if ($point) { $gcode .= sprintf " X%.${dec}f Y%.${dec}f", ($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x - $self->extruder->extruder_offset->[X], ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y - $self->extruder->extruder_offset->[Y]; #** + $speed_factor = $self->_limit_frequency($point); $self->last_pos($point->clone); } if (defined $z && (!defined $self->z || $z != $self->z)) { @@ -322,7 +334,7 @@ sub _G0_G1 { $gcode .= sprintf " Z%.${dec}f", $z; } - return $self->_Gx($gcode, $e, $comment); + return $self->_Gx($gcode, $e, $speed_factor, $comment); } sub G2_G3 { @@ -342,39 +354,45 @@ sub G2_G3 { ($center->[Y] - $self->last_pos->[Y]) * &Slic3r::SCALING_FACTOR; $self->last_pos($point); - return $self->_Gx($gcode, $e, $comment); + return $self->_Gx($gcode, $e, undef, $comment); } sub _Gx { my $self = shift; - my ($gcode, $e, $comment) = @_; + my ($gcode, $e, $speed_factor, $comment) = @_; my $dec = $self->dec; - # determine speed - my $speed = ($e ? $self->speed : 'travel'); - # output speed if it's different from last one used # (goal: reduce gcode size) my $append_bridge_off = 0; - if ($speed ne $self->last_speed) { - if ($speed eq 'bridge') { + my $F; + if ($self->speed ne $self->last_speed) { + if ($self->speed eq 'bridge') { $gcode = ";_BRIDGE_FAN_START\n$gcode"; } elsif ($self->last_speed eq 'bridge') { $append_bridge_off = 1; } # apply the speed reduction for print moves on bottom layer - my $speed_f = $speed eq 'retract' + $F = $self->speed eq 'retract' ? ($self->extruder->retract_speed_mm_min) - : $self->speeds->{$speed} // $speed; + : $self->speeds->{$self->speed} // $self->speed; if ($e && $self->layer && $self->layer->id == 0 && $comment !~ /retract/) { - $speed_f = $Slic3r::Config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ - ? ($speed_f * $1/100) + $F = $Slic3r::Config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ + ? ($F * $1/100) : $Slic3r::Config->first_layer_speed * 60; } - $gcode .= sprintf " F%.${dec}f", $speed_f; - $self->last_speed($speed); + $self->last_speed($self->speed); + $self->last_f($F); + $F *= $speed_factor // 1; + } elsif (defined $speed_factor && $speed_factor != 1) { + $gcode .= sprintf " F%.${dec}f", ($self->last_f * $speed_factor); + $self->force_f(1); # next move will need explicit F + } elsif ($self->force_f) { + $gcode .= sprintf " F%.${dec}f", $self->last_f; + $self->force_f(0); } + $gcode .= sprintf " F%.${dec}f", $F if defined $F; # output extrusion distance if ($e && $Slic3r::Config->extrusion_axis) { @@ -469,85 +487,39 @@ sub set_bed_temperature { } # http://hydraraptor.blogspot.it/2010/12/frequency-limit.html -sub limit_frequency { +# the following implementation is inspired by Marlin code +sub _limit_frequency { my $self = shift; - my ($gcode) = @_; - - return $gcode if $Slic3r::Config->vibration_limit == 0; - - my $current_gcode = $gcode; - $gcode = ''; - - # the following code is inspired by Marlin frequency limit implementation + my ($point) = @_; + return if $Slic3r::Config->vibration_limit == 0; my $min_time = 1 / ($Slic3r::Config->vibration_limit * 60); - my @axes = qw(X Y); - my %segment_time = (map { $_ => [0,0,0] } @axes); - my %last = (map { $_ => 0 } @axes); - my %last_dir = (map { $_ => 0 } @axes); - my $F; - foreach my $line (split /\n/, $current_gcode) { - if ($line =~ /^G[01] /) { - my %cur; - my $f; - for (@axes) { - $cur{$_} = $1 if $line =~ /$_([0-9.]+)/; - } - $f = $1 if $line =~ /F([0-9.]+)/; - - # calculate the move vector - my %move = ( - map { $_ => (defined $cur{$_} && defined $last{$_}) ? ($cur{$_} - $last{$_}) : 0 } @axes - ); - - # check move directions - my %dir = ( - map { $_ => ($move{$_}) ? ($move{$_} > 0 ? 1 : -1) : 0 } @axes - ); - - my $factor = 1; - my $segment_time = abs(max(values %move)) / ($f // $F); - if ($segment_time > 0) { - my %max_segment_time = (); - foreach my $axis (@axes) { - # are we changing direction on this axis? - if ($last_dir{$axis} == $dir{$axis}) { - $segment_time{$axis}[0] += $segment_time; - } else { - @{ $segment_time{$axis} } = ($segment_time, @{ $segment_time{$axis} }[0,1]); - } - - $max_segment_time{$axis} = max($segment_time{$axis}[0], max($segment_time{$axis}[1], $segment_time{$axis}[2])); - } - - my $min_segment_time = min(values %max_segment_time); - if ($min_segment_time < $min_time) { - $factor = $min_segment_time / $min_time; - } - } - - if ($factor == 1) { - $gcode .= "$line\n"; + # calculate the move vector and move direction + my @move = map unscale $_, @{ Slic3r::Line->new($self->last_pos, $point)->vector->[B] }; + my @dir = map { $move[$_] ? (($move[$_] > 0) ? 1 : -1) : 0 } X,Y; + + my $factor = 1; + my $segment_time = abs(max(@move)) / $self->speeds->{$self->speed}; + if ($segment_time > 0) { + my @max_segment_time = (); + foreach my $axis (X,Y) { + if ($self->last_dir->[$axis] == $dir[$axis]) { + $self->segment_time->[$axis][0] += $segment_time; } else { - $line =~ s/ F[0-9.]+//; - my $new_speed = sprintf '%.3f', ($f // $F) * $factor; - $line =~ s/^(G[01]) /$1 F$new_speed /; - $gcode .= "$line\nG1 F" . ($f // $F) . "\n"; + @{ $self->segment_time->[$axis] } = ($segment_time, @{ $self->segment_time->[$axis] }[0,1]); } - - for (@axes) { - $last{$_} = $cur{$_} if $cur{$_}; - $last_dir{$_} = $dir{$_} if $dir{$_}; - } - $F = $f if defined $f; - - } else { - $gcode .= "$line\n"; + $max_segment_time[$axis] = max($self->segment_time->[$axis][0], max($self->segment_time->[$axis][1], $self->segment_time->[$axis][2])); + $self->last_dir->[$axis] = $dir[$axis] if $dir[$axis]; + } + + my $min_segment_time = min(@max_segment_time); + if ($min_segment_time < $min_time) { + $factor = $min_segment_time / $min_time; } } - return $gcode; + return $factor; } 1; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 15e02b1c6..84140e9ee 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -851,8 +851,6 @@ sub write_gcode { $gcode =~ s/^;_BRIDGE_FAN_END\n/ $gcodegen->set_fan($fan_speed, 1) /gmex; } - $gcode = $gcodegen->limit_frequency($gcode); - return $gcode; };