diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 08b6355f5..f7c120ca0 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -252,135 +252,4 @@ sub _support_material { ); } -# combine fill surfaces across layers -# Idempotence of this method is guaranteed by the fact that we don't remove things from -# fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. -sub combine_infill { - my $self = shift; - - # define the type used for voids - my %voidtype = ( - &S_TYPE_INTERNAL() => S_TYPE_INTERNALVOID, - ); - - # work on each region separately - for my $region_id (0 .. ($self->print->region_count-1)) { - my $region = $self->print->get_region($region_id); - my $every = $region->config->infill_every_layers; - 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 = 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 - { - my $current_height = my $layers = 0; - for my $layer_idx (0 .. ($self->layer_count-1)) { - my $layer = $self->get_layer($layer_idx); - next if $layer->id == 0; # skip first print layer (which may not be first layer in array because of raft) - my $height = $layer->height; - - # check whether the combination of this layer with the lower layers' buffer - # would exceed max layer height or max combined layer count - if ($current_height + $height >= $nozzle_diameter + epsilon || $layers >= $every) { - # append combination to lower layer - $combine{$layer_idx-1} = $layers; - $current_height = $layers = 0; - } - - $current_height += $height; - $layers++; - } - - # append lower layers (if any) to uppermost layer - $combine{$self->layer_count-1} = $layers; - } - - # loop through layers to which we have assigned layers to combine - for my $layer_idx (sort keys %combine) { - next unless $combine{$layer_idx} > 1; - - # get all the LayerRegion objects to be combined - my @layerms = map $self->get_layer($_)->get_region($region_id), - ($layer_idx - ($combine{$layer_idx}-1) .. $layer_idx); - - # only combine internal infill - for my $type (S_TYPE_INTERNAL) { - # we need to perform a multi-layer intersection, so let's split it in pairs - - # initialize the intersection with the candidates of the lowest layer - my $intersection = [ map $_->expolygon, @{$layerms[0]->fill_surfaces->filter_by_type($type)} ]; - - # start looping from the second layer and intersect the current intersection with it - for my $layerm (@layerms[1 .. $#layerms]) { - $intersection = intersection_ex( - [ map @$_, @$intersection ], - [ map @{$_->expolygon}, @{$layerm->fill_surfaces->filter_by_type($type)} ], - ); - } - - my $area_threshold = $layerms[0]->infill_area_threshold; - @$intersection = grep $_->area > $area_threshold, @$intersection; - next if !@$intersection; - Slic3r::debugf " combining %d %s regions from layers %d-%d\n", - scalar(@$intersection), - ($type == S_TYPE_INTERNAL ? 'internal' : 'internal-solid'), - $layer_idx-($every-1), $layer_idx; - - # $intersection now contains the regions that can be combined across the full amount of layers - # so let's remove those areas from all layers - - my @intersection_with_clearance = map @{$_->offset( - $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width / 2 - + $layerms[-1]->flow(FLOW_ROLE_PERIMETER)->scaled_width / 2 - # Because fill areas for rectilinear and honeycomb are grown - # later to overlap perimeters, we need to counteract that too. - + (($type == S_TYPE_INTERNALSOLID || $region->config->fill_pattern =~ /(rectilinear|grid|line|honeycomb)/) - ? $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width - : 0) - )}, @$intersection; - - - foreach my $layerm (@layerms) { - my @this_type = @{$layerm->fill_surfaces->filter_by_type($type)}; - my @other_types = map $_->clone, grep $_->surface_type != $type, @{$layerm->fill_surfaces}; - - my @new_this_type = map Slic3r::Surface->new(expolygon => $_, surface_type => $type), - @{diff_ex( - [ map $_->p, @this_type ], - [ @intersection_with_clearance ], - )}; - - # apply surfaces back with adjusted depth to the uppermost layer - if ($layerm->layer->id == $self->get_layer($layer_idx)->id) { - push @new_this_type, - map Slic3r::Surface->new( - expolygon => $_, - surface_type => $type, - thickness => sum(map $_->layer->height, @layerms), - thickness_layers => scalar(@layerms), - ), - @$intersection; - } else { - # save void surfaces - push @new_this_type, - map Slic3r::Surface->new(expolygon => $_, surface_type => $voidtype{$type}), - @{intersection_ex( - [ map @{$_->expolygon}, @this_type ], - [ @intersection_with_clearance ], - )}; - } - - $layerm->fill_surfaces->clear; - $layerm->fill_surfaces->append($_) for (@new_this_type, @other_types); - } - } - } - } -} - 1; diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index c2c0d66da..9d4579389 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -34,6 +34,7 @@ Pointf circle_taubin_newton(const Pointfs& input, size_t cycles = 20); Pointf circle_taubin_newton(const Pointfs::const_iterator& input_start, const Pointfs::const_iterator& input_end, size_t cycles = 20); /// Epsilon value +// FIXME: this is a duplicate from libslic3r.h constexpr double epsilon { 1e-4 }; constexpr coord_t scaled_epsilon { static_cast(epsilon / SCALING_FACTOR) }; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 74f36099e..f401d143d 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -1175,40 +1175,48 @@ PrintObject::prepare_infill() } +// combine fill surfaces across layers +// Idempotence of this method is guaranteed by the fact that we don't remove things from +// fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. void PrintObject::combine_infill() { // Work on each region separately. for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { const PrintRegion *region = this->print()->regions[region_id]; - const int every = region->config.infill_every_layers.value; + const int every = region->config.infill_every_layers(); if (every < 2 || region->config.fill_density == 0.) continue; + // Limit the number of combined layers to the maximum height allowed by this regions' nozzle. - //FIXME limit the layer height to max_layer_height - double nozzle_diameter = std::min( - this->print()->config.nozzle_diameter.get_at(region->config.infill_extruder.value - 1), - this->print()->config.nozzle_diameter.get_at(region->config.solid_infill_extruder.value - 1)); + // FIXME: limit the layer height to max_layer_height + const double nozzle_diameter = std::min( + this->_print->config.nozzle_diameter.get_at(region->config.infill_extruder.value - 1), + this->_print->config.nozzle_diameter.get_at(region->config.solid_infill_extruder.value - 1) + ); + // define the combinations - std::vector combine(this->layers.size(), 0); + std::vector combine(this->layers.size(), 0); // layer_idx => number of additional combined lower layers { double current_height = 0.; size_t num_layers = 0; - for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) { + for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++layer_idx) { const Layer *layer = this->layers[layer_idx]; + + // Skip first print layer (which may not be first layer in array because of raft). if (layer->id() == 0) - // Skip first print layer (which may not be first layer in array because of raft). continue; + // Check whether the combination of this layer with the lower layers' buffer // would exceed max layer height or max combined layer count. - if (current_height + layer->height >= nozzle_diameter + EPSILON || (every < 0 || num_layers >= static_cast(every)) ) { + if (current_height + layer->height >= nozzle_diameter + EPSILON || num_layers >= static_cast(every) ) { // Append combination to lower layer. combine[layer_idx - 1] = num_layers; current_height = 0.; num_layers = 0; } current_height += layer->height; - ++ num_layers; + ++num_layers; } // Append lower layers (if any) to uppermost layer. @@ -1216,66 +1224,84 @@ PrintObject::combine_infill() } // loop through layers to which we have assigned layers to combine - for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) { - size_t num_layers = combine[layer_idx]; + for (size_t layer_idx = 0; layer_idx < combine.size(); ++layer_idx) { + const size_t& num_layers = combine[layer_idx]; if (num_layers <= 1) continue; + // Get all the LayerRegion objects to be combined. std::vector layerms; layerms.reserve(num_layers); - for (size_t i = layer_idx + 1 - num_layers; i <= layer_idx; ++ i) - layerms.emplace_back(this->layers[i]->regions[region_id]); + for (size_t i = layer_idx + 1 - num_layers; i <= layer_idx; ++i) + layerms.push_back(this->layers[i]->regions[region_id]); + // We need to perform a multi-layer intersection, so let's split it in pairs. + // Initialize the intersection with the candidates of the lowest layer. ExPolygons intersection = to_expolygons(layerms.front()->fill_surfaces.filter_by_type(stInternal)); + // Start looping from the second layer and intersect the current intersection with it. - for (size_t i = 1; i < layerms.size(); ++ i) + for (size_t i = 1; i < layerms.size(); ++i) intersection = intersection_ex( - to_polygons(intersection), - to_polygons(layerms[i]->fill_surfaces.filter_by_type(stInternal)), - false); - double area_threshold = layerms.front()->infill_area_threshold(); - if (! intersection.empty() && area_threshold > 0.) - intersection.erase(std::remove_if(intersection.begin(), intersection.end(), - [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), - intersection.end()); + to_polygons(intersection), + to_polygons(layerms[i]->fill_surfaces.filter_by_type(stInternal)) + ); + + // Remove ExPolygons whose area is <= infill_area_threshold() + const double area_threshold = layerms.front()->infill_area_threshold(); + intersection.erase(std::remove_if(intersection.begin(), intersection.end(), + [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), + intersection.end()); + if (intersection.empty()) continue; - // Slic3r::debugf " combining %d %s regions from layers %d-%d\n", - // scalar(@$intersection), - // ($type == S_TYPE_INTERNAL ? 'internal' : 'internal-solid'), - // $layer_idx-($every-1), $layer_idx; + + #ifdef SLIC3R_DEBUG + std::cout << " combining " << intersection.size() + << " internal regions from layers " << (layer_idx-(every-1)) + << "-" << layer_idx << std::endl; + #endif + // intersection now contains the regions that can be combined across the full amount of layers, // so let's remove those areas from all layers. + + const float clearance_offset = + 0.5f * layerms.back()->flow(frPerimeter).scaled_width() + + // Because fill areas for rectilinear and honeycomb are grown + // later to overlap perimeters, we need to counteract that too. + ((region->config.fill_pattern == ipRectilinear || + region->config.fill_pattern == ipGrid || + region->config.fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) + * layerms.back()->flow(frSolidInfill).scaled_width(); + Polygons intersection_with_clearance; intersection_with_clearance.reserve(intersection.size()); - float clearance_offset = - 0.5f * layerms.back()->flow(frPerimeter).scaled_width() + - // Because fill areas for rectilinear and honeycomb are grown - // later to overlap perimeters, we need to counteract that too. - ((region->config.fill_pattern == ipRectilinear || - region->config.fill_pattern == ipGrid || - region->config.fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * - layerms.back()->flow(frSolidInfill).scaled_width(); - for (ExPolygon &expoly : intersection) + for (const ExPolygon &expoly : intersection) polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); + for (LayerRegion *layerm : layerms) { - Polygons internal = to_polygons(layerm->fill_surfaces.filter_by_type(stInternal)); + const Polygons internal = to_polygons(layerm->fill_surfaces.filter_by_type(stInternal)); layerm->fill_surfaces.remove_type(stInternal); - layerm->fill_surfaces.append(diff_ex(internal, intersection_with_clearance, false), stInternal); + + layerm->fill_surfaces.append( + diff_ex(internal, intersection_with_clearance), + stInternal + ); + if (layerm == layerms.back()) { // Apply surfaces back with adjusted depth to the uppermost layer. Surface templ(stInternal, ExPolygon()); templ.thickness = 0.; - for (LayerRegion *layerm2 : layerms) + for (const LayerRegion *layerm2 : layerms) templ.thickness += layerm2->layer()->height; templ.thickness_layers = (unsigned short)layerms.size(); layerm->fill_surfaces.append(intersection, templ); } else { // Save void surfaces. layerm->fill_surfaces.append( - intersection_ex(internal, intersection_with_clearance, false), - stInternalVoid); + intersection_ex(internal, intersection_with_clearance), + stInternalVoid + ); } } } @@ -1624,8 +1650,9 @@ PrintObject::clip_fill_surfaces() // get our current internal fill boundaries Polygons lower_layer_internal_surfaces; for (const auto* layerm : lower_layer->regions) - for (const auto* s : layerm->fill_surfaces.filter_by_type({ stInternal, stInternalVoid })) - polygons_append(lower_layer_internal_surfaces, *s); + polygons_append(lower_layer_internal_surfaces, to_polygons( + layerm->fill_surfaces.filter_by_type({ stInternal, stInternalVoid }) + )); upper_internal = intersection(overhangs, lower_layer_internal_surfaces); } @@ -1634,10 +1661,7 @@ PrintObject::clip_fill_surfaces() if (layerm->region()->config.fill_density.value == 0) continue; - Polygons internal; - for (const auto* s : layerm->fill_surfaces.filter_by_type({ stInternal, stInternalVoid })) - polygons_append(internal, *s); - + Polygons internal{ to_polygons(layerm->fill_surfaces.filter_by_type({ stInternal, stInternalVoid })) }; layerm->fill_surfaces.remove_types({ stInternal, stInternalVoid }); layerm->fill_surfaces.append(intersection_ex(internal, upper_internal, true), stInternal); layerm->fill_surfaces.append(diff_ex (internal, upper_internal, true), stInternalVoid); diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 40d59837a..c2ec2d813 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -129,6 +129,7 @@ _constant() %name{_detect_surfaces_type} void detect_surfaces_type(); void process_external_surfaces(); void bridge_over_infill(); + void combine_infill(); void discover_horizontal_shells(); void clip_fill_surfaces(); void _slice();