Browse Source

Stricter implementation of the overhang detection. Includes unit tests

Alessandro Ranellucci 11 years ago
parent
commit
a31b2e6ca2
5 changed files with 51 additions and 21 deletions
  1. 19 13
      lib/Slic3r/GCode.pm
  2. 1 0
      lib/Slic3r/Layer.pm
  3. 0 7
      lib/Slic3r/Layer/Region.pm
  4. 1 0
      lib/Slic3r/Print/Object.pm
  5. 30 1
      t/perimeters.t

+ 19 - 13
lib/Slic3r/GCode.pm

@@ -5,7 +5,7 @@ use List::Util qw(min max first);
 use Slic3r::ExtrusionPath ':roles';
 use Slic3r::Flow ':roles';
 use Slic3r::Geometry qw(epsilon scale unscale scaled_epsilon points_coincide PI X Y B);
-use Slic3r::Geometry::Clipper qw(union_ex);
+use Slic3r::Geometry::Clipper qw(union_ex offset_ex);
 use Slic3r::Surface ':types';
 
 has 'print_config'       => (is => 'ro', default => sub { Slic3r::Config::Print->new });
@@ -19,7 +19,7 @@ has 'layer'              => (is => 'rw');
 has 'region'             => (is => 'rw');
 has '_layer_islands'     => (is => 'rw');
 has '_upper_layer_islands'  => (is => 'rw');
-has '_layer_overhangs'   => (is => 'ro', default => sub { Slic3r::ExPolygon::Collection->new });
+has '_lower_layer_slices'   => (is => 'ro', default => sub { Slic3r::ExPolygon::Collection->new });
 has 'shift_x'            => (is => 'rw', default => sub {0} );
 has 'shift_y'            => (is => 'rw', default => sub {0} );
 has 'z'                  => (is => 'rw');
@@ -111,11 +111,15 @@ sub change_layer {
     # avoid computing islands and overhangs if they're not needed
     $self->_layer_islands($layer->islands);
     $self->_upper_layer_islands($layer->upper_layer ? $layer->upper_layer->islands : []);
-    $self->_layer_overhangs->clear;
-    if ($layer->id > 0 && ($self->print_config->overhangs || $self->print_config->start_perimeters_at_non_overhang)) {
-        $self->_layer_overhangs->append(
+    $self->_lower_layer_slices->clear;
+    if ($layer->lower_layer && ($self->print_config->overhangs || $self->print_config->start_perimeters_at_non_overhang)) {
+        # We consider overhang any part where the entire nozzle diameter is not supported by the
+        # lower layer, so we take lower slices and offset them by half the nozzle diameter used 
+        # in the current layer
+        my $max_nozzle_diameter = max(map $layer->print->config->get_at('nozzle_diameter', $_->region->config->perimeter_extruder-1), @{$layer->regions});
+        $self->_lower_layer_slices->append(
             # clone ExPolygons because they come from Surface objects but will be used outside here
-            map $_->expolygon, map @{$_->slices->filter_by_type(S_TYPE_BOTTOMBRIDGE)}, @{$layer->regions}
+            @{offset_ex([ map @$_, @{$layer->lower_layer->slices} ], +scale($max_nozzle_diameter/2))},
         );
     }
     if ($self->print_config->avoid_crossing_perimeters) {
@@ -205,7 +209,7 @@ sub extrude_loop {
     }
     my @candidates = ();
     if ($self->print_config->start_perimeters_at_non_overhang) {
-        @candidates = grep !$self->_layer_overhangs->contains_point($_), @concave;
+        @candidates = grep $self->_lower_layer_slices->contains_point($_), @concave;
     }
     if (!@candidates) {
         # if none, look for any concave vertex
@@ -213,7 +217,7 @@ sub extrude_loop {
         if (!@candidates) {
             # if none, look for any non-overhang vertex
             if ($self->print_config->start_perimeters_at_non_overhang) {
-                @candidates = grep !$self->_layer_overhangs->contains_point($_), @$polygon;
+                @candidates = grep $self->_lower_layer_slices->contains_point($_), @$polygon;
             }
             if (!@candidates) {
                 # if none, all points are valid candidates
@@ -243,14 +247,16 @@ sub extrude_loop {
     
     my @paths = ();
     # detect overhanging/bridging perimeters
-    if ($self->layer->print->config->overhangs && $extrusion_path->is_perimeter && $self->_layer_overhangs->count > 0) {
-        # get non-overhang paths by subtracting overhangs from the loop
+    if ($self->layer->print->config->overhangs && $extrusion_path->is_perimeter && $self->_lower_layer_slices->count > 0) {
+        # get non-overhang paths by intersecting this loop with the grown lower slices
         push @paths,
             map $_->clone,
-            @{$extrusion_path->subtract_expolygons($self->_layer_overhangs)};
+            @{$extrusion_path->intersect_expolygons($self->_lower_layer_slices)};
         
-        # get overhang paths by intersecting overhangs with the loop
-        foreach my $path (@{$extrusion_path->intersect_expolygons($self->_layer_overhangs)}) {
+        # get overhang paths by checking what parts of this loop fall 
+        # outside the grown lower slices (thus where the distance between
+        # the loop centerline and original lower slices is >= half nozzle diameter
+        foreach my $path (@{$extrusion_path->subtract_expolygons($self->_lower_layer_slices)}) {
             $path = $path->clone;
             $path->role(EXTR_ROLE_OVERHANG_PERIMETER);
             $path->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, -1, 1, 0, undef, $self->layer->object)->mm3_per_mm(-1));

+ 1 - 0
lib/Slic3r/Layer.pm

@@ -8,6 +8,7 @@ use Slic3r::Geometry::Clipper qw(union_ex);
 has 'id'                => (is => 'rw', required => 1); # sequential number of layer, 0-based
 has 'object'            => (is => 'ro', weak_ref => 1, required => 1, handles => [qw(print config)]);
 has 'upper_layer'       => (is => 'rw', weak_ref => 1);
+has 'lower_layer'       => (is => 'rw', weak_ref => 1);
 has 'regions'           => (is => 'ro', default => sub { [] });
 has 'slicing_errors'    => (is => 'rw');
 

+ 0 - 7
lib/Slic3r/Layer/Region.pm

@@ -18,7 +18,6 @@ has 'layer' => (
 );
 has 'region'            => (is => 'ro', required => 1, handles => [qw(config)]);
 has 'infill_area_threshold' => (is => 'lazy');
-has 'overhang_width'    => (is => 'lazy');
 
 # collection of surfaces generated by slicing the original geometry
 # divided by type top/bottom/internal
@@ -42,12 +41,6 @@ has 'perimeters' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collect
 # ordered collection of extrusion paths to fill surfaces
 has 'fills' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new });
 
-sub _build_overhang_width {
-    my $self = shift;
-    my $threshold_rad = PI/2 - atan2($self->flow(FLOW_ROLE_PERIMETER)->width / $self->height / 2, 1);
-    return scale($self->height * ((cos $threshold_rad) / (sin $threshold_rad)));
-}
-
 sub _build_infill_area_threshold {
     my $self = shift;
     return $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing ** 2;

+ 1 - 0
lib/Slic3r/Print/Object.pm

@@ -176,6 +176,7 @@ sub slice {
             );
             if (@{$self->layers} >= 2) {
                 $self->layers->[-2]->upper_layer($self->layers->[-1]);
+                $self->layers->[-1]->lower_layer($self->layers->[-2]);
             }
             $id++;
         

+ 30 - 1
t/perimeters.t

@@ -1,4 +1,4 @@
-use Test::More tests => 7;
+use Test::More tests => 9;
 use strict;
 use warnings;
 
@@ -236,4 +236,33 @@ use Slic3r::Test;
     ok !(defined first { $_->area > ($pflow->scaled_width**2) } @$non_covered), 'no gap between perimeters and infill';
 }
 
+{
+    my $config = Slic3r::Config->new_from_defaults;
+    $config->set('skirts', 0);
+    $config->set('perimeters', 3);
+    $config->set('layer_height', 0.4);
+    $config->set('bridge_speed', 99);
+    $config->set('fill_density', 0);                # to prevent bridging over sparse infill
+    $config->set('overhangs', 1);
+    $config->set('cooling', 0);                     # to prevent speeds from being altered
+    $config->set('first_layer_speed', '100%');      # to prevent speeds from being altered
+    
+    my $test = sub {
+        my ($print) = @_;
+        my $has_bridges = 0;
+        Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
+            my ($self, $cmd, $args, $info) = @_;
+        
+            if ($info->{extruding} && $info->{dist_XY} > 0) {
+                $has_bridges++ if ($args->{F} // $self->F) == $config->bridge_speed*60;
+            }
+        });
+        return $has_bridges;
+    };
+    ok !$test->(Slic3r::Test::init_print('V', config => $config)),
+        'no overhangs printed with bridge speed';
+    ok $test->(Slic3r::Test::init_print('V', config => $config, scale_xyz => [3,1,1])),
+        'overhangs printed with bridge speed';
+}
+
 __END__