Stricter implementation of the overhang detection. Includes unit tests
This commit is contained in:
parent
caf7b3f97e
commit
a31b2e6ca2
@ -5,7 +5,7 @@ use List::Util qw(min max first);
|
|||||||
use Slic3r::ExtrusionPath ':roles';
|
use Slic3r::ExtrusionPath ':roles';
|
||||||
use Slic3r::Flow ':roles';
|
use Slic3r::Flow ':roles';
|
||||||
use Slic3r::Geometry qw(epsilon scale unscale scaled_epsilon points_coincide PI X Y B);
|
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';
|
use Slic3r::Surface ':types';
|
||||||
|
|
||||||
has 'print_config' => (is => 'ro', default => sub { Slic3r::Config::Print->new });
|
has 'print_config' => (is => 'ro', default => sub { Slic3r::Config::Print->new });
|
||||||
@ -19,7 +19,7 @@ has 'layer' => (is => 'rw');
|
|||||||
has 'region' => (is => 'rw');
|
has 'region' => (is => 'rw');
|
||||||
has '_layer_islands' => (is => 'rw');
|
has '_layer_islands' => (is => 'rw');
|
||||||
has '_upper_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_x' => (is => 'rw', default => sub {0} );
|
||||||
has 'shift_y' => (is => 'rw', default => sub {0} );
|
has 'shift_y' => (is => 'rw', default => sub {0} );
|
||||||
has 'z' => (is => 'rw');
|
has 'z' => (is => 'rw');
|
||||||
@ -111,11 +111,15 @@ sub change_layer {
|
|||||||
# avoid computing islands and overhangs if they're not needed
|
# avoid computing islands and overhangs if they're not needed
|
||||||
$self->_layer_islands($layer->islands);
|
$self->_layer_islands($layer->islands);
|
||||||
$self->_upper_layer_islands($layer->upper_layer ? $layer->upper_layer->islands : []);
|
$self->_upper_layer_islands($layer->upper_layer ? $layer->upper_layer->islands : []);
|
||||||
$self->_layer_overhangs->clear;
|
$self->_lower_layer_slices->clear;
|
||||||
if ($layer->id > 0 && ($self->print_config->overhangs || $self->print_config->start_perimeters_at_non_overhang)) {
|
if ($layer->lower_layer && ($self->print_config->overhangs || $self->print_config->start_perimeters_at_non_overhang)) {
|
||||||
$self->_layer_overhangs->append(
|
# 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
|
# 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) {
|
if ($self->print_config->avoid_crossing_perimeters) {
|
||||||
@ -205,7 +209,7 @@ sub extrude_loop {
|
|||||||
}
|
}
|
||||||
my @candidates = ();
|
my @candidates = ();
|
||||||
if ($self->print_config->start_perimeters_at_non_overhang) {
|
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 (!@candidates) {
|
||||||
# if none, look for any concave vertex
|
# if none, look for any concave vertex
|
||||||
@ -213,7 +217,7 @@ sub extrude_loop {
|
|||||||
if (!@candidates) {
|
if (!@candidates) {
|
||||||
# if none, look for any non-overhang vertex
|
# if none, look for any non-overhang vertex
|
||||||
if ($self->print_config->start_perimeters_at_non_overhang) {
|
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 (!@candidates) {
|
||||||
# if none, all points are valid candidates
|
# if none, all points are valid candidates
|
||||||
@ -243,14 +247,16 @@ sub extrude_loop {
|
|||||||
|
|
||||||
my @paths = ();
|
my @paths = ();
|
||||||
# detect overhanging/bridging perimeters
|
# detect overhanging/bridging perimeters
|
||||||
if ($self->layer->print->config->overhangs && $extrusion_path->is_perimeter && $self->_layer_overhangs->count > 0) {
|
if ($self->layer->print->config->overhangs && $extrusion_path->is_perimeter && $self->_lower_layer_slices->count > 0) {
|
||||||
# get non-overhang paths by subtracting overhangs from the loop
|
# get non-overhang paths by intersecting this loop with the grown lower slices
|
||||||
push @paths,
|
push @paths,
|
||||||
map $_->clone,
|
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
|
# get overhang paths by checking what parts of this loop fall
|
||||||
foreach my $path (@{$extrusion_path->intersect_expolygons($self->_layer_overhangs)}) {
|
# 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 = $path->clone;
|
||||||
$path->role(EXTR_ROLE_OVERHANG_PERIMETER);
|
$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));
|
$path->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, -1, 1, 0, undef, $self->layer->object)->mm3_per_mm(-1));
|
||||||
|
@ -8,6 +8,7 @@ use Slic3r::Geometry::Clipper qw(union_ex);
|
|||||||
has 'id' => (is => 'rw', required => 1); # sequential number of layer, 0-based
|
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 'object' => (is => 'ro', weak_ref => 1, required => 1, handles => [qw(print config)]);
|
||||||
has 'upper_layer' => (is => 'rw', weak_ref => 1);
|
has 'upper_layer' => (is => 'rw', weak_ref => 1);
|
||||||
|
has 'lower_layer' => (is => 'rw', weak_ref => 1);
|
||||||
has 'regions' => (is => 'ro', default => sub { [] });
|
has 'regions' => (is => 'ro', default => sub { [] });
|
||||||
has 'slicing_errors' => (is => 'rw');
|
has 'slicing_errors' => (is => 'rw');
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ has 'layer' => (
|
|||||||
);
|
);
|
||||||
has 'region' => (is => 'ro', required => 1, handles => [qw(config)]);
|
has 'region' => (is => 'ro', required => 1, handles => [qw(config)]);
|
||||||
has 'infill_area_threshold' => (is => 'lazy');
|
has 'infill_area_threshold' => (is => 'lazy');
|
||||||
has 'overhang_width' => (is => 'lazy');
|
|
||||||
|
|
||||||
# collection of surfaces generated by slicing the original geometry
|
# collection of surfaces generated by slicing the original geometry
|
||||||
# divided by type top/bottom/internal
|
# 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
|
# ordered collection of extrusion paths to fill surfaces
|
||||||
has 'fills' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new });
|
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 {
|
sub _build_infill_area_threshold {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing ** 2;
|
return $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing ** 2;
|
||||||
|
@ -176,6 +176,7 @@ sub slice {
|
|||||||
);
|
);
|
||||||
if (@{$self->layers} >= 2) {
|
if (@{$self->layers} >= 2) {
|
||||||
$self->layers->[-2]->upper_layer($self->layers->[-1]);
|
$self->layers->[-2]->upper_layer($self->layers->[-1]);
|
||||||
|
$self->layers->[-1]->lower_layer($self->layers->[-2]);
|
||||||
}
|
}
|
||||||
$id++;
|
$id++;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use Test::More tests => 7;
|
use Test::More tests => 9;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
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';
|
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__
|
__END__
|
||||||
|
Loading…
Reference in New Issue
Block a user