diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index dee041772..42fe15a63 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -70,6 +70,9 @@ sub extrude { my $self = shift; my ($path, $description, $recursive) = @_; + $path->merge_continuous_lines; + + # detect arcs if ($Slic3r::gcode_arcs && !$recursive) { my $gcode = ""; $gcode .= $self->extrude($_, $description, 1) for $path->detect_arcs; @@ -173,8 +176,8 @@ sub G2_G3 { # 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; + ($center->[X] - $self->last_pos->[X]) * $Slic3r::resolution, + ($center->[Y] - $self->last_pos->[Y]) * $Slic3r::resolution; $self->last_pos($point); return $self->_Gx($gcode, $e, $comment); diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index 67c32807d..3c33f6501 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -76,9 +76,10 @@ sub split_at_acute_angles { sub detect_arcs { my $self = shift; + my ($max_angle, $len_epsilon) = @_; - my $max_angle = deg2rad(40); - my $len_epsilon = 1000000; + $max_angle = deg2rad($max_angle || 15); + $len_epsilon ||= 10 / $Slic3r::resolution; my @points = @{$self->points}; my @paths = (); @@ -111,34 +112,13 @@ sub detect_arcs { 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 abs($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]), - ); + my @arc_points = ($points[$i], $points[$i+3]), # first and last points # now look for more points my $last_line_angle = $s3_angle; - my $last_j = $points[$i+3]; + my $last_j = $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; @@ -148,12 +128,38 @@ sub detect_arcs { last if abs($s1s2_angle - $anglediff) > $Slic3r::Geometry::parallel_degrees_limit; # point $j+1 belongs to the arc - $arc->points->[-1] = $points[$j+1]; + $arc_points[-1] = $points[$j+1]; $last_j = $j+1; $last_line_angle = $line_angle; } + # 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 last segment + # a better method would be to draw all the perpendicular lines + # and find the centroid of the enclosed polygon, or to + # intersect multiple lines and find the centroid of the convex hull + # around the intersections + my $arc_center; + { + my $s1_mid = $s1->midpoint; + my $last_mid = Slic3r::Line->new($points[$last_j-1], $points[$last_j])->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 $last_ray = Slic3r::Line->new($last_mid, rotate_points($rotation_angle, $last_mid, $points[$last_j])); + $arc_center = $ray1->intersection($last_ray, 0); + } + + my $arc = Slic3r::ExtrusionPath::Arc->new( + points => [@arc_points], + orientation => $orientation, + center => $arc_center, + radius => $arc_center->distance_to($points[$i]), + ); + # points 0..$i form a linear path push @paths, (ref $self)->new( points => [ @points[0..$i] ], @@ -162,7 +168,8 @@ sub detect_arcs { # add our arc push @paths, $arc; - print "ARC DETECTED\n"; + Slic3r::debugf "ARC DETECTED\n"; + # remove arc points from path, leaving one splice @points, 0, $last_j, (); @@ -175,7 +182,7 @@ sub detect_arcs { push @paths, (ref $self)->new( points => [@points], depth_layers => $self->depth_layers - ) if @points; + ) if @points > 1; return @paths; } diff --git a/lib/Slic3r/ExtrusionPath/Collection.pm b/lib/Slic3r/ExtrusionPath/Collection.pm index 4a4358617..53950c252 100644 --- a/lib/Slic3r/ExtrusionPath/Collection.pm +++ b/lib/Slic3r/ExtrusionPath/Collection.pm @@ -57,7 +57,7 @@ sub cleanup { sub detect_arcs { my $self = shift; - @{$self->paths} = map $_->detect_arcs, @{$self->paths}; + @{$self->paths} = map $_->detect_arcs(@_), @{$self->paths}; } 1; diff --git a/t/arcs.t b/t/arcs.t index 2d604d578..e901823ea 100644 --- a/t/arcs.t +++ b/t/arcs.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 2; +plan tests => 6; BEGIN { use FindBin; @@ -11,15 +11,46 @@ BEGIN { use Slic3r; -my $path = Slic3r::ExtrusionPath->cast([ - [135322.42,26654.96], [187029.11,99546.23], [222515.14,92381.93], [258001.16,99546.23], - [286979.42,119083.91], [306517.1,148062.17], [313681.4,183548.2], - [306517.1,219034.23], [286979.42,248012.49], [258001.16,267550.17], [222515.14,274714.47], - [187029.11,267550.17], [158050.85,248012.49], [138513.17,219034.23], [131348.87,183548.2], - [86948.77,175149.09], [119825.35,100585], -]); -my $collection = Slic3r::ExtrusionPath::Collection->new(paths => [$path]); -$collection->detect_arcs; +{ + my $path = Slic3r::ExtrusionPath->cast([ + [135322.42,26654.96], [187029.11,99546.23], [222515.14,92381.93], [258001.16,99546.23], + [286979.42,119083.91], [306517.1,148062.17], [313681.4,183548.2], + [306517.1,219034.23], [286979.42,248012.49], [258001.16,267550.17], [222515.14,274714.47], + [187029.11,267550.17], [158050.85,248012.49], [138513.17,219034.23], [131348.87,183548.2], + [86948.77,175149.09], [119825.35,100585], + ]); + + use Slic3r::SVG; + local $Slic3r::resolution = 0.0001; + Slic3r::SVG::output(undef, "arc.svg", + polylines => [ $path->points ], + ); + + my $collection = Slic3r::ExtrusionPath::Collection->new(paths => [$path]); + $collection->detect_arcs(30); + + is scalar(@{$collection->paths}), 3, 'path collection now contains three paths'; + isa_ok $collection->paths->[1], 'Slic3r::ExtrusionPath::Arc', 'second one'; +} -is scalar(@{$collection->paths}), 3, 'path collection now contains three paths'; -isa_ok $collection->paths->[1], 'Slic3r::ExtrusionPath::Arc', 'second one'; +#========================================================== + +{ + my $path = Slic3r::ExtrusionPath->cast([ + [10,20], [10.7845909572784,19.9691733373313], [11.5643446504023,19.8768834059514], + [12.3344536385591,19.7236992039768], [13.0901699437495,19.5105651629515], + [13.8268343236509,19.2387953251129], [14.5399049973955,18.9100652418837], + [15.2249856471595,18.5264016435409], [15.8778525229247,18.0901699437495], + [16.4944804833018,17.6040596560003], + ]); + my $collection = Slic3r::ExtrusionPath::Collection->new(paths => [$path]); + $collection->detect_arcs(10, 1); + + is scalar(@{$collection->paths}), 1, 'path collection now contains one path'; + isa_ok $collection->paths->[0], 'Slic3r::ExtrusionPath::Arc', 'path'; + is $collection->paths->[0]->orientation, 'cw', 'orientation was correctly detected'; + my $center = [ map sprintf('%.0f', $_), @{ $collection->paths->[0]->center } ]; + is_deeply $center, [10,10], 'center was correctly detected'; +} + +#==========================================================