From 73c05a60921961248f3da547d3bef69921d8600b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 28 Aug 2013 16:51:58 +0200 Subject: [PATCH] Moved vibration limit to its own G-code filter --- MANIFEST | 1 + lib/Slic3r.pm | 1 + lib/Slic3r/GCode.pm | 42 +------------------- lib/Slic3r/GCode/Layer.pm | 13 ++++++ lib/Slic3r/GCode/Reader.pm | 11 +++-- lib/Slic3r/GCode/SpiralVase.pm | 4 +- lib/Slic3r/GCode/VibrationLimit.pm | 64 ++++++++++++++++++++++++++++++ lib/Slic3r/Line.pm | 5 --- t/custom_gcode.t | 2 +- t/fill.t | 2 +- t/layers.t | 2 +- t/perimeters.t | 8 ++-- t/retraction.t | 2 +- t/shells.t | 8 ++-- t/skirt_brim.t | 2 +- t/support.t | 2 +- t/vibrationlimit.t | 2 +- utils/gcode_sectioncut.pl | 2 +- 18 files changed, 103 insertions(+), 70 deletions(-) create mode 100644 lib/Slic3r/GCode/VibrationLimit.pm diff --git a/MANIFEST b/MANIFEST index 62df31da1..7923aee40 100644 --- a/MANIFEST +++ b/MANIFEST @@ -29,6 +29,7 @@ lib/Slic3r/GCode/Layer.pm lib/Slic3r/GCode/MotionPlanner.pm lib/Slic3r/GCode/Reader.pm lib/Slic3r/GCode/SpiralVase.pm +lib/Slic3r/GCode/VibrationLimit.pm lib/Slic3r/Geometry.pm lib/Slic3r/Geometry/BoundingBox.pm lib/Slic3r/Geometry/Clipper.pm diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 7c54b198c..87fb47e7c 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -55,6 +55,7 @@ use Slic3r::GCode::Layer; use Slic3r::GCode::MotionPlanner; use Slic3r::GCode::Reader; use Slic3r::GCode::SpiralVase; +use Slic3r::GCode::VibrationLimit; use Slic3r::Geometry qw(PI); use Slic3r::Geometry::BoundingBox; use Slic3r::Geometry::Clipper; diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index ac93938cb..2fc410064 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -1,7 +1,7 @@ package Slic3r::GCode; use Moo; -use List::Util qw(min max first); +use List::Util qw(min first); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(epsilon scale unscale scaled_epsilon points_coincide PI X Y B); use Slic3r::Geometry::Clipper qw(union_ex); @@ -38,10 +38,6 @@ has 'last_fan_speed' => (is => 'rw', default => sub {0}); has 'wipe_path' => (is => 'rw'); has 'dec' => (is => 'ro', default => sub { 3 } ); -# used for vibration limit: -has 'last_dir' => (is => 'ro', default => sub { [0,0] }); -has 'dir_time' => (is => 'ro', default => sub { [0,0] }); - sub _build_speeds { my $self = shift; return { @@ -584,7 +580,6 @@ sub _G0_G1 { $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]; #** - $gcode = $self->_limit_frequency($point) . $gcode if $self->config->vibration_limit != 0; $self->last_pos($point->clone); } if (defined $z && (!defined $self->z || $z != $self->z)) { @@ -755,39 +750,4 @@ sub set_bed_temperature { return $gcode; } -# http://hydraraptor.blogspot.it/2010/12/frequency-limit.html -sub _limit_frequency { - my ($self, $point) = @_; - - my $min_time = 1 / ($self->config->vibration_limit * 60); # in minutes - - # calculate the move vector and move direction - my $vector = Slic3r::Line->new($self->last_pos, $point)->vector; - my @dir = map { $vector->[B][$_] <=> 0 } X,Y; - - my $time = (unscale $vector->length) / $self->speeds->{$self->speed}; # in minutes - if ($time > 0) { - my @pause = (); - foreach my $axis (X,Y) { - if ($dir[$axis] != 0 && $self->last_dir->[$axis] != $dir[$axis]) { - if ($self->last_dir->[$axis] != 0) { - # this axis is changing direction: check whether we need to pause - if ($self->dir_time->[$axis] < $min_time) { - push @pause, ($min_time - $self->dir_time->[$axis]); - } - } - $self->last_dir->[$axis] = $dir[$axis]; - $self->dir_time->[$axis] = 0; - } - $self->dir_time->[$axis] += $time; - } - - if (@pause) { - return sprintf "G4 P%d\n", max(@pause) * 60 * 1000; - } - } - - return ''; -} - 1; diff --git a/lib/Slic3r/GCode/Layer.pm b/lib/Slic3r/GCode/Layer.pm index 8569ab1a2..78bf5bb01 100644 --- a/lib/Slic3r/GCode/Layer.pm +++ b/lib/Slic3r/GCode/Layer.pm @@ -9,6 +9,7 @@ has 'gcodegen' => (is => 'ro', required => 1); has 'shift' => (is => 'ro', required => 1); has 'spiralvase' => (is => 'lazy'); +has 'vibration_limit' => (is => 'lazy'); has 'skirt_done' => (is => 'rw', default => sub { {} }); # print_z => 1 has 'brim_done' => (is => 'rw'); has 'second_layer_things_done' => (is => 'rw'); @@ -22,6 +23,14 @@ sub _build_spiralvase { : undef; } +sub _build_vibration_limit { + my $self = shift; + + return $Slic3r::Config->vibration_limit + ? Slic3r::GCode::VibrationLimit->new(config => $self->gcodegen->config) + : undef; +} + sub process_layer { my $self = shift; my ($layer, $object_copies) = @_; @@ -162,6 +171,10 @@ sub process_layer { $gcode = $self->spiralvase->process_layer($gcode, $layer) if $spiralvase; + # apply vibration limit if enabled + $gcode = $self->vibration_limit->process($gcode) + if $Slic3r::Config->vibration_limit != 0; + return $gcode; } diff --git a/lib/Slic3r/GCode/Reader.pm b/lib/Slic3r/GCode/Reader.pm index ed3bdb73b..cfcca8548 100644 --- a/lib/Slic3r/GCode/Reader.pm +++ b/lib/Slic3r/GCode/Reader.pm @@ -1,7 +1,6 @@ package Slic3r::GCode::Reader; use Moo; -has 'gcode' => (is => 'ro', required => 1); has 'X' => (is => 'rw', default => sub {0}); has 'Y' => (is => 'rw', default => sub {0}); has 'Z' => (is => 'rw', default => sub {0}); @@ -13,9 +12,9 @@ my @AXES = qw(X Y Z E); sub parse { my $self = shift; - my ($cb) = @_; + my ($gcode, $cb) = @_; - foreach my $raw_line (split /\R+/, $self->gcode) { + foreach my $raw_line (split /\R+/, $gcode) { print "$raw_line\n" if $Verbose || $ENV{SLIC3R_TESTS_GCODE}; my $line = $raw_line; $line =~ s/\s*;(.*)//; # strip comment @@ -50,12 +49,12 @@ sub parse { } # run callback - $cb->($self, $command, \%args, \%info); + #$cb->($self, $command, \%args, \%info); # update coordinates if ($command =~ /^(?:G[01]|G92)$/) { - for (@AXES, 'F') { - $self->$_($args{$_}) if exists $args{$_}; + for my $axis (@AXES, 'F') { + $self->$axis($args{$axis}) if exists $args{$axis}; } } diff --git a/lib/Slic3r/GCode/SpiralVase.pm b/lib/Slic3r/GCode/SpiralVase.pm index 1094e728e..e9f36ba70 100644 --- a/lib/Slic3r/GCode/SpiralVase.pm +++ b/lib/Slic3r/GCode/SpiralVase.pm @@ -10,7 +10,7 @@ sub process_layer { my ($gcode, $layer) = @_; my $total_layer_length = 0; - Slic3r::GCode::Reader->new(gcode => $gcode)->parse(sub { + Slic3r::GCode::Reader->new->parse($gcode, sub { my ($reader, $cmd, $args, $info) = @_; $total_layer_length += $info->{dist_XY} if $cmd eq 'G1' && $info->{extruding}; @@ -20,7 +20,7 @@ sub process_layer { my $layer_height = $layer->height; my $z = $layer->print_z + $self->config->z_offset - $layer_height; my $newlayer = 0; - Slic3r::GCode::Reader->new(gcode => $gcode)->parse(sub { + Slic3r::GCode::Reader->new->parse($gcode, sub { my ($reader, $cmd, $args, $info) = @_; if ($cmd eq 'G1' && exists $args->{Z}) { diff --git a/lib/Slic3r/GCode/VibrationLimit.pm b/lib/Slic3r/GCode/VibrationLimit.pm new file mode 100644 index 000000000..0680a3b82 --- /dev/null +++ b/lib/Slic3r/GCode/VibrationLimit.pm @@ -0,0 +1,64 @@ +package Slic3r::GCode::VibrationLimit; +use Moo; + +extends 'Slic3r::GCode::Reader'; + +has 'config' => (is => 'ro', required => 1); +has '_min_time' => (is => 'lazy'); +has '_last_dir' => (is => 'ro', default => sub { [0,0] }); +has '_dir_time' => (is => 'ro', default => sub { [0,0] }); + +# inspired by http://hydraraptor.blogspot.it/2010/12/frequency-limit.html + +use List::Util qw(max); +use Slic3r::Geometry qw(X Y); + +sub _build__min_time { + my ($self) = @_; + return 1 / ($self->config->vibration_limit * 60); # in minutes +} + +sub process { + my $self = shift; + my ($gcode) = @_; + + my $new_gcode = ""; + $self->parse($gcode, sub { + my ($reader, $cmd, $args, $info) = @_; + + if ($cmd eq 'G1' && $info->{dist_XY} > 0) { + my $point = Slic3r::Point->new($args->{X} // $reader->X, $args->{Y} // $reader->Y); + my @dir = ( + ($point->x <=> $reader->X), + ($point->y <=> $reader->Y), #$ + ); + my $time = $info->{dist_XY} / ($args->{F} // $reader->F); # in minutes + if ($time > 0) { + my @pause = (); + foreach my $axis (X,Y) { + if ($dir[$axis] != 0 && $self->_last_dir->[$axis] != $dir[$axis]) { + if ($self->_last_dir->[$axis] != 0) { + # this axis is changing direction: check whether we need to pause + if ($self->_dir_time->[$axis] < $self->_min_time) { + push @pause, ($self->_min_time - $self->_dir_time->[$axis]); + } + } + $self->_last_dir->[$axis] = $dir[$axis]; + $self->_dir_time->[$axis] = 0; + } + $self->_dir_time->[$axis] += $time; + } + + if (@pause) { + $new_gcode .= sprintf "G4 P%d\n", max(@pause) * 60 * 1000; + } + } + } + + $new_gcode .= $info->{raw} . "\n"; + }); + + return $new_gcode; +} + +1; diff --git a/lib/Slic3r/Line.pm b/lib/Slic3r/Line.pm index 7d9fd5105..e79b86350 100644 --- a/lib/Slic3r/Line.pm +++ b/lib/Slic3r/Line.pm @@ -15,11 +15,6 @@ sub coincides_with { || ($self->a->coincides_with($line->b) && $self->b->coincides_with($line->a)); } -sub vector { - my $self = shift; - return (ref $self)->new([0,0], [map $self->[B][$_] - $self->[A][$_], X,Y]); -} - sub atan { my $self = shift; return Slic3r::Geometry::line_atan($self); diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 9c5fbdcc4..765946f36 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -20,7 +20,7 @@ use Slic3r::Test; my $print = Slic3r::Test::init_print('2x20x10', config => $conf); my $last_move_was_z_change = 0; - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; if ($last_move_was_z_change && $cmd ne $config->layer_gcode) { diff --git a/t/fill.t b/t/fill.t index d8d82b495..256d6235b 100644 --- a/t/fill.t +++ b/t/fill.t @@ -174,7 +174,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } my $print = Slic3r::Test::init_print('20mm_cube', config => $config); my %layers_with_extrusion = (); - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; $layers_with_extrusion{$self->Z} = 1 if $info->{extruding}; }); diff --git a/t/layers.t b/t/layers.t index 6ee92cf2e..2da409747 100644 --- a/t/layers.t +++ b/t/layers.t @@ -21,7 +21,7 @@ my $test = sub { my @z = (); my @increments = (); - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; if ($info->{dist_Z}) { diff --git a/t/perimeters.t b/t/perimeters.t index 7fa0f3e34..26d16f140 100644 --- a/t/perimeters.t +++ b/t/perimeters.t @@ -25,7 +25,7 @@ use Slic3r::Test; 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 { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; if ($info->{extruding} && $info->{dist_XY} > 0) { @@ -47,7 +47,7 @@ use Slic3r::Test; my $has_cw_loops = my $has_outwards_move = 0; my $cur_loop; my %external_loops = (); # print_z => count of external loops - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; if ($info->{extruding} && $info->{dist_XY} > 0) { @@ -77,7 +77,7 @@ use Slic3r::Test; my $print = Slic3r::Test::init_print('L', config => $config); my $loop_starts_from_convex_point = 0; my $cur_loop; - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; if ($info->{extruding} && $info->{dist_XY} > 0) { @@ -107,7 +107,7 @@ use Slic3r::Test; my $print = Slic3r::Test::init_print('overhang', config => $config); my %layer_speeds = (); # print Z => [ speeds ] my $fan_speed = 0; - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; $fan_speed = 0 if $cmd eq 'M107'; diff --git a/t/retraction.t b/t/retraction.t index 08276ad6e..8221c4deb 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -25,7 +25,7 @@ my $test = sub { my $lifted = 0; my $changed_tool = 0; my $wait_for_toolchange = 0; - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; if ($cmd =~ /^T(\d+)/) { diff --git a/t/shells.t b/t/shells.t index 7120c0e7c..4ef1f9f12 100644 --- a/t/shells.t +++ b/t/shells.t @@ -27,7 +27,7 @@ use Slic3r::Test; my $print = Slic3r::Test::init_print('20mm_cube', config => $config); my %layers_with_shells = (); # Z => $count - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; if ($self->Z > 0) { @@ -74,7 +74,7 @@ use Slic3r::Test; my $print = Slic3r::Test::init_print('V', config => $config); my %layers_with_solid_infill = (); # Z => 1 - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; $layers_with_solid_infill{$self->Z} = 1 @@ -106,7 +106,7 @@ use Slic3r::Test; my $print = Slic3r::Test::init_print('V', config => $config); my %layers = (); # Z => 1 - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; $layers{$self->Z} = 1 if $info->{extruding} && ($args->{F} // $self->F) == $config->solid_infill_speed*60; @@ -132,7 +132,7 @@ use Slic3r::Test; my $travel_moves_after_first_extrusion = 0; my $started_extruding = 0; my @z_steps = (); - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; $started_extruding = 1 if $info->{extruding}; diff --git a/t/skirt_brim.t b/t/skirt_brim.t index c78bf41e0..e73ccc620 100644 --- a/t/skirt_brim.t +++ b/t/skirt_brim.t @@ -27,7 +27,7 @@ use Slic3r::Test; my $print = Slic3r::Test::init_print(['20mm_cube','20mm_cube'], config => $config); my %layers_with_skirt = (); # Z => $count - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; if (defined $self->Z) { diff --git a/t/support.t b/t/support.t index 9ed16395a..4cda5bff4 100644 --- a/t/support.t +++ b/t/support.t @@ -65,7 +65,7 @@ use Slic3r::Test; ok my $gcode = Slic3r::Test::gcode($print), 'no conflict between raft/support and brim'; my $tool = 0; - Slic3r::GCode::Reader->new(gcode => $gcode)->parse(sub { + Slic3r::GCode::Reader->new->parse($gcode, sub { my ($self, $cmd, $args, $info) = @_; if ($cmd =~ /^T(\d+)/) { diff --git a/t/vibrationlimit.t b/t/vibrationlimit.t index a903b6b82..ff39073e9 100644 --- a/t/vibrationlimit.t +++ b/t/vibrationlimit.t @@ -28,7 +28,7 @@ my $test = sub { my %dir_time = (X => 0, Y => 0); my %dir_sleep_time = (X => 0, Y => 0); my $last_cmd_pause = 0; - Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; if ($cmd !~ /^G[01]$/) { diff --git a/utils/gcode_sectioncut.pl b/utils/gcode_sectioncut.pl index 4304f4aba..733208a3e 100644 --- a/utils/gcode_sectioncut.pl +++ b/utils/gcode_sectioncut.pl @@ -41,7 +41,7 @@ my %opt = ( # read paths my %paths = (); # z => [ path, path ... ] - Slic3r::GCode::Reader->new(gcode => io($input_file)->all)->parse(sub { + Slic3r::GCode::Reader->new->parse(io($input_file)->all, sub { my ($self, $cmd, $args, $info) = @_; if ($cmd eq 'G1' && $info->{extruding}) {