Browse Source

New algorithm for overhang detection

Alessandro Ranellucci 12 years ago
parent
commit
575127151b

+ 6 - 3
lib/Slic3r/ExtrusionPath.pm

@@ -4,7 +4,7 @@ use Moo;
 require Exporter;
 our @ISA = qw(Exporter);
 our @EXPORT_OK = qw(EXTR_ROLE_PERIMETER EXTR_ROLE_EXTERNAL_PERIMETER 
-    EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER
+    EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER EXTR_ROLE_OVERHANG_PERIMETER
     EXTR_ROLE_FILL EXTR_ROLE_SOLIDFILL EXTR_ROLE_TOPSOLIDFILL EXTR_ROLE_BRIDGE 
     EXTR_ROLE_INTERNALBRIDGE EXTR_ROLE_SKIRT EXTR_ROLE_SUPPORTMATERIAL EXTR_ROLE_GAPFILL);
 our %EXPORT_TAGS = (roles => \@EXPORT_OK);
@@ -24,7 +24,8 @@ has 'flow_spacing' => (is => 'rw');
 has 'role'         => (is => 'rw', required => 1);
 
 use constant EXTR_ROLE_PERIMETER                    => 0;
-use constant EXTR_ROLE_EXTERNAL_PERIMETER           => 2;
+use constant EXTR_ROLE_EXTERNAL_PERIMETER           => 1;
+use constant EXTR_ROLE_OVERHANG_PERIMETER           => 2;
 use constant EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER   => 3;
 use constant EXTR_ROLE_FILL                         => 4;
 use constant EXTR_ROLE_SOLIDFILL                    => 5;
@@ -101,6 +102,7 @@ sub is_perimeter {
     my $self = shift;
     return $self->role == EXTR_ROLE_PERIMETER
         || $self->role == EXTR_ROLE_EXTERNAL_PERIMETER
+        || $self->role == EXTR_ROLE_OVERHANG_PERIMETER
         || $self->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER;
 }
 
@@ -114,7 +116,8 @@ sub is_fill {
 sub is_bridge {
     my $self = shift;
     return $self->role == EXTR_ROLE_BRIDGE
-        || $self->role == EXTR_ROLE_INTERNALBRIDGE;
+        || $self->role == EXTR_ROLE_INTERNALBRIDGE
+        || $self->role == EXTR_ROLE_OVERHANG_PERIMETER;
 }
 
 sub split_at_acute_angles {

+ 3 - 1
lib/Slic3r/Flow.pm

@@ -38,7 +38,9 @@ sub _build_width {
     
     my $min = $self->nozzle_diameter * 1.05;
     my $max;
-    if ($self->role ne 'infill') {
+    if ($self->role eq 'perimeter') {
+        $min = $max = $self->nozzle_diameter;
+    } elsif ($self->role ne 'infill') {
         # do not limit width for sparse infill so that we use full native flow for it
         $max = $self->nozzle_diameter * 1.7;
     }

+ 3 - 2
lib/Slic3r/GCode.pm

@@ -49,6 +49,7 @@ has 'speeds' => (
 my %role_speeds = (
     &EXTR_ROLE_PERIMETER                    => 'perimeter',
     &EXTR_ROLE_EXTERNAL_PERIMETER           => 'external_perimeter',
+    &EXTR_ROLE_OVERHANG_PERIMETER           => 'external_perimeter',
     &EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER   => 'perimeter',
     &EXTR_ROLE_FILL                         => 'infill',
     &EXTR_ROLE_SOLIDFILL                    => 'solid_infill',
@@ -208,13 +209,13 @@ sub extrude_path {
         $acceleration = $Slic3r::Config->perimeter_acceleration;
     } elsif ($Slic3r::Config->infill_acceleration && $path->is_fill) {
         $acceleration = $Slic3r::Config->infill_acceleration;
-    } elsif ($Slic3r::Config->infill_acceleration && ($path->role == EXTR_ROLE_BRIDGE || $path->role == EXTR_ROLE_INTERNALBRIDGE)) {
+    } elsif ($Slic3r::Config->infill_acceleration && $path->is_bridge) {
         $acceleration = $Slic3r::Config->bridge_acceleration;
     }
     $gcode .= $self->set_acceleration($acceleration) if $acceleration;
     
     my $area;  # mm^3 of extrudate per mm of tool movement 
-    if ($path->role == EXTR_ROLE_BRIDGE || $path->role == EXTR_ROLE_INTERNALBRIDGE) {
+    if ($path->is_bridge) {
         my $s = $path->flow_spacing;
         $area = ($s**2) * PI/4;
     } else {

+ 14 - 1
lib/Slic3r/Geometry/Clipper.pm

@@ -6,7 +6,8 @@ require Exporter;
 our @ISA = qw(Exporter);
 our @EXPORT_OK = qw(safety_offset safety_offset_ex offset offset_ex collapse_ex
     diff_ex diff union_ex intersection_ex xor_ex PFT_EVENODD JT_MITER JT_ROUND
-    JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex traverse_pt);
+    JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex traverse_pt
+    intersection);
 
 use Math::Clipper 1.21 qw(:cliptypes :polyfilltypes :jointypes is_counter_clockwise area);
 use Slic3r::Geometry qw(scale);
@@ -118,6 +119,18 @@ sub intersection_ex {
     ];
 }
 
+sub intersection {
+    my ($subject, $clip, $jointype, $safety_offset) = @_;
+    $jointype = PFT_NONZERO unless defined $jointype;
+    $clipper->clear;
+    $clipper->add_subject_polygons($subject);
+    $clipper->add_clip_polygons($safety_offset ? safety_offset($clip) : $clip);
+    return [
+        map Slic3r::Polygon->new($_),
+            @{ $clipper->execute(CT_INTERSECTION, $jointype, $jointype) },
+    ];
+}
+
 sub xor_ex {
     my ($subject, $clip, $jointype) = @_;
     $jointype = PFT_NONZERO unless defined $jointype;

+ 19 - 3
lib/Slic3r/Layer/Region.pm

@@ -5,7 +5,7 @@ use List::Util qw(sum first);
 use Slic3r::ExtrusionPath ':roles';
 use Slic3r::Geometry qw(PI X1 X2 Y1 Y2 A B scale chained_path_items points_coincide);
 use Slic3r::Geometry::Clipper qw(safety_offset union_ex diff_ex intersection_ex 
-    offset offset2_ex PFT_EVENODD union_pt traverse_pt);
+    offset offset2_ex PFT_EVENODD union_pt traverse_pt diff intersection);
 use Slic3r::Surface ':types';
 
 has 'layer' => (
@@ -233,6 +233,11 @@ sub make_perimeters {
     my $contours_pt = union_pt(\@contours, PFT_EVENODD);
     my $holes_pt    = union_pt(\@holes, PFT_EVENODD);
     
+    # get lower layer slices for overhang check
+    my @lower_slices = $self->id == 0
+        ? ()
+        : map @$_, @{$self->layer->object->layers->[$self->id-1]->slices};
+    
     # prepare a coderef for traversing the PolyTree object
     # external contours are root items of $contours_pt
     # internal contours are the ones next to external
@@ -249,7 +254,9 @@ sub make_perimeters {
         my @loops = ();
         foreach my $polynode (@$polynodes) {
             push @loops, $traverse->($polynode->{children}, $depth+1, $is_contour);
-
+            
+            my $polygon = Slic3r::Polygon->new($polynode->{outer} // [ reverse @{$polynode->{hole}} ]);
+            
             my $role = EXTR_ROLE_PERIMETER;
             if ($is_contour ? $depth == 0 : !@{ $polynode->{children} }) {
                 # external perimeters are root level in case of contours
@@ -258,8 +265,17 @@ sub make_perimeters {
             } elsif ($depth == 1 && $is_contour) {
                 $role = EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER;
             }
+            
+            if ($self->id > 0) {
+                my $is_overhang = $is_contour
+                    ? @{diff([$polygon], \@lower_slices)}
+                    : !@{intersection([$polygon], \@lower_slices)};
+                
+                $role = EXTR_ROLE_OVERHANG_PERIMETER if $is_overhang;
+            }
+            
             push @loops, Slic3r::ExtrusionLoop->pack(
-                polygon         => Slic3r::Polygon->new($polynode->{outer} // [ reverse @{$polynode->{hole}} ]),
+                polygon         => $polygon,
                 role            => $role,
                 flow_spacing    => $self->perimeter_flow->spacing,
             );