diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 8adfd2126..eb2656a34 100644 --- a/lib/Slic3r/GCode.pm +++ b/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)); diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 91961f602..e67da8add 100644 --- a/lib/Slic3r/Layer.pm +++ b/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'); diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 2decedaed..ba93efa2a 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/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; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index c653a154e..e41f6813f 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/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++; diff --git a/t/perimeters.t b/t/perimeters.t index ca9ed9ac0..b290615d1 100644 --- a/t/perimeters.t +++ b/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__