From 2bbb089a4fa74516172ec246ed6f43e44b143ce0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 26 Nov 2018 13:57:31 +0100 Subject: [PATCH 1/8] Ported PrintObject::combine_infill() to C++ --- lib/Slic3r/Print/Object.pm | 131 ------------------------------- xs/src/libslic3r/Geometry.hpp | 1 + xs/src/libslic3r/PrintObject.cpp | 118 +++++++++++++++++----------- xs/xsp/Print.xsp | 1 + 4 files changed, 73 insertions(+), 178 deletions(-) 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(); From f7509f2a3f5320290157297f3f2b527bf6dd810c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 26 Nov 2018 21:13:04 +0100 Subject: [PATCH 2/8] Ported PrintObject::slice() to C++ --- lib/Slic3r/Print/Object.pm | 89 ------------------------ xs/src/libslic3r/ExPolygonCollection.hpp | 1 + xs/src/libslic3r/PrintObject.cpp | 83 ++++++++++++++++++++-- xs/xsp/Print.xsp | 1 + 4 files changed, 80 insertions(+), 94 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index f7c120ca0..4e8aff5a7 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -34,95 +34,6 @@ sub support_layers { return [ map $self->get_support_layer($_), 0..($self->support_layer_count - 1) ]; } -# 1) Decides Z positions of the layers, -# 2) Initializes layers and their regions -# 3) Slices the object meshes -# 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes -# 5) Applies size compensation (offsets the slices in XY plane) -# 6) Replaces bad slices by the slices reconstructed from the upper/lower layer -# Resulting expolygons of layer regions are marked as Internal. -# -# this should be idempotent -sub slice { - my $self = shift; - - return if $self->step_done(STEP_SLICE); - $self->set_step_started(STEP_SLICE); - $self->print->status_cb->(10, "Processing triangulated mesh"); - - $self->_slice; - - # detect slicing errors - my $warning_thrown = 0; - for my $i (0 .. ($self->layer_count - 1)) { - my $layer = $self->get_layer($i); - next unless $layer->slicing_errors; - if (!$warning_thrown) { - warn "The model has overlapping or self-intersecting facets. I tried to repair it, " - . "however you might want to check the results or repair the input file and retry.\n"; - $warning_thrown = 1; - } - - # try to repair the layer surfaces by merging all contours and all holes from - # neighbor layers - Slic3r::debugf "Attempting to repair layer %d\n", $i; - - foreach my $region_id (0 .. ($layer->region_count - 1)) { - my $layerm = $layer->region($region_id); - - my (@upper_surfaces, @lower_surfaces); - for (my $j = $i+1; $j < $self->layer_count; $j++) { - if (!$self->get_layer($j)->slicing_errors) { - @upper_surfaces = @{$self->get_layer($j)->region($region_id)->slices}; - last; - } - } - for (my $j = $i-1; $j >= 0; $j--) { - if (!$self->get_layer($j)->slicing_errors) { - @lower_surfaces = @{$self->get_layer($j)->region($region_id)->slices}; - last; - } - } - - my $union = union_ex([ - map $_->expolygon->contour, @upper_surfaces, @lower_surfaces, - ]); - my $diff = diff_ex( - [ map @$_, @$union ], - [ map @{$_->expolygon->holes}, @upper_surfaces, @lower_surfaces, ], - ); - - $layerm->slices->clear; - $layerm->slices->append($_) - for map Slic3r::Surface->new - (expolygon => $_, surface_type => S_TYPE_INTERNAL), - @$diff; - } - - # update layer slices after repairing the single regions - $layer->make_slices; - } - - # remove empty layers from bottom - while (@{$self->layers} && !@{$self->get_layer(0)->slices}) { - $self->delete_layer(0); - for (my $i = 0; $i <= $#{$self->layers}; $i++) { - $self->get_layer($i)->set_id( $self->get_layer($i)->id-1 ); - } - } - - # simplify slices if required - if ($self->print->config->resolution) { - $self->_simplify_slices(scale($self->print->config->resolution)); - } - - die "No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n" - if !@{$self->layers}; - - $self->set_typed_slices(0); - $self->set_step_done(STEP_SLICE); -} - sub make_perimeters { my ($self) = @_; diff --git a/xs/src/libslic3r/ExPolygonCollection.hpp b/xs/src/libslic3r/ExPolygonCollection.hpp index 1d911f7e9..4c97f2610 100644 --- a/xs/src/libslic3r/ExPolygonCollection.hpp +++ b/xs/src/libslic3r/ExPolygonCollection.hpp @@ -41,6 +41,7 @@ class ExPolygonCollection /// ExPolygons and check if at least one contains the point. bool contains(const Point &point) const; + bool empty() const { return expolygons.empty(); } size_t size() const { return expolygons.size(); } ExPolygons::iterator begin() { return expolygons.begin(); } ExPolygons::iterator end() { return expolygons.end(); } diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index f401d143d..643b8a839 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -956,6 +956,17 @@ PrintObject::make_perimeters() this->_make_perimeters(); } +/* + 1) Decides Z positions of the layers, + 2) Initializes layers and their regions + 3) Slices the object meshes + 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes + 5) Applies size compensation (offsets the slices in XY plane) + 6) Replaces bad slices by the slices reconstructed from the upper/lower layer + Resulting expolygons of layer regions are marked as Internal. + + This should be idempotent. +*/ void PrintObject::slice() { @@ -964,8 +975,7 @@ PrintObject::slice() if (_print->status_cb != nullptr) { _print->status_cb(10, "Processing triangulated mesh"); } - - + this->_slice(); // detect slicing errors @@ -975,12 +985,75 @@ PrintObject::slice() << "I tried to repair it, however you might want to check " << "the results or repair the input file and retry.\n"; - if (this->layers.size() == 0) { + bool warning_thrown = false; + for (size_t i = 0; i < this->layer_count(); ++i) { + Layer* layer{ this->get_layer(i) }; + if (!layer->slicing_errors) continue; + if (!warning_thrown) { + Slic3r::Log::warn("PrintObject") << "The model has overlapping or self-intersecting facets. " + << "I tried to repair it, however you might want to check " + << "the results or repair the input file and retry.\n"; + warning_thrown = true; + } + + // try to repair the layer surfaces by merging all contours and all holes from + // neighbor layers + #ifdef SLIC3R_DEBUG + std::cout << "Attempting to repair layer " << i << std::endl; + #endif + + for (size_t region_id = 0; region_id < layer->region_count(); ++region_id) { + LayerRegion* layerm{ layer->get_region(region_id) }; + + ExPolygons slices; + for (size_t j = i+1; j < this->layer_count(); ++j) { + const Layer* upper = this->get_layer(j); + if (!upper->slicing_errors) { + append_to(slices, (ExPolygons)upper->get_region(region_id)->slices); + break; + } + } + for (int j = i-1; j >= 0; --j) { + const Layer* lower = this->get_layer(j); + if (!lower->slicing_errors) { + append_to(slices, (ExPolygons)lower->get_region(region_id)->slices); + break; + } + } + + // TODO: do we actually need to split contours and holes before performing the diff? + Polygons contours, holes; + for (ExPolygon ex : slices) + contours.push_back(ex.contour); + for (ExPolygon ex : slices) + append_to(holes, ex.holes); + + const ExPolygons diff = diff_ex(contours, holes); + + layerm->slices.clear(); + layerm->slices.append(diff, stInternal); + } + + // update layer slices after repairing the single regions + layer->make_slices(); + } + + // remove empty layers from bottom + while (!this->layers.empty() && this->get_layer(0)->slices.empty()) { + this->delete_layer(0); + for (Layer* layer : this->layers) + layer->set_id(layer->id()-1); + } + + // simplify slices if required + if (this->_print->config.resolution() > 0) + this->_simplify_slices(scale_(this->_print->config.resolution())); + + if (this->layers.empty()) { Slic3r::Log::error("PrintObject") << "slice(): " << "No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"; return; // make this throw an exception instead? } - - + this->typed_slices = false; this->state.set_done(posSlice); } diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index c2ec2d813..1d6f0eba6 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -132,6 +132,7 @@ _constant() void combine_infill(); void discover_horizontal_shells(); void clip_fill_surfaces(); + void slice(); void _slice(); SV* _slice_region(size_t region_id, std::vector z, bool modifier) %code%{ From f2e2c9aa71208273427eb124281771fe7e00c6d1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 26 Nov 2018 21:16:37 +0100 Subject: [PATCH 3/8] Ported PrintObject::make_perimeters() to C++ --- lib/Slic3r/Print/Object.pm | 18 ------------------ xs/src/libslic3r/Print.hpp | 1 - xs/src/libslic3r/PrintObject.cpp | 21 ++++++++++----------- xs/xsp/Print.xsp | 2 +- 4 files changed, 11 insertions(+), 31 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 4e8aff5a7..5a450a9ea 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -34,24 +34,6 @@ sub support_layers { return [ map $self->get_support_layer($_), 0..($self->support_layer_count - 1) ]; } -sub make_perimeters { - my ($self) = @_; - - return if $self->step_done(STEP_PERIMETERS); - - # Temporary workaround for detect_surfaces_type() not being idempotent (see #3764). - # We can remove this when idempotence is restored. This make_perimeters() method - # will just call merge_slices() to undo the typed slices and invalidate posDetectSurfaces. - if ($self->typed_slices) { - $self->invalidate_step(STEP_SLICE); - } - - # prerequisites - $self->slice; - - $self->_make_perimeters; -} - # This will assign a type (top/bottom/internal) to $layerm->slices # and transform $layerm->fill_surfaces from expolygon # to typed top/bottom/internal surfaces; diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index e3420a5a9..250ed6e96 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -164,7 +164,6 @@ class PrintObject void _slice(); std::vector _slice_region(size_t region_id, std::vector z, bool modifier); - void _make_perimeters(); void _infill(); /// Initialize and generate support material. diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 643b8a839..35279aac4 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -946,16 +946,6 @@ PrintObject::_slice_region(size_t region_id, std::vector z, bool modifier return layers; } -void -PrintObject::make_perimeters() -{ - if (this->state.is_done(posPerimeters)) return; - if (this->typed_slices) - this->state.invalidate(posSlice); - this->slice(); // take care of prereqs - this->_make_perimeters(); -} - /* 1) Decides Z positions of the layers, 2) Initializes layers and their regions @@ -1059,11 +1049,20 @@ PrintObject::slice() } void -PrintObject::_make_perimeters() +PrintObject::make_perimeters() { if (this->state.is_done(posPerimeters)) return; this->state.set_started(posPerimeters); + // Temporary workaround for detect_surfaces_type() not being idempotent (see #3764). + // We can remove this when idempotence is restored. This make_perimeters() method + // will just call merge_slices() to undo the typed slices and invalidate posDetectSurfaces. + if (this->typed_slices) + this->state.invalidate(posSlice); + + // prerequisites + this->slice(); + // merge slices if they were split into types // This is not currently taking place because since merge_slices + detect_surfaces_type // are not truly idempotent we are invalidating posSlice here (see the Perl part of diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 1d6f0eba6..e5cceda9b 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -153,7 +153,7 @@ _constant() } RETVAL = (SV*)newRV_noinc((SV*)layers_av); %}; - void _make_perimeters(); + void make_perimeters(); void _infill(); void _simplify_slices(double distance); From 1cc8a8d3e3c54863cedf89d14610df3a3ff8356e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 26 Nov 2018 21:17:54 +0100 Subject: [PATCH 4/8] Ported PrintObject::detect_surfaces_type() to C++ --- lib/Slic3r/Print/Object.pm | 12 ------------ xs/src/libslic3r/PrintObject.cpp | 5 ++++- xs/xsp/Print.xsp | 2 +- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 5a450a9ea..26317621c 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -34,18 +34,6 @@ sub support_layers { return [ map $self->get_support_layer($_), 0..($self->support_layer_count - 1) ]; } -# This will assign a type (top/bottom/internal) to $layerm->slices -# and transform $layerm->fill_surfaces from expolygon -# to typed top/bottom/internal surfaces; -sub detect_surfaces_type { - my ($self) = @_; - - # prerequisites - $self->slice; - - $self->_detect_surfaces_type; -} - sub prepare_infill { my ($self) = @_; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 35279aac4..ccbc7a00a 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -329,11 +329,14 @@ PrintObject::has_support_material() const || this->config.support_material_enforce_layers > 0; } +// This will assign a type (top/bottom/internal) to layerm->slices +// and transform layerm->fill_surfaces from expolygon +// to typed top/bottom/internal surfaces; void PrintObject::detect_surfaces_type() { // prerequisites - // this->slice(); + this->slice(); if (this->state.is_done(posDetectSurfaces)) return; this->state.set_started(posDetectSurfaces); diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index e5cceda9b..89d84df05 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -126,7 +126,7 @@ _constant() void set_step_started(PrintObjectStep step) %code%{ THIS->state.set_started(step); %}; - %name{_detect_surfaces_type} void detect_surfaces_type(); + void detect_surfaces_type(); void process_external_surfaces(); void bridge_over_infill(); void combine_infill(); From aac9c2481cae204d910e7efbe52a8f8b8035f11b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 26 Nov 2018 21:24:33 +0100 Subject: [PATCH 5/8] Ported PrintObject::infill() to C++ --- lib/Slic3r/Print/Object.pm | 9 --------- src/test/libslic3r/test_support_material.cpp | 2 +- xs/src/libslic3r/PrintObject.cpp | 18 +++++++----------- xs/xsp/Print.xsp | 2 +- 4 files changed, 9 insertions(+), 22 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 26317621c..2a50dcc46 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -75,15 +75,6 @@ sub prepare_infill { $self->set_step_done(STEP_PREPARE_INFILL); } -sub infill { - my ($self) = @_; - - # prerequisites - $self->prepare_infill; - - $self->_infill; -} - sub generate_support_material { my $self = shift; diff --git a/src/test/libslic3r/test_support_material.cpp b/src/test/libslic3r/test_support_material.cpp index 5e9d6c932..fa82323d5 100644 --- a/src/test/libslic3r/test_support_material.cpp +++ b/src/test/libslic3r/test_support_material.cpp @@ -282,7 +282,7 @@ bool test_6_checks(Print &print) // Pre-Processing. PrintObject *print_object = print.objects.front(); - print_object->_infill(); + print_object->infill(); SupportMaterial *support_material = print.objects.front()->_support_material(); support_material->generate(print_object); // TODO but not needed in test 6 (make brims and make skirts). diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index ccbc7a00a..c55f7ecf9 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -1054,6 +1054,9 @@ PrintObject::slice() void PrintObject::make_perimeters() { + // prerequisites + this->slice(); + if (this->state.is_done(posPerimeters)) return; this->state.set_started(posPerimeters); @@ -1063,9 +1066,6 @@ PrintObject::make_perimeters() if (this->typed_slices) this->state.invalidate(posSlice); - // prerequisites - this->slice(); - // merge slices if they were split into types // This is not currently taking place because since merge_slices + detect_surfaces_type // are not truly idempotent we are invalidating posSlice here (see the Perl part of @@ -1185,8 +1185,11 @@ PrintObject::make_perimeters() } void -PrintObject::_infill() +PrintObject::infill() { + // prerequisites + this->prepare_infill(); + if (this->state.is_done(posInfill)) return; this->state.set_started(posInfill); @@ -1383,13 +1386,6 @@ PrintObject::combine_infill() } } -void -PrintObject::infill() -{ - this->prepare_infill(); - this->_infill(); -} - SupportMaterial * PrintObject::_support_material() { diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 89d84df05..3cab015e0 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -154,7 +154,7 @@ _constant() RETVAL = (SV*)newRV_noinc((SV*)layers_av); %}; void make_perimeters(); - void _infill(); + void infill(); void _simplify_slices(double distance); int ptr() From c0465d04343e5f2b4afae49808faa78b18b4cd32 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 26 Nov 2018 23:45:19 +0100 Subject: [PATCH 6/8] Ported PrintObject::prepare_infill() to C++ --- lib/Slic3r/Print/Object.pm | 41 -------------------------------- xs/src/libslic3r/PrintObject.cpp | 22 ++++++++--------- xs/xsp/Print.xsp | 1 + 3 files changed, 11 insertions(+), 53 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 2a50dcc46..3ac6422d0 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -34,47 +34,6 @@ sub support_layers { return [ map $self->get_support_layer($_), 0..($self->support_layer_count - 1) ]; } -sub prepare_infill { - my ($self) = @_; - - return if $self->step_done(STEP_PREPARE_INFILL); - - # This prepare_infill() is not really idempotent. - # TODO: It should clear and regenerate fill_surfaces at every run - # instead of modifying it in place. - $self->invalidate_step(STEP_PERIMETERS); - $self->make_perimeters; - - # Do this after invalidating STEP_PERIMETERS because that would re-invalidate STEP_PREPARE_INFILL - $self->set_step_started(STEP_PREPARE_INFILL); - - # prerequisites - $self->detect_surfaces_type; - - $self->print->status_cb->(30, "Preparing infill"); - - # decide what surfaces are to be filled - $_->prepare_fill_surfaces for map @{$_->regions}, @{$self->layers}; - - # this will detect bridges and reverse bridges - # and rearrange top/bottom/internal surfaces - $self->process_external_surfaces; - - # detect which fill surfaces are near external layers - # they will be split in internal and internal-solid surfaces - $self->discover_horizontal_shells; - $self->clip_fill_surfaces; - - # the following step needs to be done before combination because it may need - # to remove only half of the combined infill - $self->bridge_over_infill; - - # combine fill surfaces to honor the "infill every N layers" option - $self->combine_infill; - - $self->set_step_done(STEP_PREPARE_INFILL); -} - sub generate_support_material { my $self = shift; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index c55f7ecf9..1faadab9b 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -1054,9 +1054,6 @@ PrintObject::slice() void PrintObject::make_perimeters() { - // prerequisites - this->slice(); - if (this->state.is_done(posPerimeters)) return; this->state.set_started(posPerimeters); @@ -1066,6 +1063,9 @@ PrintObject::make_perimeters() if (this->typed_slices) this->state.invalidate(posSlice); + // prerequisites + this->slice(); + // merge slices if they were split into types // This is not currently taking place because since merge_slices + detect_surfaces_type // are not truly idempotent we are invalidating posSlice here (see the Perl part of @@ -1209,7 +1209,8 @@ PrintObject::infill() void PrintObject::prepare_infill() { - if (this->state.is_done(posInfill)) return; + if (this->state.is_done(posPrepareInfill)) return; + // This prepare_infill() is not really idempotent. // TODO: It should clear and regenerate fill_surfaces at every run // instead of modifying it in place. @@ -1222,16 +1223,13 @@ PrintObject::prepare_infill() // prerequisites this->detect_surfaces_type(); - if (this->print()->status_cb != nullptr) - this->print()->status_cb(30, "Preparing infill"); + if (this->_print->status_cb != nullptr) + this->_print->status_cb(30, "Preparing infill"); - // decide what surfaces are to be filled - for (auto& layer : this->layers) { - for (auto& region : layer->regions) { - region->prepare_fill_surfaces(); - } - } + for (auto& layer : this->layers) + for (auto& layerm : layer->regions) + layerm->prepare_fill_surfaces(); // this will detect bridges and reverse bridges // and rearrange top/bottom/internal surfaces diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 3cab015e0..cb1cb7d1f 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -154,6 +154,7 @@ _constant() RETVAL = (SV*)newRV_noinc((SV*)layers_av); %}; void make_perimeters(); + void prepare_infill(); void infill(); void _simplify_slices(double distance); From 75670c6819865c30b117be72d26f8bb32cbd64d1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 26 Nov 2018 23:50:44 +0100 Subject: [PATCH 7/8] For consistency, move prerequisites after the state guard --- xs/src/libslic3r/PrintObject.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 1faadab9b..4bea641e5 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -335,12 +335,12 @@ PrintObject::has_support_material() const void PrintObject::detect_surfaces_type() { - // prerequisites - this->slice(); - if (this->state.is_done(posDetectSurfaces)) return; this->state.set_started(posDetectSurfaces); + // prerequisites + this->slice(); + parallelize( std::queue(std::deque(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue boost::bind(&Slic3r::Layer::detect_surfaces_type, _1), @@ -1187,12 +1187,12 @@ PrintObject::make_perimeters() void PrintObject::infill() { - // prerequisites - this->prepare_infill(); - if (this->state.is_done(posInfill)) return; this->state.set_started(posInfill); + // prerequisites + this->prepare_infill(); + parallelize( std::queue(std::deque(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue boost::bind(&Slic3r::Layer::make_fills, _1), From 33b3faacfd3b50cc643d6eecd6f48aec8ddcc7bc Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 27 Nov 2018 12:18:34 +0100 Subject: [PATCH 8/8] Ported Print::make_skirt() to C++ --- lib/Slic3r/Print.pm | 128 ---------------- xs/src/libslic3r/ExPolygonCollection.hpp | 2 + .../libslic3r/ExtrusionEntityCollection.hpp | 2 + xs/src/libslic3r/Print.cpp | 144 +++++++----------- xs/xsp/Print.xsp | 1 + 5 files changed, 61 insertions(+), 216 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 23799e8be..3b0bfea25 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -255,134 +255,6 @@ EOF print "Done.\n" unless $params{quiet}; } -sub make_skirt { - my $self = shift; - - # prerequisites - $_->make_perimeters for @{$self->objects}; - $_->infill for @{$self->objects}; - $_->generate_support_material for @{$self->objects}; - - return if $self->step_done(STEP_SKIRT); - $self->set_step_started(STEP_SKIRT); - - # since this method must be idempotent, we clear skirt paths *before* - # checking whether we need to generate them - $self->skirt->clear; - - if (!$self->has_skirt) { - $self->set_step_done(STEP_SKIRT); - return; - } - $self->status_cb->(88, "Generating skirt"); - - # First off we need to decide how tall the skirt must be. - # The skirt_height option from config is expressed in layers, but our - # object might have different layer heights, so we need to find the print_z - # of the highest layer involved. - # Note that unless has_infinite_skirt() == true - # the actual skirt might not reach this $skirt_height_z value since the print - # order of objects on each layer is not guaranteed and will not generally - # include the thickest object first. It is just guaranteed that a skirt is - # prepended to the first 'n' layers (with 'n' = skirt_height). - # $skirt_height_z in this case is the highest possible skirt height for safety. - my $skirt_height_z = -1; - foreach my $object (@{$self->objects}) { - my $skirt_height = $self->has_infinite_skirt - ? $object->layer_count - : min($self->config->skirt_height, $object->layer_count); - my $highest_layer = $object->get_layer($skirt_height - 1); - $skirt_height_z = max($skirt_height_z, $highest_layer->print_z); - } - - # collect points from all layers contained in skirt height - my @points = (); - foreach my $object (@{$self->objects}) { - my @object_points = (); - - # get object layers up to $skirt_height_z - foreach my $layer (@{$object->layers}) { - last if $layer->print_z > $skirt_height_z; - push @object_points, map @$_, map @$_, @{$layer->slices}; - } - - # get support layers up to $skirt_height_z - foreach my $layer (@{$object->support_layers}) { - last if $layer->print_z > $skirt_height_z; - push @object_points, map @{$_->polyline}, @{$layer->support_fills} if $layer->support_fills; - push @object_points, map @{$_->polyline}, @{$layer->support_interface_fills} if $layer->support_interface_fills; - } - - # repeat points for each object copy - foreach my $copy (@{$object->_shifted_copies}) { - my @copy_points = map $_->clone, @object_points; - $_->translate(@$copy) for @copy_points; - push @points, @copy_points; - } - } - return if @points < 3; # at least three points required for a convex hull - - # find out convex hull - my $convex_hull = convex_hull(\@points); - - my @extruded_length = (); # for each extruder - - # skirt may be printed on several layers, having distinct layer heights, - # but loops must be aligned so can't vary width/spacing - # TODO: use each extruder's own flow - my $first_layer_height = $self->skirt_first_layer_height; - my $flow = $self->skirt_flow; - my $spacing = $flow->spacing; - my $mm3_per_mm = $flow->mm3_per_mm; - - my @extruders_e_per_mm = (); - my $extruder_idx = 0; - - my $skirts = $self->config->skirts; - $skirts ||= 1 if $self->has_infinite_skirt; - - # draw outlines from outside to inside - # loop while we have less skirts than required or any extruder hasn't reached the min length if any - my $distance = scale max($self->config->skirt_distance, $self->config->brim_width); - for (my $i = $skirts; $i > 0; $i--) { - $distance += scale $spacing; - my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0]; - my $eloop = Slic3r::ExtrusionLoop->new_from_paths( - Slic3r::ExtrusionPath->new( - polyline => Slic3r::Polygon->new(@$loop)->split_at_first_point, - role => EXTR_ROLE_SKIRT, - mm3_per_mm => $mm3_per_mm, # this will be overridden at G-code export time - width => $flow->width, - height => $first_layer_height, # this will be overridden at G-code export time - ), - ); - $eloop->role(EXTRL_ROLE_SKIRT); - $self->skirt->append($eloop); - - if ($self->config->min_skirt_length > 0) { - $extruded_length[$extruder_idx] ||= 0; - if (!$extruders_e_per_mm[$extruder_idx]) { - my $config = Slic3r::Config::GCode->new; - $config->apply_static($self->config); - my $extruder = Slic3r::Extruder->new($extruder_idx, $config); - $extruders_e_per_mm[$extruder_idx] = $extruder->e_per_mm($mm3_per_mm); - } - $extruded_length[$extruder_idx] += unscale $loop->length * $extruders_e_per_mm[$extruder_idx]; - $i++ if defined first { ($extruded_length[$_] // 0) < $self->config->min_skirt_length } 0 .. $#{$self->extruders}; - if ($extruded_length[$extruder_idx] >= $self->config->min_skirt_length) { - if ($extruder_idx < $#{$self->extruders}) { - $extruder_idx++; - next; - } - } - } - } - - $self->skirt->reverse; - - $self->set_step_done(STEP_SKIRT); -} - sub make_brim { my $self = shift; diff --git a/xs/src/libslic3r/ExPolygonCollection.hpp b/xs/src/libslic3r/ExPolygonCollection.hpp index 4c97f2610..e2638a265 100644 --- a/xs/src/libslic3r/ExPolygonCollection.hpp +++ b/xs/src/libslic3r/ExPolygonCollection.hpp @@ -45,6 +45,8 @@ class ExPolygonCollection size_t size() const { return expolygons.size(); } ExPolygons::iterator begin() { return expolygons.begin(); } ExPolygons::iterator end() { return expolygons.end(); } + const ExPolygons::const_iterator begin() const { return expolygons.cbegin(); } + const ExPolygons::const_iterator end() const { return expolygons.cend(); } ExPolygons::const_iterator cbegin() const { return expolygons.cbegin();} ExPolygons::const_iterator cend() const { return expolygons.cend();} ExPolygon& at(size_t i) { return expolygons.at(i); } diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp index 819a1bc2c..eaad7d97b 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -74,6 +74,8 @@ class ExtrusionEntityCollection : public ExtrusionEntity ExtrusionEntitiesPtr::iterator begin() { return entities.begin(); } ExtrusionEntitiesPtr::iterator end() { return entities.end(); } + ExtrusionEntitiesPtr::const_iterator begin() const { return entities.cbegin(); } + ExtrusionEntitiesPtr::const_iterator end() const { return entities.cend(); } ExtrusionEntitiesPtr::const_iterator cbegin() const { return entities.cbegin(); } ExtrusionEntitiesPtr::const_iterator cend() const { return entities.cend(); } diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 728b5dacb..487a5b418 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -140,16 +140,16 @@ Print::make_brim() void Print::make_skirt() { + if (this->state.is_done(psSkirt)) return; + this->state.set_started(psSkirt); + // prereqs - for(auto& obj: this->objects) { + for (auto* obj: this->objects) { obj->make_perimeters(); obj->infill(); obj->generate_support_material(); } - if (this->state.is_done(psSkirt)) return; - this->state.set_started(psSkirt); - // since this method must be idempotent, we clear skirt paths *before* // checking whether we need to generate them this->skirt.clear(); @@ -173,10 +173,11 @@ Print::make_skirt() // prepended to the first 'n' layers (with 'n' = skirt_height). // $skirt_height_z in this case is the highest possible skirt height for safety. double skirt_height_z {-1.0}; - for (const auto& object : this->objects) { + for (const auto* object : this->objects) { const size_t skirt_height { - this->has_infinite_skirt() ? object->layer_count() : - std::min(size_t(this->config.skirt_height()), object->layer_count()) + this->has_infinite_skirt() + ? object->layer_count() + : std::min(size_t(this->config.skirt_height()), object->layer_count()) }; const Layer* highest_layer { object->get_layer(skirt_height - 1) }; skirt_height_z = std::max(skirt_height_z, highest_layer->print_z); @@ -184,116 +185,83 @@ Print::make_skirt() // collect points from all layers contained in skirt height Points points; - for(auto* object : this->objects) { + for (auto* object : this->objects) { Points object_points; // get object layers up to skirt_height_z - for(auto* layer : object->layers) { - if(layer->print_z > skirt_height_z)break; - for(ExPolygon poly : layer->slices){ - for(Point point : static_cast(poly)){ - object_points.push_back(point); - } - } + for (const auto* layer : object->layers) { + if (layer->print_z > skirt_height_z) break; + for (const ExPolygon ex : layer->slices) + append_to(object_points, static_cast(ex)); } - // get support layers up to $skirt_height_z - for(auto* layer : object->support_layers) { - if(layer->print_z > skirt_height_z)break; - for(auto* ee : layer->support_fills){ - for(Point point : ee->as_polyline().points){ - object_points.push_back(point); - } - } - for(auto* ee : layer->support_interface_fills){ - for(Point point : ee->as_polyline().points){ - object_points.push_back(point); - } - } + // get support layers up to skirt_height_z + for (const auto* layer : object->support_layers) { + if (layer->print_z > skirt_height_z) break; + for (auto* ee : layer->support_fills) + append_to(object_points, ee->as_polyline().points); + for (auto* ee : layer->support_interface_fills) + append_to(object_points, ee->as_polyline().points); } // repeat points for each object copy - for(auto copy : object->_shifted_copies) { - for(Point point : object_points){ - point.translate(copy); - points.push_back(point); + for (const auto& copy : object->_shifted_copies) { + for (Point p : object_points) { + p.translate(copy); + points.push_back(p); } } } if (points.size() < 3) return; // at least three points required for a convex hull // find out convex hull - auto convex = Geometry::convex_hull(points); + const Polygon convex = Geometry::convex_hull(points); // skirt may be printed on several layers, having distinct layer heights, // but loops must be aligned so can't vary width/spacing // TODO: use each extruder's own flow - auto first_layer_height = this->skirt_first_layer_height(); - auto flow = this->skirt_flow(); - auto spacing = flow.spacing(); - auto mm3_per_mm = flow.mm3_per_mm(); + const auto first_layer_height = this->skirt_first_layer_height(); + const auto flow = this->skirt_flow(); + const auto spacing = flow.scaled_spacing(); + const auto mm3_per_mm = flow.mm3_per_mm(); + int skirts = this->config.skirts(); + if (skirts == 0 && this->has_infinite_skirt()) + skirts = 1; - auto skirts = this->config.skirts; - if(this->has_infinite_skirt() && skirts == 0){ - skirts = 1; - } - - //my @extruded_length = (); # for each extruder - //extruders_e_per_mm = (); - //size_t extruder_idx = 0; - - // new to the cpp implementation - float e_per_mm {0.0}, extruded_length = 0; - size_t extruders_warm = 0; - if (this->config.min_skirt_length.getFloat() > 0) { - //my $config = Config::GCode(); - //$config->apply_static($self->config); - auto extruder = Extruder(0, &this->config); - e_per_mm = extruder.e_per_mm(mm3_per_mm); - } + const std::set extruders{ this->extruders() }; + auto extruder_it { extruders.begin() }; + std::vector e_per_mm{0}, extruded_length{0}; + if (this->config.min_skirt_length() > 0) + for (auto i : extruders) + e_per_mm[i] = Extruder(i, &this->config).e_per_mm(mm3_per_mm); // draw outlines from outside to inside // loop while we have less skirts than required or any extruder hasn't reached the min length if any - float distance = scale_(std::max(this->config.skirt_distance.getFloat(), this->config.brim_width.getFloat())); + float distance = scale_(std::max(this->config.skirt_distance(), this->config.brim_width())); for (int i = skirts; i > 0; i--) { - distance += scale_(spacing); - auto loop = offset(Polygons{convex}, distance, 1, jtRound, scale_(0.1)).at(0); - auto epath = ExtrusionPath(erSkirt, - mm3_per_mm, // this will be overridden at G-code export time - flow.width, - first_layer_height // this will be overridden at G-code export time + distance += spacing; + const Polygon loop = offset(Polygons{convex}, distance, 1, jtRound, scale_(0.1)).at(0); + auto epath = ExtrusionPath( + erSkirt, + mm3_per_mm, // this will be overridden at G-code export time + flow.width, + first_layer_height // this will be overridden at G-code export time ); epath.polyline = loop.split_at_first_point(); - auto eloop = ExtrusionLoop(epath,elrSkirt); + auto eloop = ExtrusionLoop(epath, elrSkirt); this->skirt.append(eloop); - if (this->config.min_skirt_length.getFloat() > 0) { - // Alternative simpler method - extruded_length += unscale(loop.length()) * e_per_mm; - if(extruded_length >= this->config.min_skirt_length.getFloat()){ - extruders_warm++; - extruded_length = 0; - } - if (extruders_warm < this->extruders().size()){ - i++; - } - - /*$extruded_length[$extruder_idx] ||= 0; - if (!$extruders_e_per_mm[$extruder_idx]) { - my $config = Slic3r::Config::GCode->new; - $config->apply_static($self->config); - my $extruder = Slic3r::Extruder->new($extruder_idx, $config); - $extruders_e_per_mm[$extruder_idx] = $extruder->e_per_mm($mm3_per_mm); - } - $extruded_length[$extruder_idx] += unscale $loop->length * $extruders_e_per_mm[$extruder_idx]; - $i++ if defined first { ($extruded_length[$_] // 0) < $self->config->min_skirt_length } 0 .. $#{$self->extruders}; - if ($extruded_length[$extruder_idx] >= $self->config->min_skirt_length) { - if ($extruder_idx < $#{$self->extruders}) { - $extruder_idx++; - next; + if (this->config.min_skirt_length() > 0) { + extruded_length[*extruder_it] += unscale(loop.length()) * e_per_mm[*extruder_it]; + for (auto j : extruders) { + if (extruded_length[j] < this->config.min_skirt_length()) { + ++i; + break; } - }*/ + } + if (extruded_length[*extruder_it] >= this->config.min_skirt_length() && extruder_it != extruders.end()) + ++extruder_it; } } diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index cb1cb7d1f..0bf82d06d 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -287,6 +287,7 @@ _constant() double skirt_first_layer_height(); Clone brim_flow(); Clone skirt_flow(); + void make_skirt(); void _make_brim(); %{