mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 12:11:15 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			133 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			133 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| package Slic3r::GCode::ArcFitting;
 | |
| use Moo;
 | |
| 
 | |
| use Slic3r::Geometry qw(X Y PI scale unscale deg2rad);
 | |
| 
 | |
| extends 'Slic3r::GCode::Reader';
 | |
| has 'config'                    => (is => 'ro', required => 1);
 | |
| has 'max_angle'                 => (is => 'rw', default => sub { deg2rad(15) });
 | |
| has 'len_epsilon'               => (is => 'rw', default => sub { scale 10 });
 | |
| has 'parallel_degrees_limit'    => (is => 'rw', default => sub { abs(deg2rad(3)) });
 | |
| 
 | |
| sub process {
 | |
|     my $self = shift;
 | |
|     my ($gcode) = @_;
 | |
|     
 | |
|     my $new_gcode           = "";
 | |
|     my $buffer              = "";
 | |
|     my @cur_path            = ();
 | |
|     my $cur_len             = 0;
 | |
|     my $cur_relative_angle  = 0;
 | |
|     
 | |
|     $self->parse($gcode, sub {
 | |
|         my ($reader, $cmd, $args, $info) = @_;
 | |
|         
 | |
|         if ($info->{extruding} && $info->{dist_XY} > 0) {
 | |
|             my $point = Slic3r::Point->new_scale($args->{X}, $args->{Y});
 | |
|             
 | |
|             if (@cur_path >= 2) {
 | |
|                 if ($cur_path[-1]->distance_to($point) > $self->len_epsilon) {
 | |
|                     # if the last distance is not compatible with the current arc, flush it
 | |
|                     $new_gcode .= $self->flush_path(\@cur_path, \$buffer);
 | |
|                 } elsif (@cur_path >= 3) {
 | |
|                     my $rel_angle = relative_angle(@cur_path[-2,-1], $point);
 | |
|                     if (($cur_relative_angle != 0 && abs($rel_angle - $cur_relative_angle) > $self->parallel_degrees_limit)   # relative angle is too different from the previous one
 | |
|                         || abs($rel_angle) < $self->parallel_degrees_limit                            # relative angle is almost parallel
 | |
|                         || $rel_angle > $self->max_angle) {                                           # relative angle is excessive (too sharp)
 | |
|                         # in these cases, $point does not really look like an additional point of the current arc
 | |
|                         $new_gcode .= $self->flush_path(\@cur_path, \$buffer);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             if (@cur_path == 0) {
 | |
|                 # we're starting a path, so let's prepend the previous position
 | |
|                 push @cur_path, Slic3r::Point->new_scale($self->X, $self->Y), $point;
 | |
|                 $buffer .= $info->{raw} . "\n";
 | |
|                 $cur_len = $cur_path[0]->distance_to($cur_path[1]);
 | |
|             } else {
 | |
|                 push @cur_path, $point;
 | |
|                 $buffer .= $info->{raw} . "\n";
 | |
|                 if (@cur_path == 3) {
 | |
|                     # we have two segments, time to compute a reference angle
 | |
|                     $cur_relative_angle = relative_angle(@cur_path[0,1,2]);
 | |
|                 }
 | |
|             }
 | |
|         } else {
 | |
|             $new_gcode .= $self->flush_path(\@cur_path, \$buffer);
 | |
|             $new_gcode .= $info->{raw} . "\n";
 | |
|         }
 | |
|     });
 | |
|     
 | |
|     $new_gcode .= $self->flush_path(\@cur_path, \$buffer);
 | |
|     return $new_gcode;
 | |
| }
 | |
| 
 | |
| sub flush_path {
 | |
|     my ($self, $cur_path, $buffer) = @_;
 | |
|     
 | |
|     my $gcode = "";
 | |
|     
 | |
|     if (@$cur_path >= 3) {
 | |
|         # if we have enough points, then we have an arc
 | |
|         $$buffer =~ s/^/;/mg;
 | |
|         $gcode = "; these moves were replaced by an arc:\n" . $$buffer;
 | |
|         
 | |
|         my $orientation = $cur_path->[2]->ccw(@$cur_path[0,1]) ? '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      = Slic3r::Line->new(@$cur_path[0,1])->midpoint;
 | |
|             my $last_mid    = Slic3r::Line->new(@$cur_path[-2,-1])->midpoint;
 | |
|             my $rotation_angle = PI/2 * ($orientation eq 'ccw' ? -1 : 1);
 | |
|             my $ray1        = Slic3r::Line->new($s1_mid,   $cur_path->[1]->clone->rotate($rotation_angle, $s1_mid));
 | |
|             my $last_ray    = Slic3r::Line->new($last_mid, $cur_path->[-1]->clone->rotate($rotation_angle, $last_mid));
 | |
|             $arc_center     = $ray1->intersection($last_ray, 0) or next POINT;
 | |
|         }
 | |
|         my $radius = $arc_center->distance_to($cur_path->[0]);
 | |
|         my $total_angle = Slic3r::Geometry::angle3points($arc_center, @$cur_path[0,-1]);
 | |
|         my $length = $orientation eq 'ccw'
 | |
|             ? $radius * $total_angle
 | |
|             : $radius * (2*PI - $total_angle);
 | |
|         
 | |
|         # compose G-code line
 | |
|         $gcode .= $orientation eq 'cw' ? "G2" : "G3";
 | |
|         $gcode .= sprintf " X%.3f Y%.3f", map unscale($_), @{$cur_path->[-1]};  # destination point
 | |
|         
 | |
|         # XY distance of the center from the start position
 | |
|         $gcode .= sprintf " I%.3f J%.3f", map { unscale($arc_center->[$_] - $cur_path->[0][$_]) } (X,Y);
 | |
|         
 | |
|         my $E = 0;  # TODO: compute E using $length
 | |
|         $gcode .= sprintf(" %s%.5f", $self->config->get_extrusion_axis, $E)
 | |
|             if $E;
 | |
|         
 | |
|         my $F = 0;  # TODO: extract F from original moves
 | |
|         $gcode .= " F$F\n";
 | |
|     } else {
 | |
|         $gcode = $$buffer;
 | |
|     }
 | |
|     
 | |
|     $$buffer = "";
 | |
|     splice @$cur_path, 0, $#$cur_path;  # keep last point as starting position for next path
 | |
|     return $gcode;
 | |
| }
 | |
| 
 | |
| sub relative_angle {
 | |
|     my ($p1, $p2, $p3) = @_;
 | |
|     
 | |
|     my $s1 = Slic3r::Line->new($p1, $p2);
 | |
|     my $s2 = Slic3r::Line->new($p2, $p3);
 | |
|     my $s1_angle = $s1->atan;
 | |
|     my $s2_angle = $s2->atan;
 | |
|     $s1_angle += 2*PI if $s1_angle < 0;
 | |
|     $s2_angle += 2*PI if $s2_angle < 0;
 | |
|     return $s2_angle - $s1_angle;
 | |
| }
 | |
| 
 | |
| 1;
 | 
