New --solid-infill-extruder option. Includes a refactoring of the strategy used to order layer extrusions according to their extruder and island; toolchanges and travel moves should be more optimized now. #618

This commit is contained in:
Alessandro Ranellucci 2014-12-17 00:34:00 +01:00
parent d9cffeca4a
commit e79aa2e81c
25 changed files with 186 additions and 95 deletions

View file

@ -225,28 +225,32 @@ sub make_fill {
my $mm3_per_mm = $flow->mm3_per_mm;
# save into layer
push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new;
$collection->no_sort($params->{no_sort});
{
my $role = $is_bridge ? EXTR_ROLE_BRIDGE
: $is_solid ? (($surface->surface_type == S_TYPE_TOP) ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL)
: EXTR_ROLE_FILL;
my $extrusion_height = $is_bridge ? $flow->width : $h;
push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new($role);
$collection->no_sort($params->{no_sort});
$collection->append(
map Slic3r::ExtrusionPath->new(
polyline => $_,
role => $role,
mm3_per_mm => $mm3_per_mm,
width => $flow->width,
height => $extrusion_height,
), @polylines,
);
}
$collection->append(
map Slic3r::ExtrusionPath->new(
polyline => $_,
role => ($is_bridge
? EXTR_ROLE_BRIDGE
: $is_solid
? (($surface->surface_type == S_TYPE_TOP) ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL)
: EXTR_ROLE_FILL),
mm3_per_mm => $mm3_per_mm,
width => $flow->width,
height => ($is_bridge ? $flow->width : $h),
), @polylines,
);
push @fills_ordering_points, $polylines[0]->first_point;
}
# add thin fill regions
foreach my $thin_fill (@{$layerm->thin_fills}) {
push @fills, Slic3r::ExtrusionPath::Collection->new($thin_fill);
push @fills, Slic3r::ExtrusionPath::Collection->new($thin_fill->role, $thin_fill);
push @fills_ordering_points, $thin_fill->first_point;
}

View file

@ -256,10 +256,10 @@ sub _extrude_path {
$acceleration = $self->config->first_layer_acceleration;
} elsif ($self->config->perimeter_acceleration && $path->is_perimeter) {
$acceleration = $self->config->perimeter_acceleration;
} elsif ($self->config->infill_acceleration && $path->is_fill) {
$acceleration = $self->config->infill_acceleration;
} elsif ($self->config->bridge_acceleration && $path->is_bridge) {
$acceleration = $self->config->bridge_acceleration;
} elsif ($self->config->infill_acceleration && $path->is_infill) {
$acceleration = $self->config->infill_acceleration;
} else {
$acceleration = $self->config->default_acceleration;
}

View file

@ -454,7 +454,8 @@ sub build {
complete_objects extruder_clearance_radius extruder_clearance_height
gcode_comments output_filename_format
post_process
perimeter_extruder infill_extruder support_material_extruder support_material_interface_extruder
perimeter_extruder infill_extruder solid_infill_extruder
support_material_extruder support_material_interface_extruder
ooze_prevention standby_temperature_delta
interface_shells
extrusion_width first_layer_extrusion_width perimeter_extrusion_width
@ -648,6 +649,7 @@ sub build {
my $optgroup = $page->new_optgroup('Extruders');
$optgroup->append_single_option_line('perimeter_extruder');
$optgroup->append_single_option_line('infill_extruder');
$optgroup->append_single_option_line('solid_infill_extruder');
$optgroup->append_single_option_line('support_material_extruder');
$optgroup->append_single_option_line('support_material_interface_extruder');
}

View file

@ -246,7 +246,7 @@ sub make_perimeters {
my ($polynodes, $depth, $is_contour) = @_;
# convert all polynodes to ExtrusionLoop objects
my $collection = Slic3r::ExtrusionPath::Collection->new;
my $collection = Slic3r::ExtrusionPath::Collection->new(EXTR_ROLE_PERIMETER); # temporary collection
my @children = ();
foreach my $polynode (@$polynodes) {
my $polygon = ($polynode->{outer} // $polynode->{hole})->clone;
@ -303,7 +303,7 @@ sub make_perimeters {
# (clone because the collection gets DESTROY'ed)
# We allow polyline reversal because Clipper may have randomly
# reversed polylines during clipping.
my $collection = Slic3r::ExtrusionPath::Collection->new(@paths);
my $collection = Slic3r::ExtrusionPath::Collection->new(EXTR_ROLE_PERIMETER, @paths); # temporary collection
@paths = map $_->clone, @{$collection->chained_path(0)};
} else {
push @paths, Slic3r::ExtrusionPath->new(

View file

@ -380,54 +380,87 @@ sub process_layer {
}
}
# tweak region ordering to save toolchanges
my @region_ids = 0 .. ($self->print->region_count-1);
if ($self->_gcodegen->writer->multiple_extruders) {
my $last_extruder_id = $self->_gcodegen->writer->extruder->id;
my $best_region_id = first { $self->print->regions->[$_]->config->perimeter_extruder-1 == $last_extruder_id } @region_ids;
@region_ids = ($best_region_id, grep $_ != $best_region_id, @region_ids) if $best_region_id;
# We now define a strategy for building perimeters and fills. The separation
# between regions doesn't matter in terms of printing order, as we follow
# another logic instead:
# - we group all extrusions by extruder so that we minimize toolchanges
# - we start from the last used extruder
# - for each extruder, we group extrusions by island
# - for each island, we extrude perimeters first, unless user set the infill_first
# option
# group extrusions by extruder and then by island
my %by_extruder = (); # extruder_id => [ { perimeters => \@perimeters, infill => \@infill } ]
foreach my $region_id (0..($self->print->region_count-1)) {
my $layerm = $layer->regions->[$region_id] or next;
my $region = $self->print->get_region($region_id);
# process perimeters
{
my $extruder_id = $region->config->perimeter_extruder-1;
foreach my $perimeter (@{$layerm->perimeters}) {
# init by_extruder item only if we actually use the extruder
$by_extruder{$extruder_id} //= [];
# $perimeter is an ExtrusionLoop or ExtrusionPath object
for my $i (0 .. $#{$layer->slices}) {
if ($i == $#{$layer->slices}
|| $layer->slices->[$i]->contour->contains_point($perimeter->first_point)) {
$by_extruder{$extruder_id}[$i] //= { perimeters => {} };
$by_extruder{$extruder_id}[$i]{perimeters}{$region_id} //= [];
push @{ $by_extruder{$extruder_id}[$i]{perimeters}{$region_id} }, $perimeter;
last;
}
}
}
}
# process infill
{
foreach my $fill (@{$layerm->fills}) {
# init by_extruder item only if we actually use the extruder
my $extruder_id = $fill->[0]->is_solid_infill
? $region->config->solid_infill_extruder-1
: $region->config->infill_extruder-1;
$by_extruder{$extruder_id} //= [];
# $fill is an ExtrusionPath::Collection object
for my $i (0 .. $#{$layer->slices}) {
if ($i == $#{$layer->slices}
|| $layer->slices->[$i]->contour->contains_point($fill->first_point)) {
$by_extruder{$extruder_id}[$i] //= { infill => {} };
$by_extruder{$extruder_id}[$i]{infill}{$region_id} //= [];
push @{ $by_extruder{$extruder_id}[$i]{infill}{$region_id} }, $fill;
last;
}
}
}
}
}
foreach my $region_id (@region_ids) {
my $layerm = $layer->regions->[$region_id] or next;
my $region = $self->print->regions->[$region_id];
$self->_gcodegen->config->apply_region_config($region->config);
# group extrusions by island
my @perimeters_by_island = map [], 0..$#{$layer->slices}; # slice idx => @perimeters
my @infill_by_island = map [], 0..$#{$layer->slices}; # slice idx => @fills
# NOTE: we assume $layer->slices was already ordered with chained_path()!
PERIMETER: foreach my $perimeter (@{$layerm->perimeters}) {
for my $i (0 .. $#{$layer->slices}-1) {
if ($layer->slices->[$i]->contour->contains_point($perimeter->first_point)) {
push @{ $perimeters_by_island[$i] }, $perimeter;
next PERIMETER;
}
}
push @{ $perimeters_by_island[-1] }, $perimeter; # optimization
# tweak extruder ordering to save toolchanges
my @extruders = sort keys %by_extruder;
if (@extruders > 1) {
my $last_extruder_id = $self->_gcodegen->writer->extruder->id;
if (exists $by_extruder{$last_extruder_id}) {
@extruders = (
$last_extruder_id,
grep $_ != $last_extruder_id, @extruders,
);
}
FILL: foreach my $fill (@{$layerm->fills}) {
for my $i (0 .. $#{$layer->slices}-1) {
if ($layer->slices->[$i]->contour->contains_point($fill->first_point)) {
push @{ $infill_by_island[$i] }, $fill;
next FILL;
}
}
push @{ $infill_by_island[-1] }, $fill; # optimization
}
for my $i (0 .. $#{$layer->slices}) {
# give priority to infill if we were already using its extruder and it wouldn't
# be good for perimeters
if ($self->print->config->infill_first
|| ($self->_gcodegen->writer->multiple_extruders && $region->config->infill_extruder-1 == $self->_gcodegen->writer->extruder->id && $region->config->infill_extruder != $region->config->perimeter_extruder)) {
$gcode .= $self->_extrude_infill($infill_by_island[$i], $region);
$gcode .= $self->_extrude_perimeters($perimeters_by_island[$i], $region);
}
foreach my $extruder_id (@extruders) {
$gcode .= $self->_gcodegen->set_extruder($extruder_id);
foreach my $island (@{ $by_extruder{$extruder_id} }) {
if ($self->print->config->infill_first) {
$gcode .= $self->_extrude_infill($island->{infill} // {});
$gcode .= $self->_extrude_perimeters($island->{perimeters} // {});
} else {
$gcode .= $self->_extrude_perimeters($perimeters_by_island[$i], $region);
$gcode .= $self->_extrude_infill($infill_by_island[$i], $region);
$gcode .= $self->_extrude_perimeters($island->{perimeters} // {});
$gcode .= $self->_extrude_infill($island->{infill} // {});
}
}
}
@ -452,31 +485,30 @@ sub process_layer {
}
sub _extrude_perimeters {
my $self = shift;
my ($island_perimeters, $region) = @_;
return "" if !@$island_perimeters;
my ($self, $entities_by_region) = @_;
my $gcode = "";
$gcode .= $self->_gcodegen->set_extruder($region->config->perimeter_extruder-1);
$gcode .= $self->_gcodegen->extrude($_, 'perimeter') for @$island_perimeters;
foreach my $region_id (sort keys %$entities_by_region) {
$self->_gcodegen->config->apply_region_config($self->print->get_region($region_id)->config);
$gcode .= $self->_gcodegen->extrude($_, 'perimeter')
for @{ $entities_by_region->{$region_id} };
}
return $gcode;
}
sub _extrude_infill {
my $self = shift;
my ($island_fills, $region) = @_;
return "" if !@$island_fills;
my ($self, $entities_by_region) = @_;
my $gcode = "";
$gcode .= $self->_gcodegen->set_extruder($region->config->infill_extruder-1);
for my $fill (@$island_fills) {
if ($fill->isa('Slic3r::ExtrusionPath::Collection')) {
$gcode .= $self->_gcodegen->extrude($_, 'fill')
for @{$fill->chained_path_from($self->_gcodegen->last_pos, 0)};
} else {
$gcode .= $self->_gcodegen->extrude($fill, 'fill') ;
foreach my $region_id (sort keys %$entities_by_region) {
$self->_gcodegen->config->apply_region_config($self->print->get_region($region_id)->config);
for my $fill (@{ $entities_by_region->{$region_id} }) {
if ($fill->isa('Slic3r::ExtrusionPath::Collection')) {
$gcode .= $self->_gcodegen->extrude($_, 'infill')
for @{$fill->chained_path_from($self->_gcodegen->last_pos, 0)};
} else {
$gcode .= $self->_gcodegen->extrude($fill, 'infill') ;
}
}
}
return $gcode;

View file

@ -1004,7 +1004,10 @@ sub combine_infill {
next unless $every > 1 && $region->config->fill_density > 0;
# limit the number of combined layers to the maximum height allowed by this regions' nozzle
my $nozzle_diameter = $self->print->config->get_at('nozzle_diameter', $region->config->infill_extruder-1);
my $nozzle_diameter = min(
$self->print->config->get_at('nozzle_diameter', $region->config->infill_extruder-1),
$self->print->config->get_at('nozzle_diameter', $region->config->solid_infill_extruder-1),
);
# define the combinations
my %combine = (); # layer_idx => number of additional combined lower layers

View file

@ -262,7 +262,7 @@ sub contact_area {
{
# get the average nozzle diameter used on this layer
my @nozzle_diameters = map $self->print_config->get_at('nozzle_diameter', $_),
map { $_->config->perimeter_extruder-1, $_->config->infill_extruder-1 }
map { $_->config->perimeter_extruder-1, $_->config->infill_extruder-1, $_->config->solid_infill_extruder-1 }
@{$layer->regions};
my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters;