From 7e875393f5ecd4ac4b488f9bea1b8efdbba02193 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 20 Jun 2013 20:11:46 +0200 Subject: [PATCH] New algorithm for overhang detection --- lib/Slic3r/ExtrusionPath.pm | 68 ++++++++++++++++++++----------------- lib/Slic3r/GCode.pm | 30 +++++++++++++++- lib/Slic3r/Layer/Region.pm | 9 ----- 3 files changed, 65 insertions(+), 42 deletions(-) diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index c8efc675e..49af9b826 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -60,6 +60,17 @@ sub pack { # no-op, this allows to use both packed and non-packed objects in Collections sub unpack { $_[0] } +sub clone { + my $self = shift; + my %p = @_; + + $p{polyline} ||= $self->polyline->clone; + return (ref $self)->new( + (map { $_ => $self->$_ } qw(polyline height flow_spacing role)), + %p, + ); +} + sub clip_with_polygon { my $self = shift; my ($polygon) = @_; @@ -71,16 +82,24 @@ sub clip_with_expolygon { my $self = shift; my ($expolygon) = @_; - my @paths = (); - foreach my $polyline ($self->polyline->clip_with_expolygon($expolygon)) { - push @paths, (ref $self)->new( - polyline => $polyline, - height => $self->height, - flow_spacing => $self->flow_spacing, - role => $self->role, - ); - } - return @paths; + return map $self->clone(polyline => $_), + $self->polyline->clip_with_expolygon($expolygon); +} + +sub intersect_expolygons { + my $self = shift; + my ($expolygons) = @_; + + return map $self->clone(polyline => Slic3r::Polyline->new(@$_)), + @{Boost::Geometry::Utils::multi_polygon_multi_linestring_intersection($expolygons, [$self->polyline])}; +} + +sub subtract_expolygons { + my $self = shift; + my ($expolygons) = @_; + + return map $self->clone(polyline => Slic3r::Polyline->new(@$_)), + @{Boost::Geometry::Utils::multi_linestring_multi_polygon_difference([$self->polyline], $expolygons)}; } sub simplify { @@ -141,22 +160,15 @@ sub split_at_acute_angles { # if the angle between $p[-2], $p[-1], $p3 is too acute # then consider $p3 only as a starting point of a new # path and stop the current one as it is - push @paths, (ref $self)->new( - polyline => Slic3r::Polyline->new(\@p), - role => $self->role, - height => $self->height, - ); + push @paths, $self->clone(polyline => Slic3r::Polyline->new(\@p)); @p = ($p3); push @p, grep $_, shift @points or last; } else { push @p, $p3; } } - push @paths, (ref $self)->new( - polyline => Slic3r::Polyline->new(\@p), - role => $self->role, - height => $self->height, - ) if @p > 1; + push @paths, $self->clone(polyline => Slic3r::Polyline->new(\@p)) + if @p > 1; return @paths; } @@ -251,12 +263,8 @@ sub detect_arcs { ); # points 0..$i form a linear path - push @paths, (ref $self)->new( - polyline => Slic3r::Polyline->new(@points[0..$i]), - role => $self->role, - flow_spacing => $self->flow_spacing, - height => $self->height, - ) if $i > 0; + push @paths, $self->clone(polyline => Slic3r::Polyline->new(@points[0..$i])) + if $i > 0; # add our arc push @paths, $arc; @@ -271,12 +279,8 @@ sub detect_arcs { } # remaining points form a linear path - push @paths, (ref $self)->new( - polyline => Slic3r::Polyline->new(\@points), - role => $self->role, - flow_spacing => $self->flow_spacing, - height => $self->height, - ) if @points > 1; + push @paths, $self->clone(polyline => Slic3r::Polyline->new(\@points)) + if @points > 1; return @paths; } diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index e77b5ba1b..585b14a0b 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -5,11 +5,13 @@ use List::Util qw(min max first); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y B); use Slic3r::Geometry::Clipper qw(union_ex); +use Slic3r::Surface ':types'; has 'config' => (is => 'ro', required => 1); has 'multiple_extruders' => (is => 'ro', default => sub {0} ); has 'layer_count' => (is => 'ro', required => 1 ); has 'layer' => (is => 'rw'); +has '_layer_overhangs' => (is => 'rw'); has 'move_z_callback' => (is => 'rw'); has 'shift_x' => (is => 'rw', default => sub {0} ); has 'shift_y' => (is => 'rw', default => sub {0} ); @@ -82,6 +84,11 @@ sub change_layer { my ($layer) = @_; $self->layer($layer); + $self->_layer_overhangs( + $layer->id > 0 + ? [ map $_->expolygon, grep $_->surface_type == S_TYPE_BOTTOM, map @{$_->slices}, @{$layer->regions} ] + : [] + ); if ($self->config->avoid_crossing_perimeters) { $self->layer_mp(Slic3r::GCode::MotionPlanner->new( islands => union_ex([ map @$_, @{$layer->slices} ], undef, 1), @@ -152,8 +159,29 @@ sub extrude_loop { $extrusion_path->clip_end(scale $extrusion_path->flow_spacing * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING); return '' if !@{$extrusion_path->polyline}; + my @paths = (); + # detect overhanging/bridging perimeters + if ($extrusion_path->is_perimeter && @{$self->_layer_overhangs}) { + # get non-overhang paths by subtracting overhangs from the loop + push @paths, + $extrusion_path->subtract_expolygons($self->_layer_overhangs); + + # get overhang paths by intersecting overhangs with the loop + push @paths, + map { $_->role(EXTR_ROLE_OVERHANG_PERIMETER); $_ } + $extrusion_path->intersect_expolygons($self->_layer_overhangs); + + # reapply the nearest point search for starting point + # (TODO: choose the nearest point not on an overhang) + @paths = Slic3r::ExtrusionPath::Collection + ->new(paths => [@paths]) + ->chained_path($last_pos); + } else { + push @paths, $extrusion_path; + } + # extrude along the path - my $gcode = $self->extrude_path($extrusion_path, $description); + my $gcode = join '', map $self->extrude_path($_, $description), @paths; $self->wipe_path($extrusion_path->polyline); # make a little move inwards before leaving loop diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 33cf67953..7a838d65a 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -267,15 +267,6 @@ sub make_perimeters { $role = EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER; } - if ($self->id > 0) { - # A perimeter is considered overhang if its centerline exceeds the lower layer slices - 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 => $polygon, role => $role,