diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index 30be3651..7e759fc9 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/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 { diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index b17b1991..eca1ff55 100644 --- a/lib/Slic3r/Flow.pm +++ b/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; } diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 7cd3d0ef..48d32e78 100644 --- a/lib/Slic3r/GCode.pm +++ b/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 { diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 498ccbcf..7e5e4b48 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/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; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 65ee99ae..e00d58c4 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/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, );