mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Dynamic extrusion width for better gap filling
This commit is contained in:
		
							parent
							
								
									65b11fa850
								
							
						
					
					
						commit
						0aa224ffad
					
				
					 6 changed files with 136 additions and 21 deletions
				
			
		
							
								
								
									
										1
									
								
								MANIFEST
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								MANIFEST
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -51,6 +51,7 @@ t/arcs.t
 | 
			
		|||
t/clean_polylines.t
 | 
			
		||||
t/clipper.t
 | 
			
		||||
t/collinear.t
 | 
			
		||||
t/dynamic.t
 | 
			
		||||
t/fill.t
 | 
			
		||||
t/geometry.t
 | 
			
		||||
t/polyclip.t
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -91,6 +91,13 @@ sub offset_ex {
 | 
			
		|||
    return @{ union_ex(\@offsets) };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub noncollapsing_offset_ex {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my ($distance, @params) = @_;
 | 
			
		||||
    
 | 
			
		||||
    return $self->offset_ex($distance + 1, @params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub encloses_point {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my ($point) = @_;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -178,18 +178,8 @@ sub make_fill {
 | 
			
		|||
    }
 | 
			
		||||
    
 | 
			
		||||
    # add thin fill regions
 | 
			
		||||
    {
 | 
			
		||||
        my %args = (
 | 
			
		||||
            role            => EXTR_ROLE_SOLIDFILL,
 | 
			
		||||
            flow_spacing    => $layer->perimeter_flow->spacing,
 | 
			
		||||
        );
 | 
			
		||||
        push @fills, map {
 | 
			
		||||
            $_->isa('Slic3r::Polygon')
 | 
			
		||||
                ? (map $_->pack, Slic3r::ExtrusionLoop->new(polygon  => $_, %args)->split_at_first_point)
 | 
			
		||||
                : Slic3r::ExtrusionPath->pack(polyline => $_, %args),
 | 
			
		||||
        } @{$layer->thin_fills};
 | 
			
		||||
    }
 | 
			
		||||
    push @fills_ordering_points, map $_->[0], @{$layer->thin_fills};
 | 
			
		||||
    push @fills, @{$layer->thin_fills};
 | 
			
		||||
    push @fills_ordering_points, map $_->unpack->points->[0], @{$layer->thin_fills};
 | 
			
		||||
    
 | 
			
		||||
    # organize infill paths using a shortest path search
 | 
			
		||||
    @fills = @{shortest_path([
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,4 +55,14 @@ sub _build_spacing {
 | 
			
		|||
    return $self->width - &Slic3r::OVERLAP_FACTOR * ($self->width - $min_flow_spacing);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub clone {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    return (ref $self)->new(
 | 
			
		||||
        nozzle_diameter => $self->nozzle_diameter,
 | 
			
		||||
        layer_height    => $self->layer_height,
 | 
			
		||||
        @_,
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -225,12 +225,14 @@ sub make_perimeters {
 | 
			
		|||
            # offsetting a polygon can result in one or many offset polygons
 | 
			
		||||
            my @new_offsets = ();
 | 
			
		||||
            foreach my $expolygon (@last_offsets) {
 | 
			
		||||
                my @offsets = map $_->offset_ex(+0.5*$distance), $expolygon->offset_ex(-1.5*$distance);
 | 
			
		||||
                my @offsets = map $_->offset_ex(+0.5*$distance), $expolygon->noncollapsing_offset_ex(-1.5*$distance);
 | 
			
		||||
                push @new_offsets, @offsets;
 | 
			
		||||
                
 | 
			
		||||
                # where the above check collapses the expolygon, then there's no room for an inner loop
 | 
			
		||||
                # and we can extract the gap for later processing
 | 
			
		||||
                my $diff = diff_ex(
 | 
			
		||||
                    [ map @$_, $expolygon->offset_ex(-$distance) ],
 | 
			
		||||
                    [ map @$_, @offsets ],
 | 
			
		||||
                    [ map @$_, $expolygon->offset_ex(-0.5*$distance) ],
 | 
			
		||||
                    [ map @$_, map $_->offset_ex(+0.5*$distance), @offsets ],  # should these be offsetted in a single pass?
 | 
			
		||||
                );
 | 
			
		||||
                push @gaps, grep $_->area >= $gap_area_threshold, @$diff;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -245,14 +247,40 @@ sub make_perimeters {
 | 
			
		|||
            my @fill_boundaries = map $_->offset_ex(-$distance), @last_offsets;
 | 
			
		||||
            $_->simplify(scale &Slic3r::RESOLUTION) for @fill_boundaries;
 | 
			
		||||
            push @{ $self->surfaces }, @fill_boundaries;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        # fill gaps using dynamic extrusion width
 | 
			
		||||
        {
 | 
			
		||||
            # detect the small gaps that we need to treat like thin polygons,
 | 
			
		||||
            # thus generating the skeleton and using it to fill them
 | 
			
		||||
            push @{ $self->thin_fills },
 | 
			
		||||
                map $_->medial_axis(scale $self->perimeter_flow->width),
 | 
			
		||||
                @gaps;
 | 
			
		||||
            Slic3r::debugf "  %d gaps filled\n", scalar @{ $self->thin_fills }
 | 
			
		||||
                if @{ $self->thin_fills };
 | 
			
		||||
            my $w = $self->perimeter_flow->width;
 | 
			
		||||
            my @widths = (1.5 * $w, $w, 0.5 * $w, 0.2 * $w);
 | 
			
		||||
            foreach my $width (@widths) {
 | 
			
		||||
                my $scaled_width = scale $width;
 | 
			
		||||
                
 | 
			
		||||
                # extract the gaps having this width
 | 
			
		||||
                my @this_width = map $_->offset_ex(+0.5*$scaled_width), map $_->noncollapsing_offset_ex(-0.5*$scaled_width), @gaps;
 | 
			
		||||
                
 | 
			
		||||
                # fill them
 | 
			
		||||
                my %path_args = (
 | 
			
		||||
                    role            => EXTR_ROLE_SOLIDFILL,
 | 
			
		||||
                    flow_spacing    => $self->perimeter_flow->clone(width => $width)->spacing,
 | 
			
		||||
                );
 | 
			
		||||
                push @{ $self->thin_fills }, map {
 | 
			
		||||
                    $_->isa('Slic3r::Polygon')
 | 
			
		||||
                        ? (map $_->pack, Slic3r::ExtrusionLoop->new(polygon => $_, %path_args)->split_at_first_point)  # we should keep these as loops
 | 
			
		||||
                        : Slic3r::ExtrusionPath->pack(polyline => $_, %path_args),
 | 
			
		||||
                } map $_->medial_axis($scaled_width), @this_width;
 | 
			
		||||
            
 | 
			
		||||
                Slic3r::debugf "  %d gaps filled with extrusion width = %s\n", scalar @this_width, $width
 | 
			
		||||
                    if @{ $self->thin_fills };
 | 
			
		||||
                
 | 
			
		||||
                # check what's left
 | 
			
		||||
                @gaps = @{diff_ex(
 | 
			
		||||
                    [ map @$_, @gaps ],
 | 
			
		||||
                    [ map @$_, @this_width ],
 | 
			
		||||
                )};
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										79
									
								
								t/dynamic.t
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								t/dynamic.t
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,79 @@
 | 
			
		|||
use Test::More;
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
plan tests => 20;
 | 
			
		||||
 | 
			
		||||
BEGIN {
 | 
			
		||||
    use FindBin;
 | 
			
		||||
    use lib "$FindBin::Bin/../lib";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
use List::Util qw(first);
 | 
			
		||||
use Slic3r;
 | 
			
		||||
use Slic3r::Geometry qw(X Y scale epsilon);
 | 
			
		||||
use Slic3r::Surface ':types';
 | 
			
		||||
 | 
			
		||||
sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $square = Slic3r::ExPolygon->new([
 | 
			
		||||
        scale_points [0,0], [10,0], [10,10], [0,10],
 | 
			
		||||
    ]);
 | 
			
		||||
    
 | 
			
		||||
    my @offsets = $square->noncollapsing_offset_ex(- scale 5);
 | 
			
		||||
    is scalar @offsets, 1, 'non-collapsing offset';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $w = 0.7;
 | 
			
		||||
    local $Slic3r::perimeter_flow = Slic3r::Flow->new(
 | 
			
		||||
        nozzle_diameter => 0.5,
 | 
			
		||||
        layer_height    => 0.4,
 | 
			
		||||
        width           => $w,
 | 
			
		||||
    );
 | 
			
		||||
    local $Slic3r::Config = Slic3r::Config->new(
 | 
			
		||||
        perimeters      => 3,
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    my $make_layer = sub {
 | 
			
		||||
        my ($width) = @_;
 | 
			
		||||
        my $layer = Slic3r::Layer->new(
 | 
			
		||||
            id => 1,
 | 
			
		||||
            slices => [
 | 
			
		||||
                Slic3r::Surface->new(
 | 
			
		||||
                    surface_type    => S_TYPE_INTERNAL,
 | 
			
		||||
                    expolygon       => Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,$width], [0,$width] ]),
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
            thin_walls => [],
 | 
			
		||||
        );
 | 
			
		||||
        $layer->make_perimeters;
 | 
			
		||||
        return $layer;
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    my %widths = (
 | 
			
		||||
        1   * $w => { perimeters => 1, gaps => 0 },
 | 
			
		||||
        1.3 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->clone(width => 0.2 * $w)->spacing },
 | 
			
		||||
        1.5 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->clone(width => 0.5 * $w)->spacing },
 | 
			
		||||
        2   * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->spacing },
 | 
			
		||||
        2.5 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->clone(width => 1.5 * $w)->spacing },
 | 
			
		||||
        3   * $w => { perimeters => 2, gaps => 0 },
 | 
			
		||||
        4   * $w => { perimeters => 2, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->spacing },
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    foreach my $width (sort keys %widths) {
 | 
			
		||||
        my $layer = $make_layer->($width);
 | 
			
		||||
        is scalar @{$layer->perimeters}, $widths{$width}{perimeters}, 'right number of perimeters';
 | 
			
		||||
        is scalar @{$layer->thin_fills} ? 1 : 0, $widths{$width}{gaps},
 | 
			
		||||
            ($widths{$width}{gaps} ? 'gaps were filled' : 'no gaps detected');  # TODO: we should check the exact number of gaps, but we need a better medial axis algorithm
 | 
			
		||||
        
 | 
			
		||||
        my @gaps = map $_->unpack, @{$layer->thin_fills};
 | 
			
		||||
        if (@gaps) {
 | 
			
		||||
            ok +(!first { abs($_->flow_spacing - $widths{$width}{gap_flow_spacing}) > epsilon } @gaps),
 | 
			
		||||
                'flow spacing was dynamically adjusted';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__END__
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue