mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Move arc fitting code to its own post-processing filter and remove the built-in ExtrusionPath::Arc class
This commit is contained in:
		
							parent
							
								
									055273fbc8
								
							
						
					
					
						commit
						5f17fa342b
					
				
					 9 changed files with 149 additions and 196 deletions
				
			
		|  | @ -42,7 +42,6 @@ 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::Flow; | ||||
|  | @ -50,6 +49,7 @@ use Slic3r::Format::AMF; | |||
| use Slic3r::Format::OBJ; | ||||
| use Slic3r::Format::STL; | ||||
| use Slic3r::GCode; | ||||
| use Slic3r::GCode::ArcFitting; | ||||
| use Slic3r::GCode::CoolingBuffer; | ||||
| use Slic3r::GCode::Layer; | ||||
| use Slic3r::GCode::MotionPlanner; | ||||
|  |  | |||
|  | @ -126,116 +126,4 @@ sub split_at_acute_angles { | |||
|     return @paths; | ||||
| } | ||||
| 
 | ||||
| sub detect_arcs { | ||||
|     my $self = shift; | ||||
|     my ($max_angle, $len_epsilon) = @_; | ||||
|      | ||||
|     $max_angle = deg2rad($max_angle || 15); | ||||
|     $len_epsilon ||= 10 / &Slic3r::SCALING_FACTOR; | ||||
|     my $parallel_degrees_limit = abs(Slic3r::Geometry::deg2rad(3)); | ||||
|      | ||||
|     my @points = @{$self->points}; | ||||
|     my @paths = (); | ||||
|      | ||||
|     # we require at least 3 consecutive segments to form an arc | ||||
|     CYCLE: while (@points >= 4) { | ||||
|         POINT: 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) > $parallel_degrees_limit; | ||||
|             next if abs($s1s2_angle) < $parallel_degrees_limit;     # ignore parallel lines | ||||
|             next if $s1s2_angle > $max_angle;  # ignore too sharp vertices | ||||
|             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 = $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) > $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; | ||||
|             } | ||||
|              | ||||
|             # 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) // next POINT; | ||||
|             } | ||||
|              | ||||
|             my $arc = Slic3r::ExtrusionPath::Arc->new( | ||||
|                 polyline    => Slic3r::Polyline->new(@arc_points), | ||||
|                 role        => $self->role, | ||||
|                 flow_spacing => $self->flow_spacing, | ||||
|                 orientation => $orientation, | ||||
|                 center      => $arc_center, | ||||
|                 radius      => $arc_center->distance_to($points[$i]), | ||||
|             ); | ||||
|              | ||||
|             # points 0..$i form a linear path | ||||
|             push @paths, $self->clone(polyline => Slic3r::Polyline->new(@points[0..$i])) | ||||
|                 if $i > 0; | ||||
|              | ||||
|             # add our arc | ||||
|             push @paths, $arc; | ||||
|             Slic3r::debugf "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, $self->clone(polyline => Slic3r::Polyline->new(@points)) | ||||
|         if @points > 1; | ||||
|      | ||||
|     return @paths; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  |  | |||
|  | @ -1,34 +0,0 @@ | |||
| package Slic3r::ExtrusionPath::Arc; | ||||
| use Moo; | ||||
| 
 | ||||
| has 'polyline'      => (is => 'rw', required => 1); | ||||
| has 'role'          => (is => 'rw', required => 1); | ||||
| has 'height'        => (is => 'rw'); | ||||
| has 'flow_spacing'  => (is => 'rw'); | ||||
| 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 points { | ||||
|     my $self = shift; | ||||
|     return $self->polyline; | ||||
| } | ||||
| 
 | ||||
| sub angle { | ||||
|     my $self = shift; | ||||
|     return angle3points($self->center, @{$self->points}); | ||||
| } | ||||
| 
 | ||||
| sub length { | ||||
|     my $self = shift; | ||||
|      | ||||
|     if($self->orientation eq 'ccw') { | ||||
|         return $self->radius * $self->angle; | ||||
|     } else { | ||||
|         return $self->radius * (2*PI() - $self->angle); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  | @ -47,10 +47,4 @@ sub cleanup { | |||
|     $self->append(@paths); | ||||
| } | ||||
| 
 | ||||
| sub detect_arcs { | ||||
|     my $self = shift; | ||||
|      | ||||
|     return map $_->detect_arcs(@_), @$self; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  |  | |||
|  | @ -273,15 +273,6 @@ sub extrude_path { | |||
|      | ||||
|     $path->simplify(&Slic3r::SCALED_RESOLUTION); | ||||
|      | ||||
|     # detect arcs | ||||
|     if ($self->config->gcode_arcs && !$params{dont_detect_arcs}) { | ||||
|         my $gcode = ""; | ||||
|         foreach my $arc_path ($path->detect_arcs) { | ||||
|             $gcode .= $self->extrude_path($arc_path, $description, %params, dont_detect_arcs => 1); | ||||
|         } | ||||
|         return $gcode; | ||||
|     } | ||||
|      | ||||
|     # go to first point of extrusion path | ||||
|     my $gcode = ""; | ||||
|     my $first_point = $path->first_point; | ||||
|  | @ -330,39 +321,7 @@ sub extrude_path { | |||
|     # extrude arc or line | ||||
|     $gcode .= ";_BRIDGE_FAN_START\n" if $path->is_bridge; | ||||
|     my $path_length = 0; | ||||
|     if ($path->isa('Slic3r::ExtrusionPath::Arc')) { | ||||
|         $path_length = unscale $path->length; | ||||
|          | ||||
|         # calculate extrusion length for this arc | ||||
|         my $E = 0; | ||||
|         if ($e) { | ||||
|             $E = $e * $path_length; | ||||
|             $self->extruder->e(0) if $self->config->use_relative_e_distances; | ||||
|             $self->total_extrusion_length($self->total_extrusion_length + $E); | ||||
|             $E = $self->extruder->e($self->extruder->e + $E); | ||||
|         } | ||||
|          | ||||
|         # compose G-code line | ||||
|         my $point = $path->points->[-1]; | ||||
|         $gcode .= $path->orientation eq 'cw' ? "G2" : "G3"; | ||||
|         $gcode .= sprintf " X%.3f Y%.3f",  | ||||
|             ($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]; #** | ||||
|          | ||||
|         # XY distance of the center from the start position | ||||
|         $gcode .= sprintf " I%.3f J%.3f", | ||||
|             ($path->center->[X] - $self->last_pos->[X]) * &Slic3r::SCALING_FACTOR, | ||||
|             ($path->center->[Y] - $self->last_pos->[Y]) * &Slic3r::SCALING_FACTOR; | ||||
|          | ||||
|         $gcode .= sprintf(" %s%.5f", $self->config->extrusion_axis, $E) | ||||
|             if $E; | ||||
|         $gcode .= " F$F"; | ||||
|         $gcode .= " ; $description" | ||||
|             if $self->config->gcode_comments; | ||||
|         $gcode .= "\n"; | ||||
|          | ||||
|         $self->wipe_path(undef); | ||||
|     } else { | ||||
|     { | ||||
|         my $local_F = $F; | ||||
|         foreach my $line ($path->lines) { | ||||
|             $path_length += my $line_length = unscale $line->length; | ||||
|  |  | |||
							
								
								
									
										133
									
								
								lib/Slic3r/GCode/ArcFitting.pm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								lib/Slic3r/GCode/ArcFitting.pm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,133 @@ | |||
| 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 = Slic3r::Geometry::point_is_on_left_of_segment($cur_path->[2], [ @$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->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; | ||||
|  | @ -10,6 +10,7 @@ has 'shift'                         => (is => 'ro', required => 1); | |||
| 
 | ||||
| has 'spiralvase'                    => (is => 'lazy'); | ||||
| has 'vibration_limit'               => (is => 'lazy'); | ||||
| has 'arc_fitting'                   => (is => 'lazy'); | ||||
| has 'skirt_done'                    => (is => 'rw', default => sub { {} });  # print_z => 1 | ||||
| has 'brim_done'                     => (is => 'rw'); | ||||
| has 'second_layer_things_done'      => (is => 'rw'); | ||||
|  | @ -31,6 +32,14 @@ sub _build_vibration_limit { | |||
|         : undef; | ||||
| } | ||||
| 
 | ||||
| sub _build_arc_fitting { | ||||
|     my $self = shift; | ||||
|      | ||||
|     return $Slic3r::Config->gcode_arcs | ||||
|         ? Slic3r::GCode::ArcFitting->new(config => $self->gcodegen->config) | ||||
|         : undef; | ||||
| } | ||||
| 
 | ||||
| sub process_layer { | ||||
|     my $self = shift; | ||||
|     my ($layer, $object_copies) = @_; | ||||
|  | @ -175,6 +184,10 @@ sub process_layer { | |||
|     $gcode = $self->vibration_limit->process($gcode) | ||||
|         if $Slic3r::Config->vibration_limit != 0; | ||||
|      | ||||
|     # apply arc fitting if enabled | ||||
|     $gcode = $self->arc_fitting->process($gcode) | ||||
|         if $Slic3r::Config->gcode_arcs; | ||||
|      | ||||
|     return $gcode; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci