Ported PrintObject::combine_infill() to C++

This commit is contained in:
Alessandro Ranellucci 2018-11-26 13:57:31 +01:00
parent 450042a60c
commit 2bbb089a4f
4 changed files with 73 additions and 178 deletions

View File

@ -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; 1;

View File

@ -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); Pointf circle_taubin_newton(const Pointfs::const_iterator& input_start, const Pointfs::const_iterator& input_end, size_t cycles = 20);
/// Epsilon value /// Epsilon value
// FIXME: this is a duplicate from libslic3r.h
constexpr double epsilon { 1e-4 }; constexpr double epsilon { 1e-4 };
constexpr coord_t scaled_epsilon { static_cast<coord_t>(epsilon / SCALING_FACTOR) }; constexpr coord_t scaled_epsilon { static_cast<coord_t>(epsilon / SCALING_FACTOR) };

View File

@ -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 void
PrintObject::combine_infill() PrintObject::combine_infill()
{ {
// Work on each region separately. // Work on each region separately.
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
const PrintRegion *region = this->print()->regions[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.) if (every < 2 || region->config.fill_density == 0.)
continue; continue;
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle. // Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
//FIXME limit the layer height to max_layer_height // FIXME: limit the layer height to max_layer_height
double nozzle_diameter = std::min( 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.infill_extruder.value - 1),
this->print()->config.nozzle_diameter.get_at(region->config.solid_infill_extruder.value - 1)); this->_print->config.nozzle_diameter.get_at(region->config.solid_infill_extruder.value - 1)
);
// define the combinations // define the combinations
std::vector<size_t> combine(this->layers.size(), 0); std::vector<size_t> combine(this->layers.size(), 0); // layer_idx => number of additional combined lower layers
{ {
double current_height = 0.; double current_height = 0.;
size_t num_layers = 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]; 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) if (layer->id() == 0)
// Skip first print layer (which may not be first layer in array because of raft).
continue; continue;
// Check whether the combination of this layer with the lower layers' buffer // Check whether the combination of this layer with the lower layers' buffer
// would exceed max layer height or max combined layer count. // would exceed max layer height or max combined layer count.
if (current_height + layer->height >= nozzle_diameter + EPSILON || (every < 0 || num_layers >= static_cast<size_t>(every)) ) { if (current_height + layer->height >= nozzle_diameter + EPSILON || num_layers >= static_cast<size_t>(every) ) {
// Append combination to lower layer. // Append combination to lower layer.
combine[layer_idx - 1] = num_layers; combine[layer_idx - 1] = num_layers;
current_height = 0.; current_height = 0.;
num_layers = 0; num_layers = 0;
} }
current_height += layer->height; current_height += layer->height;
++ num_layers; ++num_layers;
} }
// Append lower layers (if any) to uppermost layer. // 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 // loop through layers to which we have assigned layers to combine
for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) { for (size_t layer_idx = 0; layer_idx < combine.size(); ++layer_idx) {
size_t num_layers = combine[layer_idx]; const size_t& num_layers = combine[layer_idx];
if (num_layers <= 1) if (num_layers <= 1)
continue; continue;
// Get all the LayerRegion objects to be combined. // Get all the LayerRegion objects to be combined.
std::vector<LayerRegion*> layerms; std::vector<LayerRegion*> layerms;
layerms.reserve(num_layers); layerms.reserve(num_layers);
for (size_t i = layer_idx + 1 - num_layers; i <= layer_idx; ++ i) for (size_t i = layer_idx + 1 - num_layers; i <= layer_idx; ++i)
layerms.emplace_back(this->layers[i]->regions[region_id]); layerms.push_back(this->layers[i]->regions[region_id]);
// We need to perform a multi-layer intersection, so let's split it in pairs. // 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. // Initialize the intersection with the candidates of the lowest layer.
ExPolygons intersection = to_expolygons(layerms.front()->fill_surfaces.filter_by_type(stInternal)); 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. // 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( intersection = intersection_ex(
to_polygons(intersection), to_polygons(intersection),
to_polygons(layerms[i]->fill_surfaces.filter_by_type(stInternal)), 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.) // Remove ExPolygons whose area is <= infill_area_threshold()
intersection.erase(std::remove_if(intersection.begin(), intersection.end(), const double area_threshold = layerms.front()->infill_area_threshold();
[area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), intersection.erase(std::remove_if(intersection.begin(), intersection.end(),
intersection.end()); [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }),
intersection.end());
if (intersection.empty()) if (intersection.empty())
continue; continue;
// Slic3r::debugf " combining %d %s regions from layers %d-%d\n",
// scalar(@$intersection), #ifdef SLIC3R_DEBUG
// ($type == S_TYPE_INTERNAL ? 'internal' : 'internal-solid'), std::cout << " combining " << intersection.size()
// $layer_idx-($every-1), $layer_idx; << " 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, // intersection now contains the regions that can be combined across the full amount of layers,
// so let's remove those areas from all 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; Polygons intersection_with_clearance;
intersection_with_clearance.reserve(intersection.size()); intersection_with_clearance.reserve(intersection.size());
float clearance_offset = for (const ExPolygon &expoly : intersection)
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)
polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); polygons_append(intersection_with_clearance, offset(expoly, clearance_offset));
for (LayerRegion *layerm : layerms) { 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.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()) { if (layerm == layerms.back()) {
// Apply surfaces back with adjusted depth to the uppermost layer. // Apply surfaces back with adjusted depth to the uppermost layer.
Surface templ(stInternal, ExPolygon()); Surface templ(stInternal, ExPolygon());
templ.thickness = 0.; templ.thickness = 0.;
for (LayerRegion *layerm2 : layerms) for (const LayerRegion *layerm2 : layerms)
templ.thickness += layerm2->layer()->height; templ.thickness += layerm2->layer()->height;
templ.thickness_layers = (unsigned short)layerms.size(); templ.thickness_layers = (unsigned short)layerms.size();
layerm->fill_surfaces.append(intersection, templ); layerm->fill_surfaces.append(intersection, templ);
} else { } else {
// Save void surfaces. // Save void surfaces.
layerm->fill_surfaces.append( layerm->fill_surfaces.append(
intersection_ex(internal, intersection_with_clearance, false), intersection_ex(internal, intersection_with_clearance),
stInternalVoid); stInternalVoid
);
} }
} }
} }
@ -1624,8 +1650,9 @@ PrintObject::clip_fill_surfaces()
// get our current internal fill boundaries // get our current internal fill boundaries
Polygons lower_layer_internal_surfaces; Polygons lower_layer_internal_surfaces;
for (const auto* layerm : lower_layer->regions) 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, to_polygons(
polygons_append(lower_layer_internal_surfaces, *s); layerm->fill_surfaces.filter_by_type({ stInternal, stInternalVoid })
));
upper_internal = intersection(overhangs, lower_layer_internal_surfaces); upper_internal = intersection(overhangs, lower_layer_internal_surfaces);
} }
@ -1634,10 +1661,7 @@ PrintObject::clip_fill_surfaces()
if (layerm->region()->config.fill_density.value == 0) if (layerm->region()->config.fill_density.value == 0)
continue; continue;
Polygons internal; Polygons internal{ to_polygons(layerm->fill_surfaces.filter_by_type({ stInternal, stInternalVoid })) };
for (const auto* s : layerm->fill_surfaces.filter_by_type({ stInternal, stInternalVoid }))
polygons_append(internal, *s);
layerm->fill_surfaces.remove_types({ stInternal, stInternalVoid }); layerm->fill_surfaces.remove_types({ stInternal, stInternalVoid });
layerm->fill_surfaces.append(intersection_ex(internal, upper_internal, true), stInternal); layerm->fill_surfaces.append(intersection_ex(internal, upper_internal, true), stInternal);
layerm->fill_surfaces.append(diff_ex (internal, upper_internal, true), stInternalVoid); layerm->fill_surfaces.append(diff_ex (internal, upper_internal, true), stInternalVoid);

View File

@ -129,6 +129,7 @@ _constant()
%name{_detect_surfaces_type} void detect_surfaces_type(); %name{_detect_surfaces_type} void detect_surfaces_type();
void process_external_surfaces(); void process_external_surfaces();
void bridge_over_infill(); void bridge_over_infill();
void combine_infill();
void discover_horizontal_shells(); void discover_horizontal_shells();
void clip_fill_surfaces(); void clip_fill_surfaces();
void _slice(); void _slice();