From 6d6533831ea63a256389a55afcca522a9db66c4b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 20 Oct 2011 18:11:59 +0200 Subject: New experimental --gcode-arcs options to generate G2/G3 commands. #23 --- lib/Slic3r.pm | 4 +- lib/Slic3r/Extruder.pm | 70 ++++++++++++++++----- lib/Slic3r/ExtrusionPath.pm | 112 ++++++++++++++++++++++++++++++++- lib/Slic3r/ExtrusionPath/Arc.pm | 23 +++++++ lib/Slic3r/ExtrusionPath/Collection.pm | 5 ++ lib/Slic3r/Geometry.pm | 4 +- lib/Slic3r/Line.pm | 30 +++++++++ lib/Slic3r/Point.pm | 2 +- 8 files changed, 230 insertions(+), 20 deletions(-) create mode 100644 lib/Slic3r/ExtrusionPath/Arc.pm (limited to 'lib') diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 03deb859e..e5b2126a7 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -13,6 +13,7 @@ use Slic3r::ExPolygon; use Slic3r::Extruder; use Slic3r::ExtrusionLoop; use Slic3r::ExtrusionPath; +use Slic3r::ExtrusionPath::Arc; use Slic3r::ExtrusionPath::Collection; use Slic3r::Fill; use Slic3r::Geometry; @@ -34,7 +35,8 @@ use Slic3r::Surface::Bridge; our $nozzle_diameter = 0.5; our $print_center = [100,100]; # object will be centered around this point our $use_relative_e_distances = 0; -our $z_offset = 0; +our $z_offset = 0; +our $gcode_arcs = 0; # filament options our $filament_diameter = 3; # mm diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index a03880f04..c58eab823 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -30,6 +30,7 @@ has 'retract_speed' => ( default => sub { $Slic3r::retract_speed * 60 }, # mm/min ); +use Slic3r::Geometry qw(points_coincide); use XXX; use constant PI => 4 * atan2(1, 1); @@ -67,7 +68,13 @@ sub extrude_loop { sub extrude { my $self = shift; - my ($path, $description) = @_; + my ($path, $description, $recursive) = @_; + + if ($Slic3r::gcode_arcs && !$recursive) { + my $gcode = ""; + $gcode .= $self->extrude($_, $description, 1) for $path->detect_arcs; + return $gcode; + } my $gcode = ""; @@ -80,23 +87,28 @@ sub extrude { } # go to first point of extrusion path - $gcode .= $self->G1($path->points->[0], undef, 0, "move to first $description point"); + $gcode .= $self->G1($path->points->[0], undef, 0, "move to first $description point") + if !points_coincide($self->last_pos, $path->points->[0]); # compensate retraction $gcode .= $self->unretract if $self->retracted; - XXX "yes!\n" if $path->depth_layers > 1; - # extrude while going to next points - foreach my $line ($path->lines) { - # calculate how much filament to drive into the extruder - # to get the desired amount of extruded plastic - my $e = $line->a->distance_to($line->b) * $Slic3r::resolution - * (($Slic3r::nozzle_diameter**2) / ($Slic3r::filament_diameter ** 2)) - * $Slic3r::thickness_ratio - * $self->flow_ratio - * $Slic3r::filament_packing_density - * $path->depth_layers; - - $gcode .= $self->G1($line->b, undef, $e, $description); + + # calculate extrusion length per distance unit + my $e = $Slic3r::resolution + * (($Slic3r::nozzle_diameter**2) / ($Slic3r::filament_diameter ** 2)) + * $Slic3r::thickness_ratio + * $self->flow_ratio + * $Slic3r::filament_packing_density + * $path->depth_layers; + + # extrude arc or line + if ($path->isa('Slic3r::ExtrusionPath::Arc')) { + $gcode .= $self->G2_G3($path->points->[-1], $path->orientation, + $path->center, $e * $path->length, $description); + } else { + foreach my $line ($path->lines) { + $gcode .= $self->G1($line->b, undef, $e * $line->length, $description); + } } return $gcode; @@ -145,6 +157,34 @@ sub G1 { $gcode .= sprintf " Z%.${dec}f", $z; } + return $self->_Gx($gcode, $e, $comment); +} + +sub G2_G3 { + my $self = shift; + my ($point, $orientation, $center, $e, $comment) = @_; + my $dec = $self->dec; + + my $gcode = $orientation eq 'cw' ? "G2" : "G3"; + + $gcode .= sprintf " X%.${dec}f Y%.${dec}f", + ($point->x * $Slic3r::resolution) + $self->shift_x, + ($point->y * $Slic3r::resolution) + $self->shift_y; #** + $self->last_pos($point); + + # XY distance of the center from the start position + $gcode .= sprintf " I%.${dec}f J%.${dec}f", + ($point->[X] - $self->last_pos->[X]) * $Slic3r::resolution + $self->shift_x, + ($point->[Y] - $self->last_pos->[Y]) * $Slic3r::resolution + $self->shift_y; + + return $self->_Gx($gcode, $e, $comment); +} + +sub _Gx { + my $self = shift; + my ($gcode, $e, $comment) = @_; + my $dec = $self->dec; + # apply the speed reduction for print moves on bottom layer my $speed_multiplier = $e && $self->z == $Slic3r::z_offset ? $Slic3r::bottom_layer_speed_ratio diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index dfc5a83f2..67c32807d 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -7,7 +7,11 @@ extends 'Slic3r::Polyline'; # expressed in layers has 'depth_layers' => (is => 'ro', default => sub {1}); -use constant PI => 4 * atan2(1, 1); +use constant X => 0; +use constant Y => 1; + +use Slic3r::Geometry qw(PI epsilon deg2rad rotate_points); +use XXX; sub clip_end { my $self = shift; @@ -70,4 +74,110 @@ sub split_at_acute_angles { return @paths; } +sub detect_arcs { + my $self = shift; + + my $max_angle = deg2rad(40); + my $len_epsilon = 1000000; + + my @points = @{$self->points}; + my @paths = (); + + # we require at least 3 consecutive segments to form an arc + CYCLE: while (@points >= 4) { + for (my $i = 0; $i <= $#points - 3; $i++) { + my $s1 = Slic3r::Line->new($points[$i], $points[$i+1]); + my $s2 = Slic3r::Line->new($points[$i+1], $points[$i+2]); + my $s3 = Slic3r::Line->new($points[$i+2], $points[$i+3]); + my $s1_len = $s1->length; + my $s2_len = $s2->length; + my $s3_len = $s3->length; + + # segments must have the same length + if (abs($s3_len - $s2_len) > $len_epsilon) { + # optimization: skip a cycle + $i++; + next; + } + next if abs($s2_len - $s1_len) > $len_epsilon; + + # segments must have the same relative angle + my $s1_angle = $s1->atan; + my $s2_angle = $s2->atan; + my $s3_angle = $s3->atan; + $s1_angle += 2*PI if $s1_angle < 0; + $s2_angle += 2*PI if $s2_angle < 0; + $s3_angle += 2*PI if $s3_angle < 0; + my $s1s2_angle = $s2_angle - $s1_angle; + my $s2s3_angle = $s3_angle - $s2_angle; + next if abs($s1s2_angle - $s2s3_angle) > $Slic3r::Geometry::parallel_degrees_limit; + next if $s1s2_angle < $Slic3r::Geometry::parallel_degrees_limit; # ignore parallel lines + next if $s1s2_angle > $max_angle; # ignore too sharp vertices + + # s1, s2, s3 form an arc + my $orientation = $s1->point_on_left($points[$i+2]) ? 'ccw' : 'cw'; + + # to find the center, we intersect the perpendicular lines + # passing by midpoints of $s1 and $s3 + my $arc_center; + { + my $s1_mid = $s1->midpoint; + my $s3_mid = $s2->midpoint; + my $rotation_angle = PI/2 * ($orientation eq 'ccw' ? -1 : 1); + my $ray1 = Slic3r::Line->new($s1_mid, rotate_points($rotation_angle, $s1_mid, $points[$i+1])); + my $ray3 = Slic3r::Line->new($s3_mid, rotate_points($rotation_angle, $s3_mid, $points[$i+3])); + $arc_center = $ray1->intersection($ray3, 0); + } + + my $arc = Slic3r::ExtrusionPath::Arc->new( + points => [$points[$i], $points[$i+3]], # first and last points + orientation => $orientation, + center => $arc_center, + radius => $arc_center->distance_to($points[$i]), + ); + + # now look for more points + my $last_line_angle = $s3_angle; + my $last_j = $points[$i+3]; + for (my $j = $i+3; $j < $#points; $j++) { + my $line = Slic3r::Line->new($points[$j], $points[$j+1]); + last if abs($line->length - $s1_len) > $len_epsilon; + my $line_angle = $line->atan; + $line_angle += 2*PI if $line_angle < 0; + my $anglediff = $line_angle - $last_line_angle; + last if abs($s1s2_angle - $anglediff) > $Slic3r::Geometry::parallel_degrees_limit; + + # point $j+1 belongs to the arc + $arc->points->[-1] = $points[$j+1]; + $last_j = $j+1; + + $last_line_angle = $line_angle; + } + + # points 0..$i form a linear path + push @paths, (ref $self)->new( + points => [ @points[0..$i] ], + depth_layers => $self->depth_layers, + ) if $i > 0; + + # add our arc + push @paths, $arc; + print "ARC DETECTED\n"; + # remove arc points from path, leaving one + splice @points, 0, $last_j, (); + + next CYCLE; + } + last; + } + + # remaining points form a linear path + push @paths, (ref $self)->new( + points => [@points], + depth_layers => $self->depth_layers + ) if @points; + + return @paths; +} + 1; diff --git a/lib/Slic3r/ExtrusionPath/Arc.pm b/lib/Slic3r/ExtrusionPath/Arc.pm new file mode 100644 index 000000000..59c65c75c --- /dev/null +++ b/lib/Slic3r/ExtrusionPath/Arc.pm @@ -0,0 +1,23 @@ +package Slic3r::ExtrusionPath::Arc; +use Moo; + +extends 'Slic3r::ExtrusionPath'; + +has 'center' => (is => 'ro', required => 1); +has 'radius' => (is => 'ro', required => 1); +has 'orientation' => (is => 'ro', required => 1); # cw/ccw + +use Slic3r::Geometry qw(PI angle3points); + +sub angle { + my $self = shift; + return angle3points($self->center, @{$self->points}); +} + +sub length { + my $self = shift; + + return $self->radius * $self->angle; +} + +1; diff --git a/lib/Slic3r/ExtrusionPath/Collection.pm b/lib/Slic3r/ExtrusionPath/Collection.pm index 1601bcdbc..4a4358617 100644 --- a/lib/Slic3r/ExtrusionPath/Collection.pm +++ b/lib/Slic3r/ExtrusionPath/Collection.pm @@ -55,4 +55,9 @@ sub cleanup { @{$self->paths} = map $_->split_at_acute_angles, @{$self->paths}; } +sub detect_arcs { + my $self = shift; + @{$self->paths} = map $_->detect_arcs, @{$self->paths}; +} + 1; diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index ac33a2197..fa7180c72 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -429,7 +429,7 @@ sub _line_intersection { # Take this test away and the line segments are # turned into lines going from infinite to another. # bounding_box_intersect() defined later in this chapter. - return "out of bounding box" unless bounding_box_intersect( 2, @box_a, @box_b ); + ###return "out of bounding box" unless bounding_box_intersect( 2, @box_a, @box_b ); } elsif ( @_ == 4 ) { # The parametric form. $x0 = $x2 = 0; @@ -509,7 +509,7 @@ sub _line_intersection { my $h10 = $dx10 ? ($x - $x0) / $dx10 : ($dy10 ? ($y - $y0) / $dy10 : 1); my $h32 = $dx32 ? ($x - $x2) / $dx32 : ($dy32 ? ($y - $y2) / $dy32 : 1); - return [[$x, $y], $h10 >= 0 && $h10 <= 1 && $h32 >= 0 && $h32 <= 1]; + return [Slic3r::Point->new($x, $y), $h10 >= 0 && $h10 <= 1 && $h32 >= 0 && $h32 <= 1]; } # 2D diff --git a/lib/Slic3r/Line.pm b/lib/Slic3r/Line.pm index 9f0dc21d0..3f218f80c 100644 --- a/lib/Slic3r/Line.pm +++ b/lib/Slic3r/Line.pm @@ -2,6 +2,11 @@ package Slic3r::Line; use strict; use warnings; +use constant A => 0; +use constant B => 1; +use constant X => 0; +use constant Y => 1; + sub new { my $class = shift; my $self; @@ -70,4 +75,29 @@ sub length { return Slic3r::Geometry::line_length($self); } +sub atan { + my $self = shift; + return Slic3r::Geometry::line_atan($self); +} + +sub intersection { + my $self = shift; + my ($line, $require_crossing) = @_; + return Slic3r::Geometry::line_intersection($self, $line, $require_crossing); +} + +sub point_on_left { + my $self = shift; + my ($point) = @_; + return Slic3r::Geometry::point_is_on_left_of_segment($point, $self); +} + +sub midpoint { + my $self = shift; + return Slic3r::Point->new( + ($self->[A][X] + $self->[B][X]) / 2, + ($self->[A][Y] + $self->[B][Y]) / 2, + ); +} + 1; diff --git a/lib/Slic3r/Point.pm b/lib/Slic3r/Point.pm index 5ddb4f495..e8570ef8a 100644 --- a/lib/Slic3r/Point.pm +++ b/lib/Slic3r/Point.pm @@ -4,7 +4,7 @@ use warnings; sub new { my $class = shift; - my $self; + my $self;use XXX; ZZZ if !defined $_[0]; if (@_ == 2) { $self = [@_]; } elsif (ref $_[0] eq 'ARRAY') { -- cgit v1.2.3