Browse Source

Dynamic extrusion width for better gap filling

Alessandro Ranellucci 12 years ago
parent
commit
0aa224ffad
6 changed files with 136 additions and 21 deletions
  1. 1 0
      MANIFEST
  2. 7 0
      lib/Slic3r/ExPolygon.pm
  3. 2 12
      lib/Slic3r/Fill.pm
  4. 10 0
      lib/Slic3r/Flow.pm
  5. 37 9
      lib/Slic3r/Layer.pm
  6. 79 0
      t/dynamic.t

+ 1 - 0
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

+ 7 - 0
lib/Slic3r/ExPolygon.pm

@@ -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) = @_;

+ 2 - 12
lib/Slic3r/Fill.pm

@@ -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([

+ 10 - 0
lib/Slic3r/Flow.pm

@@ -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;

+ 37 - 9
lib/Slic3r/Layer.pm

@@ -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 - 0
t/dynamic.t

@@ -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__