Ported PrintObject::clip_fill_surfaces() to C++

This commit is contained in:
Alessandro Ranellucci 2018-11-25 23:59:33 +01:00
parent a4d8d20c7a
commit e5bd2ef801
5 changed files with 65 additions and 149 deletions

View File

@ -252,111 +252,6 @@ sub _support_material {
);
}
# 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 clip_fill_surfaces {
my $self = shift;
return unless $self->config->infill_only_where_needed
&& any { $_->config->fill_density > 0 } @{$self->print->regions};
# We only want infill under ceilings; this is almost like an
# internal support material.
# proceed top-down skipping bottom layer
my $upper_internal = [];
for my $layer_id (reverse 1..($self->layer_count - 1)) {
my $layer = $self->get_layer($layer_id);
my $lower_layer = $self->get_layer($layer_id-1);
# detect things that we need to support
my $overhangs = []; # Polygons
# we need to support any solid surface
push @$overhangs, map $_->p,
grep $_->is_solid, map @{$_->fill_surfaces}, @{$layer->regions};
# we also need to support perimeters when there's at least one full
# unsupported loop
{
# get perimeters area as the difference between slices and fill_surfaces
my $perimeters = diff(
[ map @$_, @{$layer->slices} ],
[ map $_->p, map @{$_->fill_surfaces}, @{$layer->regions} ],
);
# only consider the area that is not supported by lower perimeters
$perimeters = intersection(
$perimeters,
[ map $_->p, map @{$_->fill_surfaces}, @{$lower_layer->regions} ],
1,
);
# only consider perimeter areas that are at least one extrusion width thick
my $pw = min(map $_->flow(FLOW_ROLE_PERIMETER)->scaled_width, @{$layer->regions});
$perimeters = offset2($perimeters, -$pw, +$pw);
# append such thick perimeters to the areas that need support
push @$overhangs, @$perimeters;
}
# find new internal infill
$upper_internal = my $new_internal = intersection(
[
@$overhangs,
@$upper_internal,
],
[
# our current internal fill boundaries
map $_->p,
grep $_->surface_type == S_TYPE_INTERNAL || $_->surface_type == S_TYPE_INTERNALVOID,
map @{$_->fill_surfaces}, @{$lower_layer->regions}
],
);
# apply new internal infill to regions
foreach my $layerm (@{$lower_layer->regions}) {
next if $layerm->region->config->fill_density == 0;
my (@internal, @other) = ();
foreach my $surface (map $_->clone, @{$layerm->fill_surfaces}) {
if ($surface->surface_type == S_TYPE_INTERNAL || $surface->surface_type == S_TYPE_INTERNALVOID) {
push @internal, $surface;
} else {
push @other, $surface;
}
}
my @new = map Slic3r::Surface->new(
expolygon => $_,
surface_type => S_TYPE_INTERNAL,
),
@{intersection_ex(
[ map $_->p, @internal ],
$new_internal,
1,
)};
push @other, map Slic3r::Surface->new(
expolygon => $_,
surface_type => S_TYPE_INTERNALVOID,
),
@{diff_ex(
[ map $_->p, @internal ],
$new_internal,
1,
)};
# If there are voids it means that our internal infill is not adjacent to
# perimeters. In this case it would be nice to add a loop around infill to
# make it more robust and nicer. TODO.
$layerm->fill_surfaces->clear;
$layerm->fill_surfaces->append($_) for (@new, @other);
}
}
}
sub discover_horizontal_shells {
my $self = shift;

View File

@ -1550,6 +1550,8 @@ PrintObject::_discover_neighbor_horizontal_shells(LayerRegion* layerm, const siz
}
}
// 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::clip_fill_surfaces()
{
@ -1562,62 +1564,73 @@ PrintObject::clip_fill_surfaces()
// internal support material.
// Proceed top-down, skipping the bottom layer.
Polygons upper_internal;
for (int layer_id = int(this->layers.size()) - 1; layer_id > 0; -- layer_id) {
Layer *layer = this->layers[layer_id];
for (int layer_id = int(this->layers.size()) - 1; layer_id > 0; --layer_id) {
const Layer *layer = this->layers[layer_id];
Layer *lower_layer = this->layers[layer_id - 1];
// Detect things that we need to support.
// Cummulative slices.
Polygons slices;
for (const ExPolygon &expoly : layer->slices.expolygons)
polygons_append(slices, to_polygons(expoly));
// Cummulative fill surfaces.
Polygons fill_surfaces;
// Solid surfaces to be supported.
Polygons overhangs;
for (const LayerRegion *layerm : layer->regions)
for (const LayerRegion *layerm : layer->regions) {
for (const Surface &surface : layerm->fill_surfaces.surfaces) {
Polygons polygons = to_polygons(surface.expolygon);
if (surface.is_solid())
polygons_append(overhangs, polygons);
polygons_append(fill_surfaces, std::move(polygons));
}
Polygons lower_layer_fill_surfaces;
Polygons lower_layer_internal_surfaces;
for (const LayerRegion *layerm : lower_layer->regions)
for (const Surface &surface : layerm->fill_surfaces.surfaces) {
Polygons polygons = to_polygons(surface.expolygon);
if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
polygons_append(lower_layer_internal_surfaces, polygons);
polygons_append(lower_layer_fill_surfaces, std::move(polygons));
//polygons_append(fill_surfaces, std::move(polygons));
}
}
// We also need to support perimeters when there's at least one full unsupported loop
{
// Get perimeters area as the difference between slices and fill_surfaces
Polygons fill_surfaces;
for (const LayerRegion *layerm : layer->regions)
polygons_append(fill_surfaces, (Polygons)layerm->fill_surfaces);
Polygons perimeters = diff(layer->slices, fill_surfaces);
// Only consider the area that is not supported by lower perimeters
Polygons perimeters = intersection(diff(slices, fill_surfaces), lower_layer_fill_surfaces);
Polygons lower_layer_fill_surfaces;
for (const LayerRegion *layerm : lower_layer->regions)
polygons_append(lower_layer_fill_surfaces, (Polygons)layerm->fill_surfaces);
perimeters = intersection(perimeters, lower_layer_fill_surfaces, true);
// Only consider perimeter areas that are at least one extrusion width thick.
//FIXME Offset2 eats out from both sides, while the perimeters are create outside in.
//Should the pw not be half of the current value?
float pw = FLT_MAX;
for (const LayerRegion *layerm : layer->regions)
pw = std::min<float>(pw, layerm->flow(frPerimeter).scaled_width());
perimeters = offset2(perimeters, -pw, +pw);
// Append such thick perimeters to the areas that need support
polygons_append(overhangs, offset2(perimeters, -pw, +pw));
polygons_append(overhangs, perimeters);
}
// Find new internal infill.
polygons_append(overhangs, std::move(upper_internal));
upper_internal = intersection(overhangs, lower_layer_internal_surfaces);
{
polygons_append(overhangs, std::move(upper_internal));
// 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);
upper_internal = intersection(overhangs, lower_layer_internal_surfaces);
}
// Apply new internal infill to regions.
for (LayerRegion *layerm : lower_layer->regions) {
for (auto* layerm : lower_layer->regions) {
if (layerm->region()->config.fill_density.value == 0)
continue;
Polygons internal;
for (Surface &surface : layerm->fill_surfaces.surfaces)
if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
polygons_append(internal, std::move(surface.expolygon));
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.append(intersection_ex(internal, upper_internal, true), stInternal);
layerm->fill_surfaces.append(diff_ex (internal, upper_internal, true), stInternalVoid);
// If there are voids it means that our internal infill is not adjacent to
// perimeters. In this case it would be nice to add a loop around infill to
// make it more robust and nicer. TODO.

View File

@ -90,29 +90,30 @@ SurfaceCollection::any_bottom_contains(const T &item) const
}
template bool SurfaceCollection::any_bottom_contains<Polyline>(const Polyline &item) const;
SurfacesPtr
SurfaceCollection::filter_by_type(SurfaceType type)
{
SurfacesPtr ss;
for (Surfaces::iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
if (surface->surface_type == type) ss.push_back(&*surface);
}
return ss;
}
SurfacesPtr
SurfaceCollection::filter_by_type(std::initializer_list<SurfaceType> types)
{
size_t n {0};
SurfacesPtr ss;
for (const auto& t : types) {
for (const auto& t : types)
n |= t;
}
for (Surfaces::iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
if ((surface->surface_type & n) == surface->surface_type) ss.push_back(&*surface);
}
SurfacesPtr ss;
for (auto& s : this->surfaces)
if ((s.surface_type & n) == s.surface_type) ss.push_back(&s);
return ss;
}
SurfacesConstPtr
SurfaceCollection::filter_by_type(std::initializer_list<SurfaceType> types) const
{
size_t n {0};
for (const auto& t : types)
n |= t;
SurfacesConstPtr ss;
for (auto& s : this->surfaces)
if ((s.surface_type & n) == s.surface_type) ss.push_back(&s);
return ss;
}
void
SurfaceCollection::filter_by_type(SurfaceType type, Polygons* polygons)
{

View File

@ -21,9 +21,15 @@ class SurfaceCollection
void group(std::vector<SurfacesConstPtr> *retval) const;
template <class T> bool any_internal_contains(const T &item) const;
template <class T> bool any_bottom_contains(const T &item) const;
SurfacesPtr filter_by_type(SurfaceType type);
SurfacesPtr filter_by_type(SurfaceType type) {
return this->filter_by_type({ type });
};
SurfacesPtr filter_by_type(std::initializer_list<SurfaceType> types);
void filter_by_type(SurfaceType type, Polygons* polygons);
SurfacesConstPtr filter_by_type(SurfaceType type) const {
return this->filter_by_type({ type });
};
SurfacesConstPtr filter_by_type(std::initializer_list<SurfaceType> types) const;
/// deletes all surfaces that match the supplied type.
void remove_type(const SurfaceType type);

View File

@ -129,6 +129,7 @@ _constant()
%name{_detect_surfaces_type} void detect_surfaces_type();
void process_external_surfaces();
void bridge_over_infill();
void clip_fill_surfaces();
void _slice();
SV* _slice_region(size_t region_id, std::vector<double> z, bool modifier)
%code%{