mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Clean medial axis code and gap fill
This commit is contained in:
		
							parent
							
								
									98ad345e42
								
							
						
					
					
						commit
						9a51964e98
					
				
					 5 changed files with 108 additions and 146 deletions
				
			
		|  | @ -28,10 +28,6 @@ has 'overhang_width'    => (is => 'lazy'); | |||
| # divided by type top/bottom/internal | ||||
| has 'slices' => (is => 'rw', default => sub { Slic3r::Surface::Collection->new }); | ||||
| 
 | ||||
| # collection of polygons or polylines representing thin walls contained  | ||||
| # in the original geometry | ||||
| has 'thin_walls' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); | ||||
| 
 | ||||
| # collection of extrusion paths/loops filling gaps | ||||
| has 'thin_fills' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); | ||||
| 
 | ||||
|  | @ -101,26 +97,6 @@ sub make_surfaces { | |||
|             expolygons          => [ map $_->expolygon, @{$self->slices} ], | ||||
|         ); | ||||
|     } | ||||
|      | ||||
|     # detect thin walls by offsetting slices by half extrusion inwards | ||||
|     if ($self->config->thin_walls) { | ||||
|         $self->thin_walls([]); | ||||
|         # we use spacing here because there could be a case where | ||||
|         # the slice collapses with width but doesn't collapse with spacing, | ||||
|         # thus causing both perimeters and medial axis to be generated | ||||
|         my $width = $self->perimeter_flow->scaled_spacing; | ||||
|         my $diff = diff_ex( | ||||
|             [ map $_->p, @{$self->slices} ], | ||||
|             offset2([ map $_->p, @{$self->slices} ], -$width*0.5, +$width*0.5), | ||||
|             1, | ||||
|         ); | ||||
|          | ||||
|         my $area_threshold = $width ** 2; | ||||
|         if (@$diff = grep { $_->area > $area_threshold } @$diff) { | ||||
|             @{$self->thin_walls} = map $_->medial_axis($width), @$diff; | ||||
|             Slic3r::debugf "  %d thin walls detected\n", scalar(@{$self->thin_walls}); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub _merge_loops { | ||||
|  | @ -177,7 +153,7 @@ sub make_perimeters { | |||
|      | ||||
|     my @contours    = ();    # array of Polygons with ccw orientation | ||||
|     my @holes       = ();    # array of Polygons with cw orientation | ||||
|     my @gaps        = ();    # array of ExPolygons | ||||
|     my @gaps        = ();    # array of Polygons | ||||
|      | ||||
|     # we need to process each island separately because we might have different | ||||
|     # extra perimeters for each one | ||||
|  | @ -205,13 +181,13 @@ sub make_perimeters { | |||
|             # where offset2() collapses the expolygon, then there's no room for an inner loop | ||||
|             # and we can extract the gap for later processing | ||||
|             if ($Slic3r::Config->gap_fill_speed > 0 && $self->object->config->fill_density > 0) { | ||||
|                 my $diff = diff_ex( | ||||
|                 my $diff = diff( | ||||
|                     offset(\@last, -0.5*$spacing), | ||||
|                     # +2 on the offset here makes sure that Clipper float truncation  | ||||
|                     # won't shrink the clip polygon to be smaller than intended. | ||||
|                     offset(\@offsets, +0.5*$spacing + 2), | ||||
|                 ); | ||||
|                 push @gaps, (@this_gaps = grep $_->area >= $gap_area_threshold, @$diff); | ||||
|                 push @gaps, (@this_gaps = grep abs($_->area) >= $gap_area_threshold, @$diff); | ||||
|             } | ||||
|              | ||||
|             last if !@offsets || $i == $loop_number; | ||||
|  | @ -222,7 +198,7 @@ sub make_perimeters { | |||
|          | ||||
|         # make sure we don't infill narrow parts that are already gap-filled | ||||
|         # (we only consider this surface's gaps to reduce the diff() complexity) | ||||
|         @last = @{diff(\@last, [ map @$_, @this_gaps ])}; | ||||
|         @last = @{diff(\@last, \@this_gaps)}; | ||||
|          | ||||
|         # create one more offset to be used as boundary for fill | ||||
|         # we offset by half the perimeter spacing (to get to the actual infill boundary) | ||||
|  | @ -237,8 +213,6 @@ sub make_perimeters { | |||
|         ); | ||||
|     } | ||||
|      | ||||
|     $self->_fill_gaps(\@gaps); | ||||
|      | ||||
|     # find nesting hierarchies separately for contours and holes | ||||
|     my $contours_pt = union_pt(\@contours); | ||||
|     my $holes_pt    = union_pt(\@holes); | ||||
|  | @ -302,14 +276,49 @@ sub make_perimeters { | |||
|     # append perimeters | ||||
|     $self->perimeters->append(@loops); | ||||
|      | ||||
|     # add thin walls as perimeters | ||||
|     push @{ $self->perimeters }, @{Slic3r::ExtrusionPath::Collection->new( | ||||
|         map Slic3r::ExtrusionPath->new( | ||||
|                 polyline        => ($_->isa('Slic3r::Polygon') ? $_->split_at_first_point : $_->clone), | ||||
|                 role            => EXTR_ROLE_EXTERNAL_PERIMETER, | ||||
|                 flow_spacing    => $self->perimeter_flow->spacing, | ||||
|         ), @{ $self->thin_walls } | ||||
|     )->chained_path(0)}; | ||||
|     # detect thin walls by offsetting slices by half extrusion inwards | ||||
|     # and add them as perimeters | ||||
|     if ($self->config->thin_walls) { | ||||
|         # we use spacing here because there could be a case where | ||||
|         # the slice collapses with width but doesn't collapse with spacing, | ||||
|         # thus causing both perimeters and medial axis to be generated | ||||
|         my $width = $self->perimeter_flow->scaled_spacing; | ||||
|         my $diff = diff_ex( | ||||
|             [ map $_->p, @{$self->slices} ], | ||||
|             offset2([ map $_->p, @{$self->slices} ], -$width*0.5, +$width*0.5), | ||||
|             1, | ||||
|         ); | ||||
|          | ||||
|         my $area_threshold = $width ** 2; | ||||
|         if (@$diff = grep { $_->area > $area_threshold } @$diff) { | ||||
|             my @p = map $_->medial_axis($width), @$diff; | ||||
|             my @paths = (); | ||||
|             for my $p (@p) { | ||||
|                 my %params = ( | ||||
|                     role            => EXTR_ROLE_EXTERNAL_PERIMETER, | ||||
|                     flow_spacing    => $self->perimeter_flow->spacing, | ||||
|                 ); | ||||
|                 push @paths, $p->isa('Slic3r::Polygon') | ||||
|                     ? Slic3r::ExtrusionLoop->new(polygon  => $p, %params) | ||||
|                     : Slic3r::ExtrusionPath->new(polyline => $p, %params); | ||||
|             } | ||||
|              | ||||
|             $self->perimeters->append( | ||||
|                 map $_->clone, @{Slic3r::ExtrusionPath::Collection->new(@paths)->chained_path(0)} | ||||
|             ); | ||||
|             Slic3r::debugf "  %d thin walls detected\n", scalar(@paths) if $Slic3r::debug; | ||||
|              | ||||
|             # in the mean time we subtract thin walls from the detected gaps so that we don't | ||||
|             # reprocess them, causing overlapping thin walls and zigzag. | ||||
|             @gaps = @{diff( | ||||
|                 \@gaps, | ||||
|                 [ map $_->grow($self->perimeter_flow->scaled_width), @p ], | ||||
|                 1, | ||||
|             )}; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     $self->_fill_gaps(\@gaps); | ||||
| } | ||||
| 
 | ||||
| sub _fill_gaps { | ||||
|  | @ -318,19 +327,15 @@ sub _fill_gaps { | |||
|      | ||||
|     return unless @$gaps; | ||||
|      | ||||
|     # turn gaps into ExPolygons | ||||
|     $gaps = union_ex($gaps); | ||||
|      | ||||
|     my $filler = $self->layer->object->fill_maker->filler('rectilinear'); | ||||
|     $filler->layer_id($self->layer->id); | ||||
|      | ||||
|     # we should probably use this code to handle thin walls and remove that logic from | ||||
|     # make_surfaces(), but we need to enable dynamic extrusion width before as we can't | ||||
|     # use zigzag for thin walls. | ||||
|     # in the mean time we subtract thin walls from the detected gaps so that we don't | ||||
|     # reprocess them, causing overlapping thin walls and zigzag. | ||||
|     @$gaps = @{diff_ex( | ||||
|         [ map @$_, @$gaps ], | ||||
|         [ map $_->grow($self->perimeter_flow->scaled_width), @{$self->{thin_walls}} ], | ||||
|         1, | ||||
|     )}; | ||||
|      | ||||
|     # medial axis-based gap fill should benefit from detection of larger gaps too, so  | ||||
|     # we could try with 1.5*$w for example, but that doesn't work well for zigzag fill | ||||
|  | @ -375,29 +380,27 @@ sub _fill_gaps { | |||
|                     flow_spacing    => $flow->spacing, | ||||
|                 ); | ||||
|                  | ||||
|                 push @{ $self->thin_fills }, | ||||
|                     map { | ||||
|                         $_->simplify($flow->scaled_width/3); | ||||
|                         $_; | ||||
|                     } | ||||
|                     map Slic3r::ExtrusionPath->new( | ||||
|                         polyline        => Slic3r::Polyline->new(@$_), | ||||
|                         role            => EXTR_ROLE_GAPFILL, | ||||
|                         height          => $self->height, | ||||
|                         flow_spacing    => $params->{flow_spacing}, | ||||
|                     ), | ||||
|                     # Split polylines into lines so that the chained_path() search | ||||
|                     # at the final stage has more freedom and will choose starting | ||||
|                     # points closer than last positions. OTOH, this will make such | ||||
|                     # search slower. Probably, ExtrusionPath objects should support | ||||
|                     # splitting nearby a given position so that we can choose the right | ||||
|                     # entry point even in the middle of the path without needing a  | ||||
|                     # complex, slow, chained_path() search on all segments. TODO. | ||||
|                     # Such logic will also avoid all the small travel moves that this  | ||||
|                     # line-splitting causes, and it will be applicable to other things | ||||
|                     # too. | ||||
|                     map Slic3r::Polyline->new(@$_)->lines, | ||||
|                     @paths; | ||||
|                 # Split polylines into lines so that the chained_path() search | ||||
|                 # at the final stage has more freedom and will choose starting | ||||
|                 # points closer than last positions. OTOH, this will make such | ||||
|                 # search slower. Probably, ExtrusionPath objects should support | ||||
|                 # splitting nearby a given position so that we can choose the right | ||||
|                 # entry point even in the middle of the path without needing a  | ||||
|                 # complex, slow, chained_path() search on all segments. TODO. | ||||
|                 # Such logic will also avoid all the small travel moves that this  | ||||
|                 # line-splitting causes, and it will be applicable to other things | ||||
|                 # too. | ||||
|                 my @lines = map @{Slic3r::Polyline->new(@$_)->lines}, @paths; | ||||
|                  | ||||
|                 @paths = map Slic3r::ExtrusionPath->new( | ||||
|                     polyline        => Slic3r::Polyline->new(@$_), | ||||
|                     role            => EXTR_ROLE_GAPFILL, | ||||
|                     height          => $self->height, | ||||
|                     flow_spacing    => $params->{flow_spacing}, | ||||
|                 ), @lines; | ||||
|                 $_->simplify($flow->scaled_width/3) for @paths; | ||||
|                  | ||||
|                 $self->thin_fills->append(@paths); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci