From d47f6d30af1e9c6d03583bd9ce969c40ae3c5794 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Nov 2016 16:58:23 +0100 Subject: [PATCH] Ported detect_surfaces_type() to XS/C++ --- lib/Slic3r/Print/Object.pm | 141 --------------------- xs/src/libslic3r/Layer.cpp | 6 - xs/src/libslic3r/Layer.hpp | 3 +- xs/src/libslic3r/Print.cpp | 6 - xs/src/libslic3r/Print.hpp | 4 +- xs/src/libslic3r/PrintObject.cpp | 164 +++++++++++++++++++++++++ xs/src/libslic3r/SurfaceCollection.hpp | 2 + xs/xsp/Print.xsp | 1 + 8 files changed, 172 insertions(+), 155 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 30f684034f..75c9877ef6 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -467,147 +467,6 @@ sub _support_material { ); } -sub detect_surfaces_type { - my $self = shift; - Slic3r::debugf "Detecting solid surfaces...\n"; - - for my $region_id (0 .. ($self->print->region_count-1)) { - for my $i (0 .. ($self->layer_count - 1)) { - my $layerm = $self->get_layer($i)->regions->[$region_id]; - - # prepare a reusable subroutine to make surface differences - my $difference = sub { - my ($subject, $clip, $result_type) = @_; - my $diff = diff( - [ map @$_, @$subject ], - [ map @$_, @$clip ], - 1, - ); - - # collapse very narrow parts (using the safety offset in the diff is not enough) - my $offset = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width / 10; - return map Slic3r::Surface->new(expolygon => $_, surface_type => $result_type), - @{ offset2_ex($diff, -$offset, +$offset) }; - }; - - # comparison happens against the *full* slices (considering all regions) - # unless internal shells are requested - my $upper_layer = $i < $self->layer_count - 1 ? $self->get_layer($i+1) : undef; - my $lower_layer = $i > 0 ? $self->get_layer($i-1) : undef; - - # find top surfaces (difference between current surfaces - # of current layer and upper one) - my @top = (); - if ($upper_layer) { - my $upper_slices = $self->config->interface_shells - ? [ map $_->expolygon, @{$upper_layer->regions->[$region_id]->slices} ] - : $upper_layer->slices; - - @top = $difference->( - [ map $_->expolygon, @{$layerm->slices} ], - $upper_slices, - S_TYPE_TOP, - ); - } else { - # if no upper layer, all surfaces of this one are solid - # we clone surfaces because we're going to clear the slices collection - @top = map $_->clone, @{$layerm->slices}; - $_->surface_type(S_TYPE_TOP) for @top; - } - - # find bottom surfaces (difference between current surfaces - # of current layer and lower one) - my @bottom = (); - if ($lower_layer) { - # any surface lying on the void is a true bottom bridge - push @bottom, $difference->( - [ map $_->expolygon, @{$layerm->slices} ], - $lower_layer->slices, - S_TYPE_BOTTOMBRIDGE, - ); - - # if we have soluble support material, don't bridge - if ($self->config->support_material && $self->config->support_material_contact_distance == 0) { - $_->surface_type(S_TYPE_BOTTOM) for @bottom; - } - - # if user requested internal shells, we need to identify surfaces - # lying on other slices not belonging to this region - if ($self->config->interface_shells) { - # non-bridging bottom surfaces: any part of this layer lying - # on something else, excluding those lying on our own region - my $supported = intersection_ex( - [ map @{$_->expolygon}, @{$layerm->slices} ], - [ map @$_, @{$lower_layer->slices} ], - ); - push @bottom, $difference->( - $supported, - [ map $_->expolygon, @{$lower_layer->regions->[$region_id]->slices} ], - S_TYPE_BOTTOM, - ); - } - } else { - # if no lower layer, all surfaces of this one are solid - # we clone surfaces because we're going to clear the slices collection - @bottom = map $_->clone, @{$layerm->slices}; - - # if we have raft layers, consider bottom layer as a bridge - # just like any other bottom surface lying on the void - if ($self->config->raft_layers > 0 && $self->config->support_material_contact_distance > 0) { - $_->surface_type(S_TYPE_BOTTOMBRIDGE) for @bottom; - } else { - $_->surface_type(S_TYPE_BOTTOM) for @bottom; - } - } - - # now, if the object contained a thin membrane, we could have overlapping bottom - # and top surfaces; let's do an intersection to discover them and consider them - # as bottom surfaces (to allow for bridge detection) - if (@top && @bottom) { - my $overlapping = intersection_ex([ map $_->p, @top ], [ map $_->p, @bottom ]); - Slic3r::debugf " layer %d contains %d membrane(s)\n", $layerm->layer->id, scalar(@$overlapping) - if $Slic3r::debug; - @top = $difference->([map $_->expolygon, @top], $overlapping, S_TYPE_TOP); - } - - # find internal surfaces (difference between top/bottom surfaces and others) - my @internal = $difference->( - [ map $_->expolygon, @{$layerm->slices} ], - [ map $_->expolygon, @top, @bottom ], - S_TYPE_INTERNAL, - ); - - # save surfaces to layer - $layerm->slices->clear; - $layerm->slices->append($_) for (@bottom, @top, @internal); - - Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n", - $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug; - } - - # clip surfaces to the fill boundaries - foreach my $layer (@{$self->layers}) { - my $layerm = $layer->regions->[$region_id]; - - # Note: this method should be idempotent, but fill_surfaces gets modified - # in place. However we're now only using its boundaries (which are invariant) - # so we're safe. This guarantees idempotence of prepare_infill() also in case - # that combine_infill() turns some fill_surface into VOID surfaces. - my $fill_boundaries = [ map $_->clone->p, @{$layerm->fill_surfaces} ]; - $layerm->fill_surfaces->clear; - foreach my $surface (@{$layerm->slices}) { - my $intersection = intersection_ex( - [ $surface->p ], - $fill_boundaries, - ); - $layerm->fill_surfaces->append($_) - for map Slic3r::Surface->new(expolygon => $_, surface_type => $surface->surface_type), - @$intersection; - } - } - } -} - # 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 { diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index da71552c95..3b944c97e0 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -61,12 +61,6 @@ Layer::clear_regions() this->delete_region(i); } -LayerRegion* -Layer::get_region(int idx) -{ - return this->regions.at(idx); -} - LayerRegion* Layer::add_region(PrintRegion* print_region) { diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp index 59badcb1f9..7355ed9195 100644 --- a/xs/src/libslic3r/Layer.hpp +++ b/xs/src/libslic3r/Layer.hpp @@ -97,7 +97,8 @@ class Layer { size_t region_count() const; - LayerRegion* get_region(int idx); + LayerRegion* get_region(size_t idx) { return this->regions.at(idx); }; + const LayerRegion* get_region(size_t idx) const { return this->regions.at(idx); }; LayerRegion* add_region(PrintRegion* print_region); void make_slices(); diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index c97ca30f0e..a4b9818bab 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -133,12 +133,6 @@ Print::clear_regions() this->delete_region(i); } -PrintRegion* -Print::get_region(size_t idx) -{ - return regions.at(idx); -} - PrintRegion* Print::add_region() { diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 0e3de68ef2..50b68d2a51 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -133,6 +133,7 @@ class PrintObject bool invalidate_all_steps(); bool has_support_material() const; + void detect_surfaces_type(); void process_external_surfaces(); void bridge_over_infill(); void _make_perimeters(); @@ -181,7 +182,8 @@ class Print bool reload_model_instances(); // methods for handling regions - PrintRegion* get_region(size_t idx); + PrintRegion* get_region(size_t idx) { return this->regions.at(idx); }; + const PrintRegion* get_region(size_t idx) const { return this->regions.at(idx); }; PrintRegion* add_region(); // methods for handling state diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index e3a21bb80e..649c468fbb 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -340,6 +340,170 @@ PrintObject::has_support_material() const || this->config.support_material_enforce_layers > 0; } +// This function analyzes slices of a region (SurfaceCollection slices). +// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface. +// Initially all slices are of type S_TYPE_INTERNAL. +// Slices are compared against the top / bottom slices and regions and classified to the following groups: +// S_TYPE_TOP - Part of a region, which is not covered by any upper layer. This surface will be filled with a top solid infill. +// S_TYPE_BOTTOMBRIDGE - Part of a region, which is not fully supported, but it hangs in the air, or it hangs losely on a support or a raft. +// S_TYPE_BOTTOM - Part of a region, which is not supported by the same region, but it is supported either by another region, or by a soluble interface layer. +// S_TYPE_INTERNAL - Part of a region, which is supported by the same region type. +// If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins. +void +PrintObject::detect_surfaces_type() +{ + //Slic3r::debugf "Detecting solid surfaces...\n"; + FOREACH_REGION(this->_print, region) { + size_t region_id = region - this->_print->regions.begin(); + + FOREACH_LAYER(this, layer_it) { + size_t layer_idx = layer_it - this->layers.begin(); + Layer &layer = **layer_it; + LayerRegion &layerm = *layer.get_region(region_id); + // comparison happens against the *full* slices (considering all regions) + // unless internal shells are requested + + const Layer* upper_layer = layer_idx < (this->layer_count()-1) ? this->get_layer(layer_idx+1) : NULL; + const Layer* lower_layer = layer_idx > 0 ? this->get_layer(layer_idx-1) : NULL; + + // collapse very narrow parts (using the safety offset in the diff is not enough) + const float offset = layerm.flow(frExternalPerimeter).scaled_width() / 10.f; + + const Polygons layerm_slices_surfaces = layerm.slices; + + // find top surfaces (difference between current surfaces + // of current layer and upper one) + SurfaceCollection top; + if (upper_layer != NULL) { + const Polygons upper_slices = this->config.interface_shells.value + ? (Polygons)upper_layer->get_region(region_id)->slices + : (Polygons)upper_layer->slices; + + top.append( + offset2_ex( + diff(layerm_slices_surfaces, upper_slices, true), + -offset, offset + ), + stTop + ); + } else { + // if no upper layer, all surfaces of this one are solid + // we clone surfaces because we're going to clear the slices collection + top = layerm.slices; + for (Surfaces::iterator it = top.surfaces.begin(); it != top.surfaces.end(); ++ it) + it->surface_type = stTop; + } + + // find bottom surfaces (difference between current surfaces + // of current layer and lower one) + SurfaceCollection bottom; + if (lower_layer != NULL) { + // If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating + // the support from the print. + const SurfaceType surface_type_bottom = + (this->config.support_material.value && this->config.support_material_contact_distance.value == 0) + ? stBottom + : stBottomBridge; + + // Any surface lying on the void is a true bottom bridge (an overhang) + bottom.append( + offset2_ex( + diff(layerm_slices_surfaces, lower_layer->slices, true), + -offset, offset + ), + surface_type_bottom + ); + + // if user requested internal shells, we need to identify surfaces + // lying on other slices not belonging to this region + if (this->config.interface_shells) { + // non-bridging bottom surfaces: any part of this layer lying + // on something else, excluding those lying on our own region + bottom.append( + offset2_ex( + diff( + intersection(layerm_slices_surfaces, lower_layer->slices), // supported + lower_layer->get_region(region_id)->slices, + true + ), + -offset, offset + ), + stBottom + ); + } + } else { + // if no lower layer, all surfaces of this one are solid + // we clone surfaces because we're going to clear the slices collection + bottom = layerm.slices; + + // if we have raft layers, consider bottom layer as a bridge + // just like any other bottom surface lying on the void + const SurfaceType surface_type_bottom = + (this->config.raft_layers.value > 0 && this->config.support_material_contact_distance.value > 0) + ? stBottomBridge + : stBottom; + for (Surfaces::iterator it = bottom.surfaces.begin(); it != bottom.surfaces.end(); ++ it) + it->surface_type = surface_type_bottom; + } + + // now, if the object contained a thin membrane, we could have overlapping bottom + // and top surfaces; let's do an intersection to discover them and consider them + // as bottom surfaces (to allow for bridge detection) + if (!top.empty() && !bottom.empty()) { + const Polygons top_polygons = to_polygons(STDMOVE(top)); + top.clear(); + top.append( + offset2_ex(diff(top_polygons, bottom, true), -offset, offset), + stTop + ); + } + + // save surfaces to layer + layerm.slices.clear(); + layerm.slices.append(STDMOVE(top)); + layerm.slices.append(STDMOVE(bottom)); + + // find internal surfaces (difference between top/bottom surfaces and others) + { + Polygons topbottom = top; append_to(topbottom, (Polygons)bottom); + + layerm.slices.append( + offset2_ex( + diff(layerm_slices_surfaces, topbottom, true), + -offset, offset + ), + stInternal + ); + } + + /* + Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n", + $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug; + */ + + } // for each layer of a region + + /* Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. + Note: this method should be idempotent, but fill_surfaces gets modified + in place. However we're now only using its boundaries (which are invariant) + so we're safe. This guarantees idempotence of prepare_infill() also in case + that combine_infill() turns some fill_surface into VOID surfaces. */ + FOREACH_LAYER(this, layer_it) { + LayerRegion &layerm = *(*layer_it)->get_region(region_id); + + const Polygons fill_boundaries = layerm.fill_surfaces; + layerm.fill_surfaces.clear(); + for (Surfaces::const_iterator surface = layerm.slices.surfaces.begin(); + surface != layerm.slices.surfaces.end(); ++ surface) { + layerm.fill_surfaces.append( + intersection_ex(*surface, fill_boundaries), + surface->surface_type + ); + } + } + } +} + void PrintObject::process_external_surfaces() { diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp index 42d26b892d..e9d42fc49a 100644 --- a/xs/src/libslic3r/SurfaceCollection.hpp +++ b/xs/src/libslic3r/SurfaceCollection.hpp @@ -28,6 +28,8 @@ class SurfaceCollection void append(const ExPolygons &src, const Surface &templ); void append(const ExPolygons &src, SurfaceType surfaceType); size_t polygons_count() const; + bool empty() const { return this->surfaces.empty(); }; + void clear() { this->surfaces.clear(); }; }; } diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index dec2e115e6..b378903803 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -107,6 +107,7 @@ _constant() void set_step_started(PrintObjectStep step) %code%{ THIS->state.set_started(step); %}; + void detect_surfaces_type(); void process_external_surfaces(); void bridge_over_infill(); void _make_perimeters();