mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 12:11:15 -06:00 
			
		
		
		
	Manual rebase of the avoid_crossing_perimeters feature
This commit is contained in:
		
							parent
							
								
									d278998f11
								
							
						
					
					
						commit
						0eadc5adba
					
				
					 12 changed files with 362 additions and 11 deletions
				
			
		|  | @ -462,6 +462,13 @@ our $Options = { | |||
|         type    => 'bool', | ||||
|         default => 1, | ||||
|     }, | ||||
|     'avoid_crossing_perimeters' => { | ||||
|         label   => 'Avoid crossing perimeters', | ||||
|         tooltip => 'Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down the processing times.', | ||||
|         cli     => 'avoid-crossing-perimeters!', | ||||
|         type    => 'bool', | ||||
|         default => 0, | ||||
|     }, | ||||
|     'support_material' => { | ||||
|         label   => 'Generate support material', | ||||
|         tooltip => 'Enable support material generation.', | ||||
|  |  | |||
|  | @ -155,16 +155,19 @@ sub clip_line { | |||
| sub simplify { | ||||
|     my $self = shift; | ||||
|     $_->simplify(@_) for @$self; | ||||
|     $self; | ||||
| } | ||||
| 
 | ||||
| sub translate { | ||||
|     my $self = shift; | ||||
|     $_->translate(@_) for @$self; | ||||
|     $self; | ||||
| } | ||||
| 
 | ||||
| sub rotate { | ||||
|     my $self = shift; | ||||
|     $_->rotate(@_) for @$self; | ||||
|     $self; | ||||
| } | ||||
| 
 | ||||
| sub area { | ||||
|  |  | |||
|  | @ -2,7 +2,8 @@ package Slic3r::GCode; | |||
| use Moo; | ||||
| 
 | ||||
| use Slic3r::ExtrusionPath ':roles'; | ||||
| use Slic3r::Geometry qw(scale unscale); | ||||
| use Slic3r::Geometry qw(PI X Y scale unscale points_coincide); | ||||
| use Slic3r::Geometry::Clipper qw(union_ex); | ||||
| 
 | ||||
| has 'layer'              => (is => 'rw'); | ||||
| has 'shift_x'            => (is => 'rw', default => sub {0} ); | ||||
|  | @ -10,6 +11,8 @@ has 'shift_y'            => (is => 'rw', default => sub {0} ); | |||
| has 'z'                  => (is => 'rw', default => sub {0} ); | ||||
| has 'speed'              => (is => 'rw'); | ||||
| 
 | ||||
| has 'motionplanner'      => (is => 'rw'); | ||||
| has 'straight_once'      => (is => 'rw'); | ||||
| has 'extruder_idx'       => (is => 'rw'); | ||||
| has 'extrusion_distance' => (is => 'rw', default => sub {0} ); | ||||
| has 'elapsed_time'       => (is => 'rw', default => sub {0} );  # seconds | ||||
|  | @ -48,18 +51,35 @@ my %role_speeds = ( | |||
|     &EXTR_ROLE_SUPPORTMATERIAL              => 'perimeter', | ||||
| ); | ||||
| 
 | ||||
| use Slic3r::Geometry qw(points_coincide PI X Y); | ||||
| 
 | ||||
| sub extruder { | ||||
|     my $self = shift; | ||||
|     return $Slic3r::extruders->[$self->extruder_idx]; | ||||
| } | ||||
| 
 | ||||
| sub set_shift { | ||||
|     my $self = shift; | ||||
|     my @shift = @_; | ||||
|      | ||||
|     # adjust last position | ||||
|     $self->last_pos($self->last_pos->clone->translate( | ||||
|         scale($self->shift_x - $shift[X]), | ||||
|         scale($self->shift_y - $shift[Y]), | ||||
|     )); | ||||
|      | ||||
|     $self->shift_x($shift[X]); | ||||
|     $self->shift_y($shift[Y]); | ||||
| } | ||||
| 
 | ||||
| sub change_layer { | ||||
|     my $self = shift; | ||||
|     my ($layer) = @_; | ||||
|      | ||||
|     $self->layer($layer); | ||||
|     if ($Slic3r::Config->avoid_crossing_perimeters) { | ||||
|         $self->motionplanner(Slic3r::GCode::MotionPlanner->new( | ||||
|             islands => union_ex([ map @{$_->expolygon}, @{$layer->slices} ], undef, 1), | ||||
|         )); | ||||
|     } | ||||
|     my $z = $Slic3r::Config->z_offset + $layer->print_z * &Slic3r::SCALING_FACTOR; | ||||
|      | ||||
|     my $gcode = ""; | ||||
|  | @ -145,8 +165,7 @@ sub extrude_path { | |||
|     } | ||||
|      | ||||
|     # go to first point of extrusion path | ||||
|     $gcode .= $self->G0($path->points->[0], undef, 0, "move to first $description point") | ||||
|         if !points_coincide($self->last_pos, $path->points->[0]); | ||||
|     $gcode .= $self->travel_to($path->points->[0], "move to first $description point"); | ||||
|      | ||||
|     # compensate retraction | ||||
|     $gcode .= $self->unretract if $self->extruder->retracted; | ||||
|  | @ -192,6 +211,20 @@ sub extrude_path { | |||
|     return $gcode; | ||||
| } | ||||
| 
 | ||||
| sub travel_to { | ||||
|     my $self = shift; | ||||
|     my ($point, $comment) = @_; | ||||
|      | ||||
|     return "" if points_coincide($self->last_pos, $point); | ||||
|     if ($Slic3r::Config->avoid_crossing_perimeters && $self->last_pos->distance_to($point) > scale 5 && !$self->straight_once) { | ||||
|         return join '', map $self->G0($_->b, undef, 0, $comment || ""), | ||||
|             $self->motionplanner->shortest_path($self->last_pos, $point)->lines; | ||||
|     } else { | ||||
|         $self->straight_once(0); | ||||
|         return $self->G0($point, undef, 0, $comment || ""); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub retract { | ||||
|     my $self = shift; | ||||
|     my %params = @_; | ||||
|  |  | |||
							
								
								
									
										259
									
								
								lib/Slic3r/GCode/MotionPlanner.pm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								lib/Slic3r/GCode/MotionPlanner.pm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,259 @@ | |||
| package Slic3r::GCode::MotionPlanner; | ||||
| use Moo; | ||||
| 
 | ||||
| has 'islands'       => (is => 'ro', required => 1); | ||||
| has 'no_internal'   => (is => 'ro'); | ||||
| has 'last_crossings'=> (is => 'rw'); | ||||
| has '_inner'        => (is => 'rw', default => sub { [] });  # arrayref of arrayrefs of expolygons | ||||
| has '_outer'        => (is => 'rw', default => sub { [] });  # arrayref of arrayrefs of polygons | ||||
| has '_contours_ex'  => (is => 'rw', default => sub { [] });  # arrayref of arrayrefs of expolygons | ||||
| has '_pointmap'     => (is => 'rw', default => sub { {} });  # { id => $point } | ||||
| has '_edges'        => (is => 'rw', default => sub { {} });  # node_idx => { node_idx => distance, ... } | ||||
| has '_crossing_edges' => (is => 'rw', default => sub { {} });  # edge_idx => bool | ||||
| 
 | ||||
| use List::Util qw(first); | ||||
| use Slic3r::Geometry qw(scale epsilon nearest_point); | ||||
| use Slic3r::Geometry::Clipper qw(diff_ex JT_MITER); | ||||
| 
 | ||||
| # clearance (in mm) from the perimeters | ||||
| has '_inner_margin' => (is => 'ro', default => sub { scale 0.5 }); | ||||
| has '_outer_margin' => (is => 'ro', default => sub { scale 2 }); | ||||
| 
 | ||||
| # this factor weigths the crossing of a perimeter  | ||||
| # vs. the alternative path. a value of 5 means that | ||||
| # a perimeter will be crossed if the alternative path | ||||
| # is >= 5x the length of the straight line we could | ||||
| # follow if we decided to cross the perimeter. | ||||
| # a nearly-infinite value for this will only permit | ||||
| # perimeter crossing when there's no alternative path. | ||||
| use constant CROSSING_FACTOR => 20; | ||||
| 
 | ||||
| use constant INFINITY => 'inf'; | ||||
| 
 | ||||
| # setup our configuration space | ||||
| sub BUILD { | ||||
|     my $self = shift; | ||||
|      | ||||
|     my $edges = $self->_edges; | ||||
|     my $crossing_edges = $self->_crossing_edges; | ||||
|     my $tolerance = scale epsilon; | ||||
|      | ||||
|     my $add_expolygon = sub { | ||||
|         my ($expolygon, $crosses_perimeter) = @_; | ||||
|         my @points = map @$_, @$expolygon; | ||||
|         for my $i (0 .. $#points) { | ||||
|             for my $j (($i+1) .. $#points) { | ||||
|                 my $line = Slic3r::Line->new($points[$i], $points[$j]); | ||||
|                 if ($expolygon->encloses_line($line, scale Slic3r::Geometry::epsilon)) { | ||||
|                     my $dist = $line->length * ($crosses_perimeter ? CROSSING_FACTOR : 1); | ||||
|                     $edges->{$points[$i]}{$points[$j]} = $dist; | ||||
|                     $edges->{$points[$j]}{$points[$i]} = $dist; | ||||
|                     $crossing_edges->{$points[$i]}{$points[$j]} = 1; | ||||
|                     $crossing_edges->{$points[$j]}{$points[$i]} = 1; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     for my $i (0 .. $#{$self->islands}) { | ||||
|         $self->islands->[$i]->simplify($self->_inner_margin); | ||||
|         $self->_inner->[$i] = [ $self->islands->[$i]->offset_ex(-$self->_inner_margin) ] | ||||
|             if !$self->no_internal; | ||||
|         $self->_outer->[$i] = [ $self->islands->[$i]->contour->offset($self->_outer_margin) ]; | ||||
|         $_->simplify($self->_inner_margin) for @{$self->_inner->[$i]}, @{$self->_outer->[$i]}; | ||||
|          | ||||
|         if (!$self->no_internal) { | ||||
|             $self->_contours_ex->[$i] = diff_ex( | ||||
|                 $self->_outer->[$i], | ||||
|                 [ map $_->contour, @{$self->_inner->[$i]} ], | ||||
|             ); | ||||
|              | ||||
|             # lines enclosed in inner expolygons are visible | ||||
|             $add_expolygon->($_) for @{ $self->_inner->[$i] }; | ||||
|              | ||||
|             # lines enclosed in expolygons covering perimeters are visible | ||||
|             # (but discouraged) | ||||
|             $add_expolygon->($_, 1) for @{ $self->_contours_ex->[$i] }; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     my $intersects = sub { | ||||
|         my ($polygon, $line) = @_; | ||||
|         @{Boost::Geometry::Utils::polygon_linestring_intersection( | ||||
|             $polygon->boost_polygon, | ||||
|             Boost::Geometry::Utils::linestring($line), | ||||
|         )} > 0; | ||||
|     }; | ||||
|      | ||||
|     # lines connecting outer polygons are visible | ||||
|     { | ||||
|         my @outer = (map @$_, @{$self->_outer}); | ||||
|         for my $i (0 .. $#outer) { | ||||
|             for my $j (($i+1) .. $#outer) { | ||||
|                 for my $m (0 .. $#{$outer[$i]}) { | ||||
|                     for my $n (0 .. $#{$outer[$j]}) { | ||||
|                         my $line = Slic3r::Line->new($outer[$i][$m], $outer[$j][$n]); | ||||
|                         if (!first { $intersects->($_, $line) } @outer) { | ||||
|                             # this line does not cross any polygon | ||||
|                             my $dist = $line->length; | ||||
|                             $edges->{$outer[$i][$m]}{$outer[$j][$n]} = $dist; | ||||
|                             $edges->{$outer[$j][$n]}{$outer[$i][$m]} = $dist; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     # lines connecting inner polygons contours are visible but discouraged | ||||
|     if (!$self->no_internal) { | ||||
|         my @inner = (map $_->contour, map @$_, @{$self->_inner}); | ||||
|         for my $i (0 .. $#inner) { | ||||
|             for my $j (($i+1) .. $#inner) { | ||||
|                 for my $m (0 .. $#{$inner[$i]}) { | ||||
|                     for my $n (0 .. $#{$inner[$j]}) { | ||||
|                         my $line = Slic3r::Line->new($inner[$i][$m], $inner[$j][$n]); | ||||
|                         if (!first { $intersects->($_, $line) } @inner) { | ||||
|                             # this line does not cross any polygon | ||||
|                             my $dist = $line->length * CROSSING_FACTOR; | ||||
|                             $edges->{$inner[$i][$m]}{$inner[$j][$n]} = $dist; | ||||
|                             $edges->{$inner[$j][$n]}{$inner[$i][$m]} = $dist; | ||||
|                             $crossing_edges->{$inner[$i][$m]}{$inner[$j][$n]} = 1; | ||||
|                             $crossing_edges->{$inner[$j][$n]}{$inner[$i][$m]} = 1; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     $self->_pointmap({ | ||||
|         map +("$_" => $_),  | ||||
|             (map @$_, map @$_, map @$_, @{$self->_inner}), | ||||
|             (map @$_, map @$_, @{$self->_outer}), | ||||
|             (map @$_, map @$_, map @$_, @{$self->_contours_ex}), | ||||
|     }); | ||||
|      | ||||
|     if (0) { | ||||
|         my @lines = (); | ||||
|         my %lines = (); | ||||
|         for my $i (keys %{$self->_edges}) { | ||||
|             for my $j (keys %{$self->_edges->{$i}}) { | ||||
|                 next if $lines{join '_', sort $i, $j}; | ||||
|                 push @lines, [ map $self->_pointmap->{$_}, $i, $j ]; | ||||
|                 $lines{join '_', sort $i, $j} = 1; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         require "Slic3r/SVG.pm"; | ||||
|         Slic3r::SVG::output(undef, "space.svg", | ||||
|             lines           => \@lines, | ||||
|             no_arrows       => 1, | ||||
|             polygons        => [ map @$_, @{$self->islands} ], | ||||
|             red_polygons    => [ map $_->holes, map @$_, @{$self->_inner} ], | ||||
|             white_polygons    => [ map @$_, @{$self->_outer} ], | ||||
|         ); | ||||
|         printf "%d islands\n", scalar @{$self->islands}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub find_node { | ||||
|     my $self = shift; | ||||
|     my ($point, $near_to) = @_; | ||||
|      | ||||
|     # for optimal pathing, we should check visibility from $point to all $candidates, and then | ||||
|     # choose the one that is nearest to $near_to among the visible ones; however this is probably too slow | ||||
|      | ||||
|     # if we're inside a hole, move to a point on hole; | ||||
|     { | ||||
|         my $polygon = first { $_->encloses_point($point) } (map $_->holes, map @$_, @{$self->_inner}); | ||||
|         return nearest_point($point, $polygon) if $polygon; | ||||
|     } | ||||
|      | ||||
|     # if we're inside an expolygon move to a point on contour or holes | ||||
|     { | ||||
|         my $expolygon = first { $_->encloses_point_quick($point) } (map @$_, @{$self->_inner}); | ||||
|         return nearest_point($point, [ map @$_, @$expolygon ]) if $expolygon; | ||||
|     } | ||||
|      | ||||
|     { | ||||
|         my $outer_polygon_idx; | ||||
|         if (!$self->no_internal) { | ||||
|             # look for an outer expolygon whose contour contains our point | ||||
|             $outer_polygon_idx = first { first { $_->contour->encloses_point($point) } @{$self->_contours_ex->[$_]} } | ||||
|                 0 .. $#{ $self->_contours_ex }; | ||||
|         } else { | ||||
|             # # look for an outer expolygon containing our point | ||||
|             $outer_polygon_idx = first { first { $_->encloses_point($point) } @{$self->_outer->[$_]} } | ||||
|                 0 .. $#{ $self->_outer }; | ||||
|         } | ||||
|         my $candidates = defined $outer_polygon_idx | ||||
|             ? [ map @{$_->contour}, @{$self->_inner->[$outer_polygon_idx]} ] | ||||
|             : [ map @$_, map @$_, @{$self->_outer} ]; | ||||
|         $candidates = [ map @$_, @{$self->_outer->[$outer_polygon_idx]} ] | ||||
|             if @$candidates == 0; | ||||
|         return nearest_point($point, $candidates); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub shortest_path { | ||||
|     my $self = shift; | ||||
|     my ($from, $to) = @_; | ||||
|      | ||||
|     # find nearest nodes | ||||
|     my $new_from = $self->find_node($from, $to); | ||||
|     my $new_to = $self->find_node($to, $from); | ||||
|      | ||||
|     my $root = "$new_from"; | ||||
|     my $target = "$new_to"; | ||||
|     my $edges = $self->_edges; | ||||
|     my %dist = map { $_ => INFINITY } keys %$edges; | ||||
|     $dist{$root} = 0; | ||||
|     my %prev = map { $_ => undef } keys %$edges; | ||||
|     my @unsolved = keys %$edges; | ||||
|     my %crossings = ();  # node_idx => bool | ||||
|      | ||||
|     while (@unsolved) { | ||||
|         # sort unsolved by distance from root | ||||
|         # using a sorting option that accounts for infinity | ||||
|         @unsolved = sort { | ||||
|             $dist{$a} eq INFINITY ? +1 : | ||||
|             $dist{$b} eq INFINITY ? -1 : | ||||
|                 $dist{$a} <=> $dist{$b}; | ||||
|         } @unsolved; | ||||
|          | ||||
|         # we'll solve the closest node | ||||
|         last if $dist{$unsolved[0]} eq INFINITY; | ||||
|         my $n = shift @unsolved; | ||||
|          | ||||
|         # stop search | ||||
|         last if $n eq $target; | ||||
|          | ||||
|         # now, look at all the nodes connected to n | ||||
|         foreach my $n2 (keys %{$edges->{$n}}) { | ||||
|             # .. and find out if any of their estimated distances | ||||
| 	        # can be improved if we go through n | ||||
| 	        if ( ($dist{$n2} eq INFINITY) || ($dist{$n2} > ($dist{$n} + $edges->{$n}{$n2})) ) { | ||||
| 	            $dist{$n2} = $dist{$n} + $edges->{$n}{$n2}; | ||||
| 	            $prev{$n2} = $n; | ||||
| 	            $crossings{$n} = 1 if $self->_crossing_edges->{$n}{$n2}; | ||||
| 	        } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     my @points = (); | ||||
|     my $crossings = 0; | ||||
|     { | ||||
|         my $pointmap = $self->_pointmap; | ||||
|         my $u = $target; | ||||
|         while (defined $prev{$u}) { | ||||
|             unshift @points, $pointmap->{$u}; | ||||
|             $crossings++ if $crossings{$u}; | ||||
|             $u = $prev{$u}; | ||||
|         } | ||||
|     } | ||||
|     $self->last_crossings($crossings); | ||||
|     return Slic3r::Polyline->new($from, $new_from, @points, $to); # @points already includes $new_to | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  | @ -392,6 +392,10 @@ sub build { | |||
|             title => 'Horizontal shells', | ||||
|             options => [qw(solid_layers)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Advanced', | ||||
|             options => [qw(avoid_crossing_perimeters)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->add_options_page('Infill', 'shading.png', optgroups => [ | ||||
|  |  | |||
|  | @ -58,12 +58,14 @@ sub rotate { | |||
|     my $self = shift; | ||||
|     my ($angle, $center) = @_; | ||||
|     @$self = @{ +(Slic3r::Geometry::rotate_points($angle, $center, $self))[0] }; | ||||
|     $self; | ||||
| } | ||||
| 
 | ||||
| sub translate { | ||||
|     my $self = shift; | ||||
|     my ($x, $y) = @_; | ||||
|     @$self = @{ +(Slic3r::Geometry::move_points([$x, $y], $self))[0] }; | ||||
|     $self; | ||||
| } | ||||
| 
 | ||||
| sub x { $_[0]->[0] } | ||||
|  |  | |||
|  | @ -14,6 +14,11 @@ sub lines { | |||
|     return polygon_lines($self); | ||||
| } | ||||
| 
 | ||||
| sub boost_polygon { | ||||
|     my $self = shift; | ||||
|     return Boost::Geometry::Utils::polygon($self); | ||||
| } | ||||
| 
 | ||||
| sub boost_linestring { | ||||
|     my $self = shift; | ||||
|     return Boost::Geometry::Utils::linestring([@$self, $self->[0]]); | ||||
|  |  | |||
|  | @ -613,9 +613,30 @@ sub write_gcode { | |||
|         $Slic3r::Config->print_center->[Y] - (unscale ($print_bb[Y2] - $print_bb[Y1]) / 2) - unscale $print_bb[Y1], | ||||
|     ); | ||||
|      | ||||
|     # initialize a motion planner for object-to-object travel moves | ||||
|     my $external_motionplanner; | ||||
|     if ($Slic3r::Config->avoid_crossing_perimeters) { | ||||
|         my $distance_from_objects = 1; | ||||
|         # compute the offsetted convex hull for each object and repeat it for each copy. | ||||
|         my @islands = (); | ||||
|         foreach my $obj_idx (0 .. $#{$self->objects}) { | ||||
|             my @island = Slic3r::ExPolygon->new(convex_hull([ | ||||
|                 map @{$_->expolygon->contour}, map @{$_->slices}, @{$self->objects->[$obj_idx]->layers}, | ||||
|             ]))->translate(map -$_, @shift)->offset_ex(scale $distance_from_objects); | ||||
|             foreach my $copy (@{$self->copies->[$obj_idx]}) { | ||||
|                 push @islands, map $_->clone->translate(@$copy), @island; | ||||
|             } | ||||
|         } | ||||
|         $external_motionplanner = Slic3r::GCode::MotionPlanner->new( | ||||
|             islands     => union_ex([ map @$_, @islands ]), | ||||
|             no_internal => 1, | ||||
|         ); | ||||
|     } | ||||
|      | ||||
|     # prepare the logic to print one layer | ||||
|     my $skirt_done = 0;  # count of skirt layers done | ||||
|     my $brim_done = 0; | ||||
|     my $last_obj_copy = ""; | ||||
|     my $extrude_layer = sub { | ||||
|         my ($layer_id, $object_copies) = @_; | ||||
|         my $gcode = ""; | ||||
|  | @ -635,8 +656,7 @@ sub write_gcode { | |||
|          | ||||
|         # extrude skirt | ||||
|         if ($skirt_done < $Slic3r::Config->skirt_height) { | ||||
|             $gcodegen->shift_x($shift[X]); | ||||
|             $gcodegen->shift_y($shift[Y]); | ||||
|             $gcodegen->set_shift(@shift); | ||||
|             $gcode .= $gcodegen->set_acceleration($Slic3r::Config->perimeter_acceleration); | ||||
|             # skip skirt if we have a large brim | ||||
|             if ($layer_id < $Slic3r::Config->skirt_height && ($layer_id != 0 || $Slic3r::Config->skirt_distance + (($Slic3r::Config->skirts - 1) * $Slic3r::flow->spacing) > $Slic3r::Config->brim_width)) { | ||||
|  | @ -647,8 +667,7 @@ sub write_gcode { | |||
|          | ||||
|         # extrude brim | ||||
|         if ($layer_id == 0 && !$brim_done) { | ||||
|             $gcodegen->shift_x($shift[X]); | ||||
|             $gcodegen->shift_y($shift[Y]); | ||||
|             $gcodegen->set_shift(@shift); | ||||
|             $gcode .= $gcodegen->extrude_loop($_, 'brim') for @{$self->brim}; | ||||
|             $brim_done = 1; | ||||
|         } | ||||
|  | @ -661,8 +680,21 @@ sub write_gcode { | |||
|             # won't always trigger the automatic retraction | ||||
|             $gcode .= $gcodegen->retract; | ||||
|              | ||||
|             $gcodegen->shift_x($shift[X] + unscale $copy->[X]); | ||||
|             $gcodegen->shift_y($shift[Y] + unscale $copy->[Y]); | ||||
|             # travel to the first perimeter point using the external motion planner | ||||
|             if ($external_motionplanner && @{ $layer->perimeters } && !$gcodegen->straight_once && $last_obj_copy ne "${obj_idx}_${copy}") { | ||||
|                 $gcodegen->set_shift(@shift); | ||||
|                 my $layer_mp = $gcodegen->motionplanner; | ||||
|                 $gcodegen->motionplanner($external_motionplanner); | ||||
|                 my $first_perimeter = $layer->perimeters->[0]->unpack; | ||||
|                 my $target = $first_perimeter->polygon->[0]->clone->translate(@$copy); | ||||
|                 $gcode .= $gcodegen->travel_to($target, "move to first perimeter point"); | ||||
|                 $gcodegen->motionplanner($layer_mp); | ||||
|             } | ||||
|              | ||||
|             $gcodegen->set_shift( | ||||
|                 $shift[X] + unscale $copy->[X], | ||||
|                 $shift[Y] + unscale $copy->[Y], | ||||
|             ); | ||||
|              | ||||
|             # extrude perimeters | ||||
|             $gcode .= $gcodegen->set_tool($Slic3r::Config->perimeter_extruder-1); | ||||
|  | @ -686,6 +718,8 @@ sub write_gcode { | |||
|                 $gcode .= $gcodegen->extrude_path($_, 'support material')  | ||||
|                     for $layer->support_fills->shortest_path($gcodegen->last_pos); | ||||
|             } | ||||
|              | ||||
|             $last_obj_copy = "${obj_idx}_${copy}"; | ||||
|         } | ||||
|         return if !$gcode; | ||||
|          | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci