Refactored the travel/retract/avoid_crossing_perimeters logic. Several edge cases are now handled correctly. #2498

This commit is contained in:
Alessandro Ranellucci 2015-01-06 14:47:53 +01:00
parent 7e82159620
commit 2562070232
6 changed files with 98 additions and 103 deletions

View File

@ -332,49 +332,75 @@ sub _extrude_path {
sub travel_to {
my ($self, $point, $role, $comment) = @_;
my $gcode = "";
# Define the travel move as a line between current position and the taget point.
# This is expressed in print coordinates, so it will need to be translated by
# $self->origin in order to get G-code coordinates.
my $travel = Slic3r::Line->new($self->last_pos, $point);
my $travel = Slic3r::Polyline->new($self->last_pos, $point);
# Skip retraction at all in the following cases:
# - travel length is shorter than the configured threshold
# - user has enabled "Only retract when crossing perimeters" and the travel move is
# contained in a single internal fill_surface (this includes the bottom layer when
# bottom_solid_layers == 0) or in a single internal slice (this would exclude such
# bottom layer but preserve perimeter-to-infill moves in all the other layers)
# - the path that will be extruded after this travel move is a support material
# extrusion and the travel move is contained in a single support material island
if ($travel->length < scale $self->config->get_at('retract_before_travel', $self->writer->extruder->id)
|| ($self->config->only_retract_when_crossing_perimeters
&& $self->config->fill_density > 0
&& defined($self->layer)
&& ($self->layer->any_internal_region_slice_contains_line($travel)
|| ($self->layer->any_bottom_region_slice_contains_line($travel)
&& (!defined $self->layer->upper_layer || $self->layer->upper_layer->slices->contains_line($travel)))))
|| (defined $role && $role == EXTR_ROLE_SUPPORTMATERIAL && $self->layer->support_islands->contains_line($travel))
) {
# Just perform a straight travel move without any retraction.
$gcode .= $self->writer->travel_to_xy($self->point_to_gcode($point), $comment || '');
} elsif ($self->config->avoid_crossing_perimeters && !$self->avoid_crossing_perimeters->disable_once) {
# If avoid_crossing_perimeters is enabled and the disable_once flag is not set
# we need to plan a multi-segment travel move inside the configuration space.
$gcode .= $self->avoid_crossing_perimeters->travel_to($self, $point, $comment || '');
} else {
# If avoid_crossing_perimeters is disabled or the disable_once flag is set,
# perform a straight move with a retraction.
$gcode .= $self->retract;
$gcode .= $self->writer->travel_to_xy($self->point_to_gcode($point), $comment || '');
# check whether a straight travel move would need retraction
my $needs_retraction = $self->needs_retraction($travel, $role);
# if a retraction would be needed, try to use avoid_crossing_perimeters to plan a
# multi-hop travel path inside the configuration space
if ($needs_retraction
&& $self->config->avoid_crossing_perimeters
&& !$self->avoid_crossing_perimeters->disable_once) {
$travel = $self->avoid_crossing_perimeters->travel_to($self, $point);
# check again whether the new travel path still needs a retraction
$needs_retraction = $self->needs_retraction($travel, $role);
}
# Re-allow avoid_crossing_perimeters for the next travel moves
$self->avoid_crossing_perimeters->disable_once(0);
$self->avoid_crossing_perimeters->use_external_mp_once(0);
# generate G-code for the travel move
my $gcode = "";
$gcode .= $self->retract if $needs_retraction;
# use G1 because we rely on paths being straight (G0 may make round paths)
$gcode .= $self->writer->travel_to_xy($self->point_to_gcode($_->b), $comment)
for @{$travel->lines};
return $gcode;
}
sub needs_retraction {
my ($self, $travel, $role) = @_;
if ($travel->length < scale $self->config->get_at('retract_before_travel', $self->writer->extruder->id)) {
# skip retraction if the move is shorter than the configured threshold
return 0;
}
if (defined $role && $role == EXTR_ROLE_SUPPORTMATERIAL && $self->layer->support_islands->contains_line($travel)) {
# skip retraction if this is a travel move inside a support material island
return 0;
}
if ($self->config->only_retract_when_crossing_perimeters && defined $self->layer) {
if ($self->config->fill_density > 0
&& $self->layer->any_internal_region_slice_contains_polyline($travel)) {
# skip retraction if travel is contained in an internal slice *and*
# internal infill is enabled (so that stringing is entirely not visible)
return 0;
} elsif ($self->layer->any_bottom_region_slice_contains_polyline($travel)
&& defined $self->layer->upper_layer
&& $self->layer->upper_layer->slices->contains_polyline($travel)
&& ($self->config->bottom_solid_layers >= 2 || $self->config->fill_density > 0)) {
# skip retraction if travel is contained in an *infilled* bottom slice
# but only if it's also covered by an *infilled* upper layer's slice
# so that it's not visible from above (a bottom surface might not have an
# upper slice in case of a thin membrane)
return 0;
}
}
# retract if only_retract_when_crossing_perimeters is disabled or doesn't apply
return 1;
}
sub retract {
my ($self, $toolchange) = @_;
@ -569,9 +595,10 @@ has '_external_mp' => (is => 'rw');
has '_layer_mp' => (is => 'rw');
has 'use_external_mp' => (is => 'rw', default => sub {0});
has 'use_external_mp_once' => (is => 'rw', default => sub {0}); # this flag triggers the use of the external configuration space for avoid_crossing_perimeters for the next travel move
has 'disable_once' => (is => 'rw', default => sub {1}); # this flag disables avoid_crossing_perimeters just for the next travel move
use Slic3r::Geometry qw(scale);
# this flag disables avoid_crossing_perimeters just for the next travel move
# we enable it by default for the first travel move in print
has 'disable_once' => (is => 'rw', default => sub {1});
sub init_external_mp {
my ($self, $islands) = @_;
@ -584,47 +611,31 @@ sub init_layer_mp {
}
sub travel_to {
my ($self, $gcodegen, $point, $comment) = @_;
my $gcode = "";
my ($self, $gcodegen, $point) = @_;
if ($self->use_external_mp || $self->use_external_mp_once) {
$self->use_external_mp_once(0);
# get current origin set in $gcodegen
# (the one that will be used to translate the G-code coordinates by)
my $scaled_origin = Slic3r::Point->new_scale(@{$gcodegen->origin});
# represent $point in G-code coordinates
# represent last_pos in absolute G-code coordinates
my $last_pos = $gcodegen->last_pos->clone;
$last_pos->translate(@{$gcodegen->origin});
# represent $point in absolute G-code coordinates
$point = $point->clone;
my $origin = $gcodegen->origin;
$point->translate(map scale $_, @$origin);
$point->translate(@$scaled_origin);
# calculate path (external_mp uses G-code coordinates so we set a temporary null origin)
$gcodegen->set_origin(Slic3r::Pointf->new(0,0));
$gcode .= $self->_plan($gcodegen, $self->_external_mp, $point, $comment);
$gcodegen->set_origin($origin);
# calculate path
my $travel = $self->_external_mp->shortest_path($last_pos, $point);
# translate the path back into the shifted coordinate system that $gcodegen
# is currently using for writing coordinates
$travel->translate(@{$scaled_origin->negative});
return $travel;
} else {
$gcode .= $self->_plan($gcodegen, $self->_layer_mp, $point, $comment);
return $self->_layer_mp->shortest_path($gcodegen->last_pos, $point);
}
return $gcode;
}
sub _plan {
my ($self, $gcodegen, $mp, $point, $comment) = @_;
my $gcode = "";
my $travel = $mp->shortest_path($gcodegen->last_pos, $point);
# if the path is not contained in a single island we need to retract
$gcode .= $gcodegen->retract
if !$gcodegen->config->only_retract_when_crossing_perimeters
|| !$gcodegen->layer->any_internal_region_fill_surface_contains_polyline($travel);
# append the actual path and return
# use G1 because we rely on paths being straight (G0 may make round paths)
$gcode .= join '',
map $gcodegen->writer->travel_to_xy($gcodegen->point_to_gcode($_->b), $comment),
@{$travel->lines};
return $gcode;
}
1;

View File

@ -354,7 +354,12 @@ sub process_layer {
}
$self->_skirt_done->{$layer->print_z} = 1;
$self->_gcodegen->avoid_crossing_perimeters->use_external_mp(0);
$self->_gcodegen->avoid_crossing_perimeters->disable_once(1);
# allow a straight travel move to the first object point if this is the first layer
# (but don't in next layers)
if ($layer->id == 0) {
$self->_gcodegen->avoid_crossing_perimeters->disable_once(1);
}
}
# extrude brim
@ -366,6 +371,8 @@ sub process_layer {
for @{$self->print->brim};
$self->_brim_done(1);
$self->_gcodegen->avoid_crossing_perimeters->use_external_mp(0);
# allow a straight travel move to the first object point
$self->_gcodegen->avoid_crossing_perimeters->disable_once(1);
}

View File

@ -129,7 +129,7 @@ Layer::any_internal_region_slice_contains(const T &item) const
}
return false;
}
template bool Layer::any_internal_region_slice_contains<Line>(const Line &item) const;
template bool Layer::any_internal_region_slice_contains<Polyline>(const Polyline &item) const;
template <class T>
bool
@ -140,20 +140,7 @@ Layer::any_bottom_region_slice_contains(const T &item) const
}
return false;
}
template bool Layer::any_bottom_region_slice_contains<Line>(const Line &item) const;
template <class T>
bool
Layer::any_internal_region_fill_surface_contains(const T &item) const
{
FOREACH_LAYERREGION(this, layerm) {
if ((*layerm)->fill_surfaces.any_internal_contains(item)) return true;
}
return false;
}
template bool Layer::any_internal_region_fill_surface_contains<Line>(const Line &item) const;
template bool Layer::any_internal_region_fill_surface_contains<Polyline>(const Polyline &item) const;
template bool Layer::any_bottom_region_slice_contains<Polyline>(const Polyline &item) const;
#ifdef SLIC3RXS
REGISTER_CLASS(Layer, "Layer");

View File

@ -95,8 +95,7 @@ class Layer {
void make_slices();
template <class T> bool any_internal_region_slice_contains(const T &item) const;
template <class T> bool any_bottom_region_slice_contains(const T &item) const;
template <class T> bool any_internal_region_fill_surface_contains(const T &item) const;
protected:
int _id; // sequential number of layer, 0-based
PrintObject *_object;

View File

@ -77,7 +77,6 @@ SurfaceCollection::any_internal_contains(const T &item) const
}
return false;
}
template bool SurfaceCollection::any_internal_contains<Line>(const Line &item) const;
template bool SurfaceCollection::any_internal_contains<Polyline>(const Polyline &item) const;
template <class T>
@ -89,7 +88,7 @@ SurfaceCollection::any_bottom_contains(const T &item) const
}
return false;
}
template bool SurfaceCollection::any_bottom_contains<Line>(const Line &item) const;
template bool SurfaceCollection::any_bottom_contains<Polyline>(const Polyline &item) const;
SurfacesPtr
SurfaceCollection::filter_by_type(SurfaceType type)

View File

@ -69,14 +69,10 @@
%code%{ RETVAL = (int)(intptr_t)THIS; %};
void make_slices();
bool any_internal_region_slice_contains_line(Line* line)
%code%{ RETVAL = THIS->any_internal_region_slice_contains(*line); %};
bool any_bottom_region_slice_contains_line(Line* line)
%code%{ RETVAL = THIS->any_bottom_region_slice_contains(*line); %};
bool any_internal_region_fill_surface_contains_line(Line* line)
%code%{ RETVAL = THIS->any_internal_region_fill_surface_contains(*line); %};
bool any_internal_region_fill_surface_contains_polyline(Polyline* polyline)
%code%{ RETVAL = THIS->any_internal_region_fill_surface_contains(*polyline); %};
bool any_internal_region_slice_contains_polyline(Polyline* polyline)
%code%{ RETVAL = THIS->any_internal_region_slice_contains(*polyline); %};
bool any_bottom_region_slice_contains_polyline(Polyline* polyline)
%code%{ RETVAL = THIS->any_bottom_region_slice_contains(*polyline); %};
};
%name{Slic3r::Layer::Support} class SupportLayer {
@ -123,12 +119,8 @@
Ref<ExPolygonCollection> slices()
%code%{ RETVAL = &THIS->slices; %};
bool any_internal_region_slice_contains_line(Line* line)
%code%{ RETVAL = THIS->any_internal_region_slice_contains(*line); %};
bool any_bottom_region_slice_contains_line(Line* line)
%code%{ RETVAL = THIS->any_bottom_region_slice_contains(*line); %};
bool any_internal_region_fill_surface_contains_line(Line* line)
%code%{ RETVAL = THIS->any_internal_region_fill_surface_contains(*line); %};
bool any_internal_region_fill_surface_contains_polyline(Polyline* polyline)
%code%{ RETVAL = THIS->any_internal_region_fill_surface_contains(*polyline); %};
bool any_internal_region_slice_contains_polyline(Polyline* polyline)
%code%{ RETVAL = THIS->any_internal_region_slice_contains(*polyline); %};
bool any_bottom_region_slice_contains_polyline(Polyline* polyline)
%code%{ RETVAL = THIS->any_bottom_region_slice_contains(*polyline); %};
};